mirror of
https://github.com/tinygo-org/drivers.git
synced 2026-07-31 21:17:49 +00:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ca37c1c461 | |||
| 5dfacc3c73 | |||
| fd21e6ac9b | |||
| 485ed702c8 | |||
| cfa50fd3c2 | |||
| 5888bb2ded |
@@ -1,3 +1,40 @@
|
||||
0.26.0
|
||||
---
|
||||
- **core**
|
||||
- i2c iface refactor: Resolve #559
|
||||
- fix uses of legacy i2c WriteRegister calls
|
||||
- add correct Tx implementation for mock I2C interfaces
|
||||
- bump golang.org/x/net version
|
||||
|
||||
- **new devices**
|
||||
- **bma42x**
|
||||
- add new BMA421/BMA425 driver
|
||||
- **ndir**
|
||||
- add Sandbox Electronics NDIR CO2 sensor driver (#580)
|
||||
- **mpu9150**
|
||||
- implement driver for Mpu9150 (#596)
|
||||
- **sht4x**
|
||||
- implement driver for sht4x (#597)
|
||||
- **pcf8523**
|
||||
- implement driver for pcf8523 (#599)
|
||||
|
||||
- **enhancements**
|
||||
- **ssd1306**
|
||||
- improve bus error handling
|
||||
|
||||
- **bugfixes**
|
||||
- **st7789**
|
||||
- fix scrolling when rotated by 180°
|
||||
- **st7789**
|
||||
- fix incorrect Rotation configuration
|
||||
- fix SetScrollArea
|
||||
- **ili9341**
|
||||
- fix SetScrollArea
|
||||
|
||||
- **build**
|
||||
- use latest tag of tinygo-dev container for running tests
|
||||
|
||||
|
||||
0.25.0
|
||||
---
|
||||
|
||||
|
||||
@@ -3,7 +3,10 @@
|
||||
[](https://pkg.go.dev/tinygo.org/x/drivers) [](https://github.com/tinygo-org/drivers/actions/workflows/build.yml)
|
||||
|
||||
|
||||
This package provides a collection of hardware drivers for devices such as sensors and displays that can be used together with [TinyGo](https://tinygo.org).
|
||||
This package provides a collection of 101 different hardware drivers for devices such as sensors and displays that can be used together with [TinyGo](https://tinygo.org).
|
||||
|
||||
For the complete list, please see:
|
||||
https://tinygo.org/docs/reference/devices/
|
||||
|
||||
## Installing
|
||||
|
||||
@@ -50,11 +53,6 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
## Supported devices
|
||||
|
||||
There are currently 96 devices supported. For the complete list, please see:
|
||||
https://tinygo.org/docs/reference/devices/
|
||||
|
||||
## Contributing
|
||||
|
||||
Your contributions are welcome!
|
||||
|
||||
+224
-145
@@ -8,15 +8,16 @@ import (
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
"tinygo.org/x/drivers/internal/legacy"
|
||||
)
|
||||
|
||||
// Device wraps an I2C connection to a APDS-9960 device.
|
||||
type Device struct {
|
||||
bus drivers.I2C
|
||||
_txerr error
|
||||
gesture gestureData
|
||||
buf [8]byte
|
||||
Address uint8
|
||||
mode uint8
|
||||
gesture gestureData
|
||||
}
|
||||
|
||||
// Configuration for APDS-9960 device.
|
||||
@@ -46,15 +47,24 @@ type gestureData struct {
|
||||
received bool
|
||||
}
|
||||
|
||||
// for enabling various device function
|
||||
type enableConfig struct {
|
||||
GEN bool
|
||||
PIEN bool
|
||||
AIEN bool
|
||||
WEN bool
|
||||
PEN bool
|
||||
AEN bool
|
||||
PON bool
|
||||
// for enabling various device functions.
|
||||
type encfg uint8
|
||||
|
||||
// data := []byte{gen<<6 | pien<<5 | aien<<4 | wen<<3 | pen<<2 | aen<<1 | pon}
|
||||
const (
|
||||
enPON encfg = 1 << iota
|
||||
enAEN
|
||||
enPEN
|
||||
enWEN
|
||||
enAIEN
|
||||
enPIEN
|
||||
enGEN
|
||||
)
|
||||
|
||||
func (e encfg) write7bits(b []byte) {
|
||||
for i := uint8(0); i < 7; i++ {
|
||||
b[i] = byte(e>>(6-i)) & 1
|
||||
}
|
||||
}
|
||||
|
||||
// New creates a new APDS-9960 connection. The I2C bus must already be
|
||||
@@ -68,9 +78,8 @@ func New(bus drivers.I2C) Device {
|
||||
// Connected returns whether APDS-9960 has been found.
|
||||
// It does a "who am I" request and checks the response.
|
||||
func (d *Device) Connected() bool {
|
||||
data := []byte{0}
|
||||
legacy.ReadRegister(d.bus, d.Address, APDS9960_ID_REG, data)
|
||||
return data[0] == 0xAB
|
||||
d.txNew()
|
||||
return d.txRead8(APDS9960_ID_REG) == 0xAB && d.txErr() == nil
|
||||
}
|
||||
|
||||
// GetMode returns current engine mode
|
||||
@@ -79,65 +88,82 @@ func (d *Device) GetMode() uint8 {
|
||||
}
|
||||
|
||||
// DisableAll turns off the device and all functions
|
||||
func (d *Device) DisableAll() {
|
||||
d.enable(enableConfig{})
|
||||
legacy.WriteRegister(d.bus, d.Address, APDS9960_GCONF4_REG, []byte{0x00})
|
||||
d.mode = MODE_NONE
|
||||
d.gesture.detected = GESTURE_NONE
|
||||
func (d *Device) DisableAll() error {
|
||||
err := d.enable(0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d.txWrite8(APDS9960_GCONF4_REG, 0)
|
||||
err = d.txErr()
|
||||
if err == nil {
|
||||
d.mode = MODE_NONE
|
||||
d.gesture.detected = GESTURE_NONE
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// SetProximityPulse sets proximity pulse length (4, 8, 16, 32) and count (1~64)
|
||||
// SetProximityPulse sets proximity pulse length (4, 8, 16, 32) and count (1..64)
|
||||
// default: 16, 64
|
||||
func (d *Device) SetProximityPulse(length, count uint8) {
|
||||
legacy.WriteRegister(d.bus, d.Address, APDS9960_PPULSE_REG, []byte{getPulseLength(length)<<6 | getPulseCount(count)})
|
||||
func (d *Device) SetProximityPulse(length, count uint8) error {
|
||||
d.txNew()
|
||||
d.txWrite8(APDS9960_PPULSE_REG, getPulseLength(length)<<6|getPulseCount(count))
|
||||
return d.txErr()
|
||||
}
|
||||
|
||||
// SetGesturePulse sets gesture pulse length (4, 8, 16, 32) and count (1~64)
|
||||
// SetGesturePulse sets gesture pulse length (4, 8, 16, 32) and count (1..64)
|
||||
// default: 16, 64
|
||||
func (d *Device) SetGesturePulse(length, count uint8) {
|
||||
legacy.WriteRegister(d.bus, d.Address, APDS9960_GPULSE_REG, []byte{getPulseLength(length)<<6 | getPulseCount(count)})
|
||||
func (d *Device) SetGesturePulse(length, count uint8) error {
|
||||
d.txNew()
|
||||
d.txWrite8(APDS9960_GPULSE_REG, getPulseLength(length)<<6|getPulseCount(count))
|
||||
return d.txErr()
|
||||
}
|
||||
|
||||
// SetADCIntegrationCycles sets ALS/color ADC internal integration cycles (1~256, 1 cycle = 2.78 ms)
|
||||
// default: 4 (~10 ms)
|
||||
func (d *Device) SetADCIntegrationCycles(cycles uint16) {
|
||||
// SetADCIntegrationCycles sets ALS/color ADC internal integration cycles (1..256, 1 cycle = 2.78 ms)
|
||||
// default: 4 (approx. 10 ms)
|
||||
func (d *Device) SetADCIntegrationCycles(cycles uint16) error {
|
||||
if cycles > 256 {
|
||||
cycles = 256
|
||||
}
|
||||
legacy.WriteRegister(d.bus, d.Address, APDS9960_ATIME_REG, []byte{uint8(256 - cycles)})
|
||||
d.txNew()
|
||||
d.txWrite8(APDS9960_ATIME_REG, uint8(256-cycles))
|
||||
return d.txErr()
|
||||
}
|
||||
|
||||
// SetGains sets proximity/gesture gain (1, 2, 4, 8x) and ALS/color gain (1, 4, 16, 64x)
|
||||
// default: 1, 1, 4
|
||||
func (d *Device) SetGains(proximityGain, gestureGain, colorGain uint8) {
|
||||
legacy.WriteRegister(d.bus, d.Address, APDS9960_CONTROL_REG, []byte{getProximityGain(proximityGain)<<2 | getALSGain(colorGain)})
|
||||
legacy.WriteRegister(d.bus, d.Address, APDS9960_GCONF2_REG, []byte{getProximityGain(gestureGain) << 5})
|
||||
func (d *Device) SetGains(proximityGain, gestureGain, colorGain uint8) error {
|
||||
d.txNew()
|
||||
d.txWrite8(APDS9960_CONTROL_REG, getProximityGain(proximityGain)<<2|getALSGain(colorGain))
|
||||
d.txWrite8(APDS9960_GCONF2_REG, getProximityGain(gestureGain)<<5)
|
||||
return d.txErr()
|
||||
}
|
||||
|
||||
// LEDBoost sets proximity and gesture LED current level (100, 150, 200, 300 (%))
|
||||
// default: 100
|
||||
func (d *Device) LEDBoost(percent uint16) {
|
||||
func (d *Device) LEDBoost(percent uint16) error {
|
||||
var v uint8
|
||||
switch percent {
|
||||
case 100:
|
||||
switch {
|
||||
case percent < 125:
|
||||
v = 0
|
||||
case 150:
|
||||
case percent < 175:
|
||||
v = 1
|
||||
case 200:
|
||||
case percent < 250:
|
||||
v = 2
|
||||
case 300:
|
||||
v = 3
|
||||
default:
|
||||
v = 3 // Maximum case.
|
||||
}
|
||||
legacy.WriteRegister(d.bus, d.Address, APDS9960_CONFIG2_REG, []byte{0x01 | v<<4})
|
||||
d.txNew()
|
||||
d.txWrite8(APDS9960_CONFIG2_REG, 0x01|(v<<4))
|
||||
return d.txErr()
|
||||
}
|
||||
|
||||
// Setthreshold sets threshold (0~255) for detecting gestures
|
||||
// Setthreshold sets threshold (0..255) for detecting gestures
|
||||
// default: 30
|
||||
func (d *Device) Setthreshold(t uint8) {
|
||||
d.gesture.threshold = t
|
||||
}
|
||||
|
||||
// Setsensitivity sets sensivity (0~100) for detecting gestures
|
||||
// Setsensitivity sets sensivity (0..100) for detecting gestures
|
||||
// default: 20
|
||||
func (d *Device) Setsensitivity(s uint8) {
|
||||
if s > 100 {
|
||||
@@ -147,47 +173,69 @@ func (d *Device) Setsensitivity(s uint8) {
|
||||
}
|
||||
|
||||
// EnableProximity starts the proximity engine
|
||||
func (d *Device) EnableProximity() {
|
||||
func (d *Device) EnableProximity() error {
|
||||
if d.mode != MODE_NONE {
|
||||
d.DisableAll()
|
||||
err := d.DisableAll()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
d.enable(enableConfig{PON: true, PEN: true, WEN: true})
|
||||
d.mode = MODE_PROXIMITY
|
||||
err := d.enable(enPON | enPEN | enWEN)
|
||||
if err == nil {
|
||||
d.mode = MODE_PROXIMITY
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Err returns the current error state of the device if encountered during I2C communication.
|
||||
// After a call to Err the error is cleared.
|
||||
func (d *Device) Err() error {
|
||||
err := d.txErr()
|
||||
d.txNew()
|
||||
return err
|
||||
}
|
||||
|
||||
// ProximityAvailable reports if proximity data is available
|
||||
func (d *Device) ProximityAvailable() bool {
|
||||
if d.mode == MODE_PROXIMITY && d.readStatus("PVALID") {
|
||||
return true
|
||||
if d.mode != MODE_PROXIMITY {
|
||||
return false
|
||||
}
|
||||
return false
|
||||
status, err := d.ReadStatus()
|
||||
return err == nil && status.PVALID()
|
||||
}
|
||||
|
||||
// ReadProximity reads proximity data (0~255)
|
||||
// ReadProximity reads proximity data (0..255)
|
||||
func (d *Device) ReadProximity() (proximity int32) {
|
||||
if d.mode != MODE_PROXIMITY {
|
||||
return 0
|
||||
}
|
||||
data := []byte{0}
|
||||
legacy.ReadRegister(d.bus, d.Address, APDS9960_PDATA_REG, data)
|
||||
return 255 - int32(data[0])
|
||||
d.txNew()
|
||||
val := d.txRead8(APDS9960_PDATA_REG)
|
||||
return 255 - int32(val)
|
||||
}
|
||||
|
||||
// EnableColor starts the color engine
|
||||
func (d *Device) EnableColor() {
|
||||
func (d *Device) EnableColor() (err error) {
|
||||
if d.mode != MODE_NONE {
|
||||
d.DisableAll()
|
||||
err = d.DisableAll()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
d.enable(enableConfig{PON: true, AEN: true, WEN: true})
|
||||
d.mode = MODE_COLOR
|
||||
err = d.enable(enPON | enAEN | enWEN)
|
||||
if err == nil {
|
||||
d.mode = MODE_COLOR
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// ColorAvailable reports if color data is available
|
||||
func (d *Device) ColorAvailable() bool {
|
||||
if d.mode == MODE_COLOR && d.readStatus("AVALID") {
|
||||
return true
|
||||
if d.mode != MODE_COLOR {
|
||||
return false
|
||||
}
|
||||
return false
|
||||
status, err := d.ReadStatus()
|
||||
return err == nil && status.AVALID()
|
||||
}
|
||||
|
||||
// ReadColor reads color data (red, green, blue, clear color/brightness)
|
||||
@@ -195,28 +243,36 @@ func (d *Device) ReadColor() (r int32, g int32, b int32, clear int32) {
|
||||
if d.mode != MODE_COLOR {
|
||||
return
|
||||
}
|
||||
data := []byte{0, 0, 0, 0, 0, 0, 0, 0}
|
||||
legacy.ReadRegister(d.bus, d.Address, APDS9960_CDATAL_REG, data[:1])
|
||||
legacy.ReadRegister(d.bus, d.Address, APDS9960_CDATAH_REG, data[1:2])
|
||||
legacy.ReadRegister(d.bus, d.Address, APDS9960_RDATAL_REG, data[2:3])
|
||||
legacy.ReadRegister(d.bus, d.Address, APDS9960_RDATAH_REG, data[3:4])
|
||||
legacy.ReadRegister(d.bus, d.Address, APDS9960_GDATAL_REG, data[4:5])
|
||||
legacy.ReadRegister(d.bus, d.Address, APDS9960_GDATAH_REG, data[5:6])
|
||||
legacy.ReadRegister(d.bus, d.Address, APDS9960_BDATAL_REG, data[6:7])
|
||||
legacy.ReadRegister(d.bus, d.Address, APDS9960_BDATAH_REG, data[7:])
|
||||
d.txNew()
|
||||
data := d.buf[:8]
|
||||
const numLowRegs = APDS9960_GDATAH_REG - APDS9960_CDATAL_REG + 1
|
||||
for i := uint8(0); i < numLowRegs; i++ {
|
||||
data[i] = d.txRead8(i + APDS9960_CDATAL_REG)
|
||||
}
|
||||
data[numLowRegs] = d.txRead8(APDS9960_BDATAL_REG)
|
||||
data[numLowRegs+1] = d.txRead8(APDS9960_BDATAH_REG)
|
||||
if d.txErr() != nil {
|
||||
return
|
||||
}
|
||||
clear = int32(uint16(data[1])<<8 | uint16(data[0]))
|
||||
r = int32(uint16(data[3])<<8 | uint16(data[2]))
|
||||
g = int32(uint16(data[5])<<8 | uint16(data[4]))
|
||||
b = int32(uint16(data[7])<<8 | uint16(data[6]))
|
||||
return
|
||||
return r, g, b, clear
|
||||
}
|
||||
|
||||
// EnableGesture starts the gesture engine
|
||||
func (d *Device) EnableGesture() {
|
||||
func (d *Device) EnableGesture() error {
|
||||
if d.mode != MODE_NONE {
|
||||
d.DisableAll()
|
||||
err := d.DisableAll()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
err := d.enable(enPON | enPEN | enGEN | enWEN)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d.enable(enableConfig{PON: true, PEN: true, GEN: true, WEN: true})
|
||||
d.mode = MODE_GESTURE
|
||||
d.gesture.detected = GESTURE_NONE
|
||||
d.gesture.gXDelta = 0
|
||||
@@ -224,6 +280,7 @@ func (d *Device) EnableGesture() {
|
||||
d.gesture.gXPrevDelta = 0
|
||||
d.gesture.gYPrevDelta = 0
|
||||
d.gesture.received = false
|
||||
return nil
|
||||
}
|
||||
|
||||
// GestureAvailable reports if gesture data is available
|
||||
@@ -231,29 +288,26 @@ func (d *Device) GestureAvailable() bool {
|
||||
if d.mode != MODE_GESTURE {
|
||||
return false
|
||||
}
|
||||
|
||||
data := []byte{0, 0, 0, 0}
|
||||
|
||||
// check GVALID
|
||||
legacy.ReadRegister(d.bus, d.Address, APDS9960_GSTATUS_REG, data[:1])
|
||||
if data[0]&0x01 == 0 {
|
||||
d.txNew()
|
||||
gstatus := d.txRead8(APDS9960_GSTATUS_REG)
|
||||
if gstatus&1 == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
// get number of data sets available in FIFO
|
||||
legacy.ReadRegister(d.bus, d.Address, APDS9960_GFLVL_REG, data[:1])
|
||||
availableDataSets := data[0]
|
||||
availableDataSets := d.txRead8(APDS9960_GFLVL_REG)
|
||||
if availableDataSets == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
data := d.buf[:]
|
||||
// read up, down, left and right proximity data from FIFO
|
||||
var dataSets [32][4]uint8
|
||||
const numAddrs = APDS9960_GFIFO_R_REG - APDS9960_GFIFO_U_REG + 1
|
||||
for i := uint8(0); i < availableDataSets; i++ {
|
||||
legacy.ReadRegister(d.bus, d.Address, APDS9960_GFIFO_U_REG, data[:1])
|
||||
legacy.ReadRegister(d.bus, d.Address, APDS9960_GFIFO_D_REG, data[1:2])
|
||||
legacy.ReadRegister(d.bus, d.Address, APDS9960_GFIFO_L_REG, data[2:3])
|
||||
legacy.ReadRegister(d.bus, d.Address, APDS9960_GFIFO_R_REG, data[3:4])
|
||||
for j := uint8(0); j < numAddrs; j++ {
|
||||
data[j] = d.txRead8(j + APDS9960_GFIFO_U_REG)
|
||||
}
|
||||
if d.txErr() != nil {
|
||||
return false
|
||||
}
|
||||
for j := uint8(0); j < 4; j++ {
|
||||
dataSets[i][j] = data[j]
|
||||
}
|
||||
@@ -315,9 +369,11 @@ func (d *Device) ReadGesture() (gesture int32) {
|
||||
|
||||
// private functions
|
||||
|
||||
func (d *Device) configureDevice(cfg Configuration) {
|
||||
d.DisableAll() // turn off everything
|
||||
|
||||
func (d *Device) configureDevice(cfg Configuration) error {
|
||||
err := d.DisableAll() // turn off everything
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// "default" settings
|
||||
if cfg.ProximityPulseLength == 0 {
|
||||
cfg.ProximityPulseLength = 16
|
||||
@@ -350,69 +406,92 @@ func (d *Device) configureDevice(cfg Configuration) {
|
||||
d.gesture.sensitivity = 20
|
||||
}
|
||||
|
||||
d.SetProximityPulse(cfg.ProximityPulseLength, cfg.ProximityPulseCount)
|
||||
d.SetGesturePulse(cfg.GesturePulseLength, cfg.GesturePulseCount)
|
||||
d.SetGains(cfg.ProximityGain, cfg.GestureGain, cfg.ColorGain)
|
||||
d.SetADCIntegrationCycles(cfg.ADCIntegrationCycles)
|
||||
|
||||
if cfg.LEDBoost > 0 {
|
||||
d.LEDBoost(cfg.LEDBoost)
|
||||
err = d.SetProximityPulse(cfg.ProximityPulseLength, cfg.ProximityPulseCount)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = d.SetGesturePulse(cfg.GesturePulseLength, cfg.GesturePulseCount)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = d.SetGains(cfg.ProximityGain, cfg.GestureGain, cfg.ColorGain)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = d.SetADCIntegrationCycles(cfg.ADCIntegrationCycles)
|
||||
if err == nil && cfg.LEDBoost > 0 {
|
||||
err = d.LEDBoost(cfg.LEDBoost)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (d *Device) enable(cfg enableConfig) {
|
||||
var gen, pien, aien, wen, pen, aen, pon uint8
|
||||
|
||||
if cfg.GEN {
|
||||
gen = 1
|
||||
}
|
||||
if cfg.PIEN {
|
||||
pien = 1
|
||||
}
|
||||
if cfg.AIEN {
|
||||
aien = 1
|
||||
}
|
||||
if cfg.WEN {
|
||||
wen = 1
|
||||
}
|
||||
if cfg.PEN {
|
||||
pen = 1
|
||||
}
|
||||
if cfg.AEN {
|
||||
aen = 1
|
||||
}
|
||||
if cfg.PON {
|
||||
pon = 1
|
||||
}
|
||||
|
||||
data := []byte{gen<<6 | pien<<5 | aien<<4 | wen<<3 | pen<<2 | aen<<1 | pon}
|
||||
legacy.WriteRegister(d.bus, d.Address, APDS9960_ENABLE_REG, data)
|
||||
|
||||
if cfg.PON {
|
||||
func (d *Device) enable(cfg encfg) error {
|
||||
d.txNew()
|
||||
cfg.write7bits(d.buf[:7])
|
||||
d.txWrite(APDS9960_ENABLE_REG, d.buf[:7])
|
||||
err := d.txErr()
|
||||
if err == nil && cfg&enPON != 0 {
|
||||
time.Sleep(time.Millisecond * 10)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (d *Device) readStatus(param string) bool {
|
||||
data := []byte{0}
|
||||
legacy.ReadRegister(d.bus, d.Address, APDS9960_STATUS_REG, data)
|
||||
func (d *Device) txErr() error { return d._txerr }
|
||||
|
||||
switch param {
|
||||
case "CPSAT":
|
||||
return data[0]>>7&0x01 == 1
|
||||
case "PGSAT":
|
||||
return data[0]>>6&0x01 == 1
|
||||
case "PINT":
|
||||
return data[0]>>5&0x01 == 1
|
||||
case "AINT":
|
||||
return data[0]>>4&0x01 == 1
|
||||
case "PVALID":
|
||||
return data[0]>>1&0x01 == 1
|
||||
case "AVALID":
|
||||
return data[0]&0x01 == 1
|
||||
default:
|
||||
return false
|
||||
func (d *Device) txNew() { d._txerr = nil }
|
||||
|
||||
func (d *Device) txRead8(addr uint8) uint8 {
|
||||
if d._txerr != nil {
|
||||
return 0
|
||||
}
|
||||
d.buf[0] = addr
|
||||
d._txerr = d.bus.Tx(uint16(d.Address), d.buf[:1], d.buf[1:2])
|
||||
return d.buf[1]
|
||||
}
|
||||
|
||||
func (d *Device) txWrite8(addr uint8, val uint8) {
|
||||
if d._txerr != nil {
|
||||
return
|
||||
}
|
||||
d.buf[0] = addr
|
||||
d.buf[1] = val
|
||||
d._txerr = d.bus.Tx(uint16(d.Address), d.buf[:2], nil)
|
||||
}
|
||||
|
||||
func (d *Device) txWrite(addr uint8, data []byte) {
|
||||
if d._txerr != nil {
|
||||
return
|
||||
} else if len(data) > len(d.buf)-1 {
|
||||
panic("txWrite: data too long")
|
||||
}
|
||||
d.buf[0] = addr
|
||||
copy(d.buf[1:], data)
|
||||
d._txerr = d.bus.Tx(uint16(d.Address), d.buf[:len(data)+1], nil)
|
||||
}
|
||||
|
||||
type status uint8
|
||||
|
||||
const (
|
||||
statusAVALID status = 1 << iota
|
||||
statusPVALID
|
||||
_
|
||||
_
|
||||
statusAINT
|
||||
statusPINT
|
||||
statusPGSAT
|
||||
statusCPSAT
|
||||
)
|
||||
|
||||
func (s status) CPSAT() bool { return s&statusCPSAT != 0 }
|
||||
func (s status) PGSAT() bool { return s&statusPGSAT != 0 }
|
||||
func (s status) PINT() bool { return s&statusPINT != 0 }
|
||||
func (s status) AINT() bool { return s&statusAINT != 0 }
|
||||
func (s status) PVALID() bool { return s&statusPVALID != 0 }
|
||||
func (s status) AVALID() bool { return s&statusAVALID != 0 }
|
||||
|
||||
func (d *Device) ReadStatus() (status, error) {
|
||||
d.txNew()
|
||||
return status(d.txRead8(APDS9960_STATUS_REG)), d.txErr()
|
||||
}
|
||||
|
||||
func getPulseLength(l uint8) uint8 {
|
||||
|
||||
@@ -8,33 +8,42 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
// Sleep to catch any errors through the serial monitor.
|
||||
time.Sleep(1000 * time.Millisecond)
|
||||
bus := machine.I2C0
|
||||
// use Nano 33 BLE Sense's internal I2C bus
|
||||
machine.I2C1.Configure(machine.I2CConfig{
|
||||
SCL: machine.SCL1_PIN,
|
||||
SDA: machine.SDA1_PIN,
|
||||
Frequency: machine.TWI_FREQ_400KHZ,
|
||||
err := bus.Configure(machine.I2CConfig{
|
||||
SCL: machine.GP1,
|
||||
SDA: machine.GP0,
|
||||
Frequency: 400 * machine.KHz,
|
||||
})
|
||||
if err != nil {
|
||||
panic(err.Error())
|
||||
}
|
||||
|
||||
sensor := apds9960.New(machine.I2C1)
|
||||
sensor := apds9960.New(bus)
|
||||
|
||||
// use default settings
|
||||
sensor.Configure(apds9960.Configuration{})
|
||||
|
||||
if !sensor.Connected() {
|
||||
println("APDS-9960 not connected!")
|
||||
println("err:", sensor.Err())
|
||||
return
|
||||
}
|
||||
|
||||
sensor.EnableProximity() // enable proximity engine
|
||||
|
||||
println("APDS connected!")
|
||||
err = sensor.EnableProximity() // enable proximity engine
|
||||
if err != nil {
|
||||
panic(err.Error())
|
||||
}
|
||||
for {
|
||||
|
||||
if sensor.ProximityAvailable() {
|
||||
p := sensor.ReadProximity()
|
||||
println("Proximity:", p)
|
||||
}
|
||||
if err := sensor.Err(); err != nil {
|
||||
println(err.Error())
|
||||
}
|
||||
time.Sleep(time.Millisecond * 100)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
"time"
|
||||
"tinygo.org/x/drivers/pcf8523"
|
||||
)
|
||||
|
||||
func main() {
|
||||
machine.I2C0.Configure(machine.I2CConfig{})
|
||||
dev := pcf8523.New(machine.I2C0)
|
||||
|
||||
// make sure the battery takes over if power is lost
|
||||
err := dev.SetPowerManagement(pcf8523.PowerManagement_SwitchOver_ModeStandard)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// set RTC once, i.e. from `date -u +"%Y-%m-%dT%H:%M:%SZ"`
|
||||
now, _ := time.Parse(time.RFC3339, "2023-09-18T20:31:38Z")
|
||||
err = dev.SetTime(now)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
for {
|
||||
ts, err := dev.ReadTime()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
println("tick-tock, it's: " + ts.String())
|
||||
time.Sleep(2 * time.Second)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"machine"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/sht4x"
|
||||
)
|
||||
|
||||
func main() {
|
||||
machine.I2C0.Configure(machine.I2CConfig{})
|
||||
sensor := sht4x.New(machine.I2C0)
|
||||
|
||||
for {
|
||||
temp, humidity, _ := sensor.ReadTemperatureHumidity()
|
||||
t := fmt.Sprintf("%.2f", float32(temp)/1000)
|
||||
h := fmt.Sprintf("%.2f", float32(humidity)/100)
|
||||
println("Temperature: ", t, "°C")
|
||||
println("Humidity: ", h, "%")
|
||||
time.Sleep(2 * time.Second)
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
module tinygo.org/x/drivers
|
||||
|
||||
go 1.15
|
||||
go 1.18
|
||||
|
||||
require (
|
||||
github.com/eclipse/paho.mqtt.golang v1.2.0
|
||||
@@ -9,4 +9,12 @@ require (
|
||||
golang.org/x/net v0.7.0
|
||||
tinygo.org/x/tinyfont v0.3.0
|
||||
tinygo.org/x/tinyterm v0.1.0
|
||||
)
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/google/go-cmp v0.5.2 // indirect
|
||||
github.com/kr/pretty v0.2.1 // indirect
|
||||
github.com/kr/text v0.1.0 // indirect
|
||||
golang.org/x/text v0.7.0 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 // indirect
|
||||
)
|
||||
|
||||
@@ -7,56 +7,22 @@ github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM=
|
||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
|
||||
github.com/hajimehoshi/go-jisx0208 v1.0.0/go.mod h1:yYxEStHL7lt9uL+AbdWgW9gBumwieDoZCiB1f/0X0as=
|
||||
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/sago35/go-bdf v0.0.0-20200313142241-6c17821c91c4/go.mod h1:rOebXGuMLsXhZAC6mF/TjxONsm45498ZyzVhel++6KM=
|
||||
github.com/valyala/fastjson v1.6.3/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
|
||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
tinygo.org/x/drivers v0.14.0/go.mod h1:uT2svMq3EpBZpKkGO+NQHjxjGf1f42ra4OnMMwQL2aI=
|
||||
tinygo.org/x/drivers v0.15.1/go.mod h1:uT2svMq3EpBZpKkGO+NQHjxjGf1f42ra4OnMMwQL2aI=
|
||||
tinygo.org/x/drivers v0.16.0/go.mod h1:uT2svMq3EpBZpKkGO+NQHjxjGf1f42ra4OnMMwQL2aI=
|
||||
tinygo.org/x/drivers v0.19.0/go.mod h1:uJD/l1qWzxzLx+vcxaW0eY464N5RAgFi1zTVzASFdqI=
|
||||
tinygo.org/x/tinyfont v0.2.1/go.mod h1:eLqnYSrFRjt5STxWaMeOWJTzrKhXqpWw7nU3bPfKOAM=
|
||||
tinygo.org/x/tinyfont v0.3.0 h1:HIRLQoI3oc+2CMhPcfv+Ig88EcTImE/5npjqOnMD4lM=
|
||||
tinygo.org/x/tinyfont v0.3.0/go.mod h1:+TV5q0KpwSGRWnN+ITijsIhrWYJkoUCp9MYELjKpAXk=
|
||||
tinygo.org/x/tinyfs v0.1.0/go.mod h1:ysc8Y92iHfhTXeyEM9+c7zviUQ4fN9UCFgSOFfMWv20=
|
||||
tinygo.org/x/tinyterm v0.1.0 h1:80i+j+KWoxCFa/Xfp6pWbh79x+8zUdMXC1vaKj2QhkY=
|
||||
tinygo.org/x/tinyterm v0.1.0/go.mod h1:/DDhNnGwNF2/tNgHywvyZuCGnbH3ov49Z/6e8LPLRR4=
|
||||
tinygo.org/x/tinyterm v0.1.0/go.mod h1:/DDhNnGwNF2/tNgHywvyZuCGnbH3ov49Z/6e8LPLRR4=
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
// Package pcf8523 implements a driver for the PCF8523 CMOS Real-Time Clock (RTC)
|
||||
//
|
||||
// Datasheet: https://www.nxp.com/docs/en/data-sheet/PCF8523.pdf
|
||||
package pcf8523
|
||||
|
||||
import (
|
||||
"time"
|
||||
"tinygo.org/x/drivers"
|
||||
)
|
||||
|
||||
type Device struct {
|
||||
bus drivers.I2C
|
||||
Address uint8
|
||||
}
|
||||
|
||||
func New(i2c drivers.I2C) Device {
|
||||
return Device{
|
||||
bus: i2c,
|
||||
Address: DefaultAddress,
|
||||
}
|
||||
}
|
||||
|
||||
// Reset resets the device according to the datasheet section 8.3
|
||||
// This does not wipe the time registers, but resets control registers.
|
||||
func (d *Device) Reset() (err error) {
|
||||
return d.bus.Tx(uint16(d.Address), []byte{rControl1, 0x58}, nil)
|
||||
}
|
||||
|
||||
// SetPowerManagement configures how the device makes use of the backup battery, see
|
||||
// datasheet section 8.5
|
||||
func (d *Device) SetPowerManagement(b PowerManagement) error {
|
||||
return d.setRegister(rControl3, byte(b)<<5, 0xE0)
|
||||
}
|
||||
|
||||
func (d *Device) setRegister(reg uint8, value, mask uint8) error {
|
||||
var buf [1]byte
|
||||
err := d.bus.Tx(uint16(d.Address), []byte{reg}, buf[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
buf[0] = (value & mask) | (buf[0] & (^mask))
|
||||
return d.bus.Tx(uint16(d.Address), []byte{reg, buf[0]}, nil)
|
||||
}
|
||||
|
||||
// SetTime sets the time and date
|
||||
func (d *Device) SetTime(t time.Time) error {
|
||||
buf := []byte{
|
||||
rSeconds,
|
||||
bin2bcd(t.Second()),
|
||||
bin2bcd(t.Minute()),
|
||||
bin2bcd(t.Hour()),
|
||||
bin2bcd(t.Day()),
|
||||
bin2bcd(int(t.Weekday())),
|
||||
bin2bcd(int(t.Month())),
|
||||
bin2bcd(t.Year() - 2000),
|
||||
}
|
||||
|
||||
return d.bus.Tx(uint16(d.Address), buf, nil)
|
||||
}
|
||||
|
||||
// ReadTime returns the date and time
|
||||
func (d *Device) ReadTime() (time.Time, error) {
|
||||
buf := make([]byte, 9)
|
||||
err := d.bus.Tx(uint16(d.Address), []byte{rSeconds}, buf)
|
||||
if err != nil {
|
||||
return time.Time{}, err
|
||||
}
|
||||
|
||||
seconds := bcd2bin(buf[0] & 0x7F)
|
||||
minute := bcd2bin(buf[1] & 0x7F)
|
||||
hour := bcd2bin(buf[2] & 0x3F)
|
||||
day := bcd2bin(buf[3] & 0x3F)
|
||||
//skipping weekday buf[4]
|
||||
month := time.Month(bcd2bin(buf[5] & 0x1F))
|
||||
year := int(bcd2bin(buf[6])) + 2000
|
||||
|
||||
t := time.Date(year, month, day, hour, minute, seconds, 0, time.UTC)
|
||||
return t, nil
|
||||
}
|
||||
|
||||
// bin2bcd converts binary to BCD
|
||||
func bin2bcd(dec int) uint8 {
|
||||
return uint8(dec + 6*(dec/10))
|
||||
}
|
||||
|
||||
// bcd2bin converts BCD to binary
|
||||
func bcd2bin(bcd uint8) int {
|
||||
return int(bcd - 6*(bcd>>4))
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package pcf8523
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"testing"
|
||||
"time"
|
||||
"tinygo.org/x/drivers/tester"
|
||||
)
|
||||
|
||||
func TestDecToBcd_RoundTrip(t *testing.T) {
|
||||
|
||||
for i := 0; i < 60; i++ {
|
||||
a := bcd2bin(bin2bcd(i))
|
||||
if a != i {
|
||||
t.Logf("not equal: %d != %d", a, i)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDevice_Reset(t *testing.T) {
|
||||
bus := tester.NewI2CBus(t)
|
||||
fake := bus.NewDevice(DefaultAddress)
|
||||
|
||||
dev := New(bus)
|
||||
|
||||
err := dev.Reset()
|
||||
assertNoError(t, err)
|
||||
|
||||
assertEquals(t, fake.Registers[rControl1], 0x58)
|
||||
}
|
||||
|
||||
func TestDevice_SetPowerManagement(t *testing.T) {
|
||||
bus := tester.NewI2CBus(t)
|
||||
fake := bus.NewDevice(DefaultAddress)
|
||||
|
||||
dev := New(bus)
|
||||
|
||||
err := dev.SetPowerManagement(PowerManagement_SwitchOver_ModeStandard)
|
||||
assertNoError(t, err)
|
||||
|
||||
assertEquals(t, fake.Registers[rControl3], 0b100<<5)
|
||||
}
|
||||
|
||||
func TestDevice_SetTime(t *testing.T) {
|
||||
bus := tester.NewI2CBus(t)
|
||||
fake := bus.NewDevice(DefaultAddress)
|
||||
|
||||
dev := New(bus)
|
||||
|
||||
pointInTime, _ := time.Parse(time.RFC3339, "2023-09-12T22:35:50Z")
|
||||
err := dev.SetTime(pointInTime)
|
||||
assertNoError(t, err)
|
||||
|
||||
actual := hex.EncodeToString(fake.Registers[rSeconds : rSeconds+7])
|
||||
expected := "50352212020923"
|
||||
assertEquals(t, actual, expected)
|
||||
}
|
||||
|
||||
func TestDevice_ReadTime(t *testing.T) {
|
||||
bus := tester.NewI2CBus(t)
|
||||
fake := bus.NewDevice(DefaultAddress)
|
||||
|
||||
expectedPointInTime := time.Date(2023, 9, 12, 17, 55, 42, 0, time.UTC)
|
||||
fake.Registers[rSeconds] = 0x42
|
||||
fake.Registers[rMinutes] = 0x55
|
||||
fake.Registers[rHours] = 0x17
|
||||
fake.Registers[rDays] = 0x12
|
||||
fake.Registers[rMonths] = 0x9
|
||||
fake.Registers[rYears] = 0x23
|
||||
|
||||
dev := New(bus)
|
||||
|
||||
//when
|
||||
actualPointInTime, err := dev.ReadTime()
|
||||
|
||||
//then
|
||||
assertNoError(t, err)
|
||||
assertEquals(t, actualPointInTime, expectedPointInTime)
|
||||
}
|
||||
|
||||
func assertNoError(t testing.TB, e error) {
|
||||
if e != nil {
|
||||
t.Fatalf("unexpected error: %v", e)
|
||||
}
|
||||
}
|
||||
func assertEquals[T comparable](t testing.TB, a, b T) {
|
||||
if a != b {
|
||||
t.Fatalf("%v != %v", a, b)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package pcf8523
|
||||
|
||||
const DefaultAddress = 0x68
|
||||
|
||||
// datasheet 8.5 Power management functions, table 11
|
||||
type PowerManagement byte
|
||||
|
||||
const (
|
||||
PowerManagement_SwitchOver_ModeStandard_LowDetection PowerManagement = 0b000
|
||||
PowerManagement_SwitchOver_ModeDirect_LowDetection PowerManagement = 0b001
|
||||
PowerManagement_VddOnly_LowDetection PowerManagement = 0b010
|
||||
PowerManagement_SwitchOver_ModeStandard PowerManagement = 0b100
|
||||
PowerManagement_SwitchOver_ModeDirect PowerManagement = 0b101
|
||||
PowerManagement_VddOnly PowerManagement = 0b101
|
||||
)
|
||||
|
||||
// constants for all internal registers
|
||||
const (
|
||||
rControl1 = 0x00 // Control_1
|
||||
rControl2 = 0x01 // Control_2
|
||||
rControl3 = 0x02 // Control_3
|
||||
rSeconds = 0x03 // Seconds
|
||||
rMinutes = 0x04 // Minutes
|
||||
rHours = 0x05 // Hours
|
||||
rDays = 0x06 // Days
|
||||
rWeekdays = 0x07 // Weekdays
|
||||
rMonths = 0x08 // Months
|
||||
rYears = 0x09 // Years
|
||||
rMinuteAlarm = 0x0A // Minute_alarm
|
||||
rHourAlarm = 0x0B // Hour_alarm
|
||||
rDayAlarm = 0x0C // Day_alarm
|
||||
rWeekdayAlarm = 0x0D // Weekday_alarm
|
||||
rOffset = 0x0E // Offset
|
||||
rTimerClkoutControl = 0x0F // Tmr_CLKOUT_ctrl
|
||||
rTimerAFrequencyControl = 0x10 // Tmr_A_freq_ctrl
|
||||
rTimerARegister = 0x11 // Tmr_A_reg
|
||||
rTimerBFrequencyControl = 0x12 // Tmr_B_freq_ctrl
|
||||
rTimerBRegister = 0x13 // Tmr_B_reg
|
||||
)
|
||||
@@ -0,0 +1,76 @@
|
||||
// Package sht4x provides a driver for the SHT4x digital humidity sensor series by Sensirion.
|
||||
// Datasheet: https://www.sensirion.com/media/documents/33FD6951/64D3B030/Sensirion_Datasheet_SHT4x.pdf
|
||||
package sht4x
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
)
|
||||
|
||||
const DefaultAddress = 0x44
|
||||
|
||||
const (
|
||||
// single-shot, high-repeatability measurement
|
||||
commandMeasurement = 0xfd
|
||||
)
|
||||
|
||||
// Device represents a SHT4x sensor
|
||||
type Device struct {
|
||||
bus drivers.I2C
|
||||
Address uint8
|
||||
}
|
||||
|
||||
// New creates a new SHT4x connection. The I2C bus must already be
|
||||
// configured.
|
||||
func New(bus drivers.I2C) Device {
|
||||
return Device{
|
||||
bus: bus,
|
||||
Address: DefaultAddress,
|
||||
}
|
||||
}
|
||||
|
||||
// ReadTemperatureHumidity starts a measurement and then reads out the results. This function blocks
|
||||
// while the measurement is in progress.
|
||||
//
|
||||
// Temperature is returned in [degree Celsius], multiplied by 1000,
|
||||
// and relative humidity in [percent relative humidity], multiplied by 1000.
|
||||
func (d *Device) ReadTemperatureHumidity() (temperatureMilliCelsius int32, relativeHumidityMilliPercent int32, err error) {
|
||||
rawTemp, rawHum, err := d.rawReadings()
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
|
||||
// from the reference driver: https://github.com/Sensirion/embedded-sht/blob/fcc8a523210cc1241a2750899ff6b0f68f3ed212/sht4x/sht4x.c#L81
|
||||
temperatureMilliCelsius = ((21875 * int32(rawTemp)) >> 13) - 45000
|
||||
relativeHumidityMilliPercent = ((15625 * int32(rawHum)) >> 13) - 6000
|
||||
|
||||
return temperatureMilliCelsius, relativeHumidityMilliPercent, err
|
||||
}
|
||||
|
||||
// rawReadings returns the sensor's raw values of the temperature and humidity
|
||||
func (d *Device) rawReadings() (uint16, uint16, error) {
|
||||
err := d.bus.Tx(uint16(d.Address), []byte{commandMeasurement}, nil)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
|
||||
// max time for measurement according to datasheet
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
|
||||
var data [6]byte
|
||||
err = d.bus.Tx(uint16(d.Address), nil, data[:])
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
|
||||
tTicks := readUint(data[0], data[1])
|
||||
rhTicks := readUint(data[3], data[4])
|
||||
|
||||
return tTicks, rhTicks, nil
|
||||
}
|
||||
|
||||
// readUint converts two bytes to uint16
|
||||
func readUint(msb byte, lsb byte) uint16 {
|
||||
return (uint16(msb) << 8) | uint16(lsb)
|
||||
}
|
||||
@@ -63,6 +63,7 @@ tinygo build -size short -o ./build/test.hex -target=microbit ./examples/pcd8544
|
||||
tinygo build -size short -o ./build/test.hex -target=arduino ./examples/servo
|
||||
tinygo build -size short -o ./build/test.hex -target=pybadge ./examples/shifter/main.go
|
||||
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/sht3x/main.go
|
||||
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/sht4x/main.go
|
||||
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/shtc3/main.go
|
||||
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/ssd1306/i2c_128x32/main.go
|
||||
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/ssd1306/spi_128x64/main.go
|
||||
@@ -102,6 +103,7 @@ tinygo build -size short -o ./build/test.hex -target=circuitplay-express ./examp
|
||||
tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/max72xx/main.go
|
||||
tinygo build -size short -o ./build/test.hex -target=feather-m0 ./examples/dht/main.go
|
||||
# tinygo build -size short -o ./build/test.hex -target=arduino ./examples/keypad4x4/main.go
|
||||
tinygo build -size short -o ./build/test.hex -target=feather-rp2040 ./examples/pcf8523/
|
||||
tinygo build -size short -o ./build/test.hex -target=xiao ./examples/pcf8563/alarm/
|
||||
tinygo build -size short -o ./build/test.hex -target=xiao ./examples/pcf8563/clkout/
|
||||
tinygo build -size short -o ./build/test.hex -target=xiao ./examples/pcf8563/time/
|
||||
|
||||
+1
-1
@@ -2,4 +2,4 @@ package drivers
|
||||
|
||||
// Version returns a user-readable string showing the version of the drivers package for support purposes.
|
||||
// Update this value before release of new version of software.
|
||||
const Version = "0.25.0"
|
||||
const Version = "0.26.0"
|
||||
|
||||
Reference in New Issue
Block a user