gobot.io/x/gobot@v1.16.0/platforms/microbit/io_pin_driver.go (about)

     1  package microbit
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/binary"
     6  	"errors"
     7  	"strconv"
     8  
     9  	"gobot.io/x/gobot"
    10  	"gobot.io/x/gobot/platforms/ble"
    11  )
    12  
    13  // IOPinDriver is the Gobot driver for the Microbit's built-in digital and
    14  // analog I/O
    15  type IOPinDriver struct {
    16  	name       string
    17  	adMask     int
    18  	ioMask     int
    19  	connection gobot.Connection
    20  	gobot.Eventer
    21  }
    22  
    23  const (
    24  	// BLE services
    25  	ioPinService = "e95d127b251d470aa062fa1922dfa9a8"
    26  
    27  	// BLE characteristics
    28  	pinDataCharacteristic     = "e95d8d00251d470aa062fa1922dfa9a8"
    29  	pinADConfigCharacteristic = "e95d5899251d470aa062fa1922dfa9a8"
    30  	pinIOConfigCharacteristic = "e95db9fe251d470aa062fa1922dfa9a8"
    31  )
    32  
    33  // PinData has the read data for a specific digital pin
    34  type PinData struct {
    35  	pin   uint8
    36  	value uint8
    37  }
    38  
    39  // NewIOPinDriver creates a Microbit IOPinDriver
    40  func NewIOPinDriver(a ble.BLEConnector) *IOPinDriver {
    41  	n := &IOPinDriver{
    42  		name:       gobot.DefaultName("Microbit IO Pins"),
    43  		connection: a,
    44  		Eventer:    gobot.NewEventer(),
    45  	}
    46  
    47  	return n
    48  }
    49  
    50  // Connection returns the BLE connection
    51  func (b *IOPinDriver) Connection() gobot.Connection { return b.connection }
    52  
    53  // Name returns the Driver Name
    54  func (b *IOPinDriver) Name() string { return b.name }
    55  
    56  // SetName sets the Driver Name
    57  func (b *IOPinDriver) SetName(n string) { b.name = n }
    58  
    59  // adaptor returns BLE adaptor
    60  func (b *IOPinDriver) adaptor() ble.BLEConnector {
    61  	return b.Connection().(ble.BLEConnector)
    62  }
    63  
    64  // Start tells driver to get ready to do work
    65  func (b *IOPinDriver) Start() (err error) {
    66  	_, err = b.ReadPinADConfig()
    67  	if err != nil {
    68  		return
    69  	}
    70  	_, err = b.ReadPinIOConfig()
    71  	return
    72  }
    73  
    74  // Halt stops driver (void)
    75  func (b *IOPinDriver) Halt() (err error) {
    76  	return
    77  }
    78  
    79  // ReadAllPinData reads and returns the pin data for all pins
    80  func (b *IOPinDriver) ReadAllPinData() (pins []PinData) {
    81  	c, _ := b.adaptor().ReadCharacteristic(pinDataCharacteristic)
    82  	buf := bytes.NewBuffer(c)
    83  	pinsData := make([]PinData, buf.Len()/2)
    84  
    85  	for i := 0; i < buf.Len()/2; i++ {
    86  		pinData := PinData{}
    87  		pinData.pin, _ = buf.ReadByte()
    88  		pinData.value, _ = buf.ReadByte()
    89  		pinsData[i] = pinData
    90  	}
    91  
    92  	return pinsData
    93  }
    94  
    95  // WritePinData writes the pin data for a single pin
    96  func (b *IOPinDriver) WritePinData(pin string, data byte) (err error) {
    97  	i, err := strconv.Atoi(pin)
    98  	if err != nil {
    99  		return
   100  	}
   101  
   102  	buf := []byte{byte(i), data}
   103  	err = b.adaptor().WriteCharacteristic(pinDataCharacteristic, buf)
   104  	return err
   105  }
   106  
   107  // ReadPinADConfig reads and returns the pin A/D config mask for all pins
   108  func (b *IOPinDriver) ReadPinADConfig() (config int, err error) {
   109  	c, e := b.adaptor().ReadCharacteristic(pinADConfigCharacteristic)
   110  	if e != nil {
   111  		return 0, e
   112  	}
   113  	var result byte
   114  	for i := 0; i < 4; i++ {
   115  		result |= c[i] << uint(i)
   116  	}
   117  
   118  	b.adMask = int(result)
   119  	return int(result), nil
   120  }
   121  
   122  // WritePinADConfig writes the pin A/D config mask for all pins
   123  func (b *IOPinDriver) WritePinADConfig(config int) (err error) {
   124  	b.adMask = config
   125  	data := &bytes.Buffer{}
   126  	binary.Write(data, binary.LittleEndian, uint32(config))
   127  	err = b.adaptor().WriteCharacteristic(pinADConfigCharacteristic, data.Bytes())
   128  	return
   129  }
   130  
   131  // ReadPinIOConfig reads and returns the pin IO config mask for all pins
   132  func (b *IOPinDriver) ReadPinIOConfig() (config int, err error) {
   133  	c, e := b.adaptor().ReadCharacteristic(pinIOConfigCharacteristic)
   134  	if e != nil {
   135  		return 0, e
   136  	}
   137  
   138  	var result byte
   139  	for i := 0; i < 4; i++ {
   140  		result |= c[i] << uint(i)
   141  	}
   142  
   143  	b.ioMask = int(result)
   144  	return int(result), nil
   145  }
   146  
   147  // WritePinIOConfig writes the pin I/O config mask for all pins
   148  func (b *IOPinDriver) WritePinIOConfig(config int) (err error) {
   149  	b.ioMask = config
   150  	data := &bytes.Buffer{}
   151  	binary.Write(data, binary.LittleEndian, uint32(config))
   152  	err = b.adaptor().WriteCharacteristic(pinIOConfigCharacteristic, data.Bytes())
   153  	return
   154  }
   155  
   156  // DigitalRead reads from a pin
   157  func (b *IOPinDriver) DigitalRead(pin string) (val int, err error) {
   158  	p, err := validatedPin(pin)
   159  	if err != nil {
   160  		return
   161  	}
   162  
   163  	b.ensureDigital(p)
   164  	b.ensureInput(p)
   165  
   166  	pins := b.ReadAllPinData()
   167  	return int(pins[p].value), nil
   168  }
   169  
   170  // DigitalWrite writes to a pin
   171  func (b *IOPinDriver) DigitalWrite(pin string, level byte) (err error) {
   172  	p, err := validatedPin(pin)
   173  	if err != nil {
   174  		return
   175  	}
   176  
   177  	b.ensureDigital(p)
   178  	b.ensureOutput(p)
   179  
   180  	return b.WritePinData(pin, level)
   181  }
   182  
   183  // AnalogRead reads from a pin
   184  func (b *IOPinDriver) AnalogRead(pin string) (val int, err error) {
   185  	p, err := validatedPin(pin)
   186  	if err != nil {
   187  		return
   188  	}
   189  
   190  	b.ensureAnalog(p)
   191  	b.ensureInput(p)
   192  
   193  	pins := b.ReadAllPinData()
   194  	return int(pins[p].value), nil
   195  }
   196  
   197  func (b *IOPinDriver) ensureDigital(pin int) {
   198  	if hasBit(b.adMask, pin) {
   199  		b.WritePinADConfig(clearBit(b.adMask, pin))
   200  	}
   201  }
   202  
   203  func (b *IOPinDriver) ensureAnalog(pin int) {
   204  	if !hasBit(b.adMask, pin) {
   205  		b.WritePinADConfig(setBit(b.adMask, pin))
   206  	}
   207  }
   208  
   209  func (b *IOPinDriver) ensureInput(pin int) {
   210  	if !hasBit(b.ioMask, pin) {
   211  		b.WritePinIOConfig(setBit(b.ioMask, pin))
   212  	}
   213  }
   214  
   215  func (b *IOPinDriver) ensureOutput(pin int) {
   216  	if hasBit(b.ioMask, pin) {
   217  		b.WritePinIOConfig(clearBit(b.ioMask, pin))
   218  	}
   219  }
   220  
   221  func validatedPin(pin string) (int, error) {
   222  	i, err := strconv.Atoi(pin)
   223  	if err != nil {
   224  		return 0, err
   225  	}
   226  
   227  	if i < 0 || i > 2 {
   228  		return 0, errors.New("Invalid pin.")
   229  	}
   230  
   231  	return i, nil
   232  }
   233  
   234  // via http://stackoverflow.com/questions/23192262/how-would-you-set-and-clear-a-single-bit-in-go
   235  // Sets the bit at pos in the integer n.
   236  func setBit(n int, pos int) int {
   237  	n |= (1 << uint(pos))
   238  	return n
   239  }
   240  
   241  // Test if the bit at pos is set in the integer n.
   242  func hasBit(n int, pos int) bool {
   243  	val := n & (1 << uint(pos))
   244  	return (val > 0)
   245  }
   246  
   247  // Clears the bit at pos in n.
   248  func clearBit(n int, pos int) int {
   249  	return n &^ (1 << uint(pos))
   250  }