gobot.io/x/gobot/v2@v2.1.0/drivers/i2c/grovepi_driver.go (about) 1 package i2c 2 3 import ( 4 "encoding/binary" 5 "fmt" 6 "math" 7 "strconv" 8 "strings" 9 "time" 10 ) 11 12 // default is for grovepi4 installer 13 const grovePiDefaultAddress = 0x04 14 15 // commands, see: 16 // * https://www.dexterindustries.com/GrovePi/programming/grovepi-protocol-adding-custom-sensors/ 17 // * https://github.com/DexterInd/GrovePi/blob/master/Script/multi_grovepi_installer/grovepi4.py 18 const ( 19 commandReadDigital = 1 20 commandWriteDigital = 2 21 commandReadAnalog = 3 22 commandWriteAnalog = 4 23 commandSetPinMode = 5 24 commandReadUltrasonic = 7 25 commandReadFirmwareVersion = 8 26 commandReadDHT = 40 27 ) 28 29 // GrovePiDriver is a driver for the GrovePi+ for I²C bus interface. 30 // https://www.dexterindustries.com/grovepi/ 31 // 32 // To use this driver with the GrovePi, it must be running the firmware >= 1.4.0 and and the system version >=3. 33 // https://github.com/DexterInd/GrovePi/blob/master/README.md 34 // 35 type GrovePiDriver struct { 36 *Driver 37 pins map[int]string 38 } 39 40 // NewGrovePiDriver creates a new driver with specified i2c interface 41 // Params: 42 // conn Connector - the Adaptor to use with this Driver 43 // 44 // Optional params: 45 // i2c.WithBus(int): bus to use with this driver 46 // i2c.WithAddress(int): address to use with this driver 47 // 48 func NewGrovePiDriver(c Connector, options ...func(Config)) *GrovePiDriver { 49 d := &GrovePiDriver{ 50 Driver: NewDriver(c, "GrovePi", grovePiDefaultAddress), 51 pins: make(map[int]string), 52 } 53 54 for _, option := range options { 55 option(d) 56 } 57 58 // TODO: add commands for API 59 return d 60 } 61 62 // Connect is here to implement the Adaptor interface. 63 func (d *GrovePiDriver) Connect() error { 64 return nil 65 } 66 67 // Finalize is here to implement the Adaptor interface. 68 func (d *GrovePiDriver) Finalize() error { 69 return nil 70 } 71 72 // AnalogRead returns value from analog pin implementing the AnalogReader interface. 73 func (d *GrovePiDriver) AnalogRead(pin string) (value int, err error) { 74 d.mutex.Lock() 75 defer d.mutex.Unlock() 76 77 pinNum, err := d.preparePin(pin, "input") 78 if err != nil { 79 return 0, err 80 } 81 82 buf := []byte{commandReadAnalog, byte(pinNum), 0, 0} 83 if _, err := d.connection.Write(buf); err != nil { 84 return 0, err 85 } 86 87 time.Sleep(2 * time.Millisecond) 88 89 data := make([]byte, 3) 90 if err = d.readForCommand(commandReadAnalog, data); err != nil { 91 return 0, err 92 } 93 94 return int(data[1])*256 + int(data[2]), nil 95 } 96 97 // DigitalRead performs a read on a digital pin. 98 func (d *GrovePiDriver) DigitalRead(pin string) (int, error) { 99 d.mutex.Lock() 100 defer d.mutex.Unlock() 101 102 pinNum, err := d.preparePin(pin, "input") 103 if err != nil { 104 return 0, err 105 } 106 107 buf := []byte{commandReadDigital, byte(pinNum), 0, 0} 108 if _, err := d.connection.Write(buf); err != nil { 109 return 0, err 110 } 111 112 time.Sleep(2 * time.Millisecond) 113 114 data := make([]byte, 2) 115 if err = d.readForCommand(commandReadDigital, data); err != nil { 116 return 0, err 117 } 118 119 return int(data[1]), nil 120 } 121 122 // UltrasonicRead performs a read on an ultrasonic pin with duration >=2 millisecond. 123 func (d *GrovePiDriver) UltrasonicRead(pin string, duration int) (int, error) { 124 d.mutex.Lock() 125 defer d.mutex.Unlock() 126 127 if duration < 2 { 128 duration = 2 129 } 130 131 pinNum, err := d.preparePin(pin, "input") 132 if err != nil { 133 return 0, err 134 } 135 136 buf := []byte{commandReadUltrasonic, byte(pinNum), 0, 0} 137 if _, err = d.connection.Write(buf); err != nil { 138 return 0, err 139 } 140 141 time.Sleep(time.Duration(duration) * time.Millisecond) 142 143 data := make([]byte, 3) 144 if err := d.readForCommand(commandReadUltrasonic, data); err != nil { 145 return 0, err 146 } 147 148 return int(data[1])*255 + int(data[2]), nil 149 } 150 151 // FirmwareVersionRead returns the GrovePi firmware version. 152 func (d *GrovePiDriver) FirmwareVersionRead() (string, error) { 153 d.mutex.Lock() 154 defer d.mutex.Unlock() 155 156 buf := []byte{commandReadFirmwareVersion, 0, 0, 0} 157 if _, err := d.connection.Write(buf); err != nil { 158 return "", err 159 } 160 161 time.Sleep(2 * time.Millisecond) 162 163 data := make([]byte, 4) 164 if err := d.readForCommand(commandReadFirmwareVersion, data); err != nil { 165 return "", err 166 } 167 168 return fmt.Sprintf("%v.%v.%v", data[1], data[2], data[3]), nil 169 } 170 171 // DHTRead performs a read temperature and humidity sensors with duration >=2 millisecond. 172 // DHT11 (blue): sensorType=0 173 // DHT22 (white): sensorTyp=1 174 func (d *GrovePiDriver) DHTRead(pin string, sensorType byte, duration int) (temp float32, hum float32, err error) { 175 d.mutex.Lock() 176 defer d.mutex.Unlock() 177 178 if duration < 2 { 179 duration = 2 180 } 181 182 pinNum, err := d.preparePin(pin, "input") 183 if err != nil { 184 return 0, 0, err 185 } 186 187 buf := []byte{commandReadDHT, byte(pinNum), sensorType, 0} 188 if _, err = d.connection.Write(buf); err != nil { 189 return 190 } 191 time.Sleep(time.Duration(duration) * time.Millisecond) 192 193 data := make([]byte, 9) 194 if err = d.readForCommand(commandReadDHT, data); err != nil { 195 return 196 } 197 198 temp = float32Of4BytesLittleEndian(data[1:5]) 199 if temp > 150 { 200 temp = 150 201 } 202 if temp < -100 { 203 temp = -100 204 } 205 206 hum = float32Of4BytesLittleEndian(data[5:9]) 207 if hum > 100 { 208 hum = 100 209 } 210 if hum < 0 { 211 hum = 0 212 } 213 214 return 215 } 216 217 // DigitalWrite writes a value to a specific digital pin implementing the DigitalWriter interface. 218 func (d *GrovePiDriver) DigitalWrite(pin string, val byte) error { 219 d.mutex.Lock() 220 defer d.mutex.Unlock() 221 222 pinNum, err := d.preparePin(pin, "output") 223 if err != nil { 224 return err 225 } 226 227 buf := []byte{commandWriteDigital, byte(pinNum), val, 0} 228 if _, err := d.connection.Write(buf); err != nil { 229 return err 230 } 231 232 time.Sleep(2 * time.Millisecond) 233 234 _, err = d.connection.ReadByte() 235 return err 236 } 237 238 // AnalogWrite writes PWM aka analog to the GrovePi analog pin implementing the AnalogWriter interface. 239 func (d *GrovePiDriver) AnalogWrite(pin string, val int) error { 240 d.mutex.Lock() 241 defer d.mutex.Unlock() 242 243 pinNum, err := d.preparePin(pin, "output") 244 if err != nil { 245 return err 246 } 247 248 buf := []byte{commandWriteAnalog, byte(pinNum), byte(val), 0} 249 if _, err := d.connection.Write(buf); err != nil { 250 return err 251 } 252 253 time.Sleep(2 * time.Millisecond) 254 255 _, err = d.connection.ReadByte() 256 return err 257 } 258 259 // SetPinMode sets the pin mode to input or output. 260 func (d *GrovePiDriver) SetPinMode(pin byte, mode string) error { 261 d.mutex.Lock() 262 defer d.mutex.Unlock() 263 264 return d.setPinMode(pin, mode) 265 } 266 267 func getPin(pin string) string { 268 if len(pin) > 1 { 269 if strings.ToUpper(pin[0:1]) == "A" || strings.ToUpper(pin[0:1]) == "D" { 270 return pin[1:] 271 } 272 } 273 274 return pin 275 } 276 277 func (d *GrovePiDriver) setPinMode(pin byte, mode string) error { 278 var b []byte 279 if mode == "output" { 280 b = []byte{commandSetPinMode, pin, 1, 0} 281 } else { 282 b = []byte{commandSetPinMode, pin, 0, 0} 283 } 284 if _, err := d.connection.Write(b); err != nil { 285 return err 286 } 287 288 time.Sleep(2 * time.Millisecond) 289 290 _, err := d.connection.ReadByte() 291 return err 292 } 293 294 func (d *GrovePiDriver) ensurePinMode(pinNum int, mode string) error { 295 if dir, ok := d.pins[pinNum]; !ok || dir != mode { 296 if err := d.setPinMode(byte(pinNum), mode); err != nil { 297 return err 298 } 299 d.pins[pinNum] = mode 300 } 301 return nil 302 } 303 304 func (d *GrovePiDriver) preparePin(pin string, mode string) (int, error) { 305 pin = getPin(pin) 306 pinNum, err := strconv.Atoi(pin) 307 if err != nil { 308 return -1, err 309 } 310 311 if err := d.ensurePinMode(pinNum, mode); err != nil { 312 return -1, err 313 } 314 315 return pinNum, nil 316 } 317 318 func (d *GrovePiDriver) readForCommand(command byte, data []byte) error { 319 cnt, err := d.connection.Read(data) 320 if err != nil { 321 return err 322 } 323 if len(data) != cnt { 324 return fmt.Errorf("read count mismatch (%d should be %d)", cnt, len(data)) 325 } 326 if data[0] != command { 327 return fmt.Errorf("answer (%d) was not for command (%d)", data[0], command) 328 } 329 return nil 330 } 331 332 func float32Of4BytesLittleEndian(bytes []byte) float32 { 333 bits := binary.LittleEndian.Uint32(bytes) 334 float := math.Float32frombits(bits) 335 return float 336 }