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 }