tinygo.org/x/drivers@v0.27.1-0.20240509133757-7dbca2a54349/seesaw/seesaw.go (about) 1 // Package seesaw provides a driver implementation to communicate with Adafruit's seesaw chip. 2 // There are many Adafruit boards that use a seesaw. Soil moisture sensors, LED keyboards, etc. 3 // 4 // - Documentation: https://learn.adafruit.com/adafruit-seesaw-atsamd09-breakout/overview 5 // - Arduino driver: https://github.com/adafruit/Adafruit_Seesaw 6 // - Seesaw firmware: https://github.com/adafruit/seesaw 7 package seesaw 8 9 import ( 10 "errors" 11 "strconv" 12 "time" 13 14 "tinygo.org/x/drivers" 15 ) 16 17 // DefaultAddress is the I2C address the chips have by default. Most boards 18 // built on top of it come with their own respective default addresses. 19 const DefaultAddress = 0x49 20 21 // DefaultReadDelay is an empirically determined delay used when reading from the device, 22 // the one from the official library seems to be too short (250us) 23 const DefaultReadDelay = 100 * time.Millisecond 24 25 const ( 26 seesawHwIdCodeSAMD09 = 0x55 // HW ID code for SAMD09 27 seesawHwIdCodeTINY8x7 = 0x87 // HW ID code for ATtiny817 28 ) 29 30 type Device struct { 31 bus drivers.I2C 32 Address uint16 33 ReadDelay time.Duration 34 } 35 36 func New(bus drivers.I2C) *Device { 37 return &Device{ 38 bus: bus, 39 Address: DefaultAddress, 40 ReadDelay: DefaultReadDelay, 41 } 42 } 43 44 // SoftReset triggers a soft-reset of seesaw and waits for it to be ready 45 func (d *Device) SoftReset() error { 46 err := d.WriteRegister(ModuleStatusBase, FunctionStatusSwrst, 0xFF) 47 if err != nil { 48 return errors.New("failed sending soft-reset command: " + err.Error()) 49 } 50 51 return d.waitForReset() 52 } 53 54 func (d *Device) waitForReset() error { 55 // give the device a little bit of time to reset 56 time.Sleep(time.Second) 57 58 var lastErr error 59 tries := 0 60 for ; tries < 20; tries++ { 61 _, err := d.readHardwareID() 62 if err == nil { 63 return nil 64 } 65 lastErr = err 66 time.Sleep(20 * time.Millisecond) 67 } 68 return errors.New("failed to wait for device to start: " + lastErr.Error()) 69 } 70 71 func (d *Device) readHardwareID() (byte, error) { 72 hwid, err := d.ReadRegister(ModuleStatusBase, FunctionStatusHwId) 73 if err != nil { 74 return 0, err 75 } 76 77 if hwid == seesawHwIdCodeSAMD09 || hwid == seesawHwIdCodeTINY8x7 { 78 return hwid, nil 79 } 80 81 return 0, errors.New("unknown hardware ID: " + strconv.FormatUint(uint64(hwid), 16)) 82 } 83 84 // WriteRegister writes a single seesaw register 85 func (d *Device) WriteRegister(module ModuleBaseAddress, function FunctionAddress, value byte) error { 86 var buf [3]byte 87 buf[0] = byte(module) 88 buf[1] = byte(function) 89 buf[2] = value 90 return d.bus.Tx(d.Address, buf[:], nil) 91 } 92 93 // ReadRegister reads a single register from seesaw 94 func (d *Device) ReadRegister(module ModuleBaseAddress, function FunctionAddress) (byte, error) { 95 var buf [1]byte 96 err := d.Read(module, function, buf[:]) 97 if err != nil { 98 return 0, err 99 } 100 return buf[0], nil 101 } 102 103 // Read reads a number of bytes from the device after sending the read command and waiting 'ReadDelay'. The delays depend 104 // on the module and function and are documented in the seesaw datasheet 105 func (d *Device) Read(module ModuleBaseAddress, function FunctionAddress, buf []byte) error { 106 var cmd [2]byte 107 cmd[0] = byte(module) 108 cmd[1] = byte(function) 109 110 err := d.bus.Tx(d.Address, cmd[:], nil) 111 if err != nil { 112 return err 113 } 114 115 // This is needed for the client seesaw device to flush its RX buffer and process the command. 116 // See seesaw datasheet for timings for specific modules. 117 time.Sleep(d.ReadDelay) 118 119 return d.bus.Tx(d.Address, nil, buf) 120 } 121 122 // Write writes data into a given module and function 123 func (d *Device) Write(module ModuleBaseAddress, function FunctionAddress, buf []byte) error { 124 cmd := []byte{byte(module), byte(function)} 125 data := append(cmd, buf...) 126 return d.bus.Tx(d.Address, data, nil) 127 }