mirror of
https://github.com/tinygo-org/drivers.git
synced 2026-08-08 00:43:39 +00:00
Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 93372474a2 | |||
| 7dcbfbecc6 | |||
| 2e606b090a | |||
| e0cdc931e7 | |||
| 130d9de03b | |||
| 2c2f1d3db4 | |||
| 2413eb86e0 | |||
| c7555a1469 | |||
| 3fca96e0ef | |||
| c7981f72ec | |||
| 5df157230f | |||
| bcb291992c | |||
| 7b710e3a48 | |||
| dcfd9c066d | |||
| 955b3a56e8 | |||
| 21b8d953f4 | |||
| d1b917b835 |
@@ -1,3 +1,36 @@
|
|||||||
|
0.8.0
|
||||||
|
---
|
||||||
|
- **new devices**
|
||||||
|
- mcp3008: add implementation for MCP3008 ADC with SPI interface
|
||||||
|
- semihosting: initial implementation of ARM semihosting
|
||||||
|
- **enhancements**
|
||||||
|
- espat: refactor response processing for greater speed and efficiency
|
||||||
|
- espat: implement mqtt subscribe functionality via blocking select/channels (experiemental)
|
||||||
|
- **bugfixes**
|
||||||
|
- st7789: fix index out of bounds error
|
||||||
|
- **examples**
|
||||||
|
- Add espat driver example for mqtt subscribe
|
||||||
|
|
||||||
|
0.7.0
|
||||||
|
---
|
||||||
|
- **new devices**
|
||||||
|
- veml6070: add Vishay UV light sensor
|
||||||
|
- **enhancements**
|
||||||
|
- lis3dh: example uses I2C1 so requires config to specify pins since they are not default
|
||||||
|
- ssd1331: make SPI TX faster
|
||||||
|
- st7735: make SPI Tx faster
|
||||||
|
- **docs**
|
||||||
|
- complete missing GoDocs for main and sub-packages
|
||||||
|
- **core**
|
||||||
|
- add Version string for support purposes
|
||||||
|
- **examples**
|
||||||
|
- Change all espat driver examples to use Arduino Nano33 IoT by default
|
||||||
|
|
||||||
|
0.6.0
|
||||||
|
---
|
||||||
|
- **new devices**
|
||||||
|
- Support software SPI for APA102 (Itsy Bitsy M0 on-board "Dotstar" LED as example)
|
||||||
|
|
||||||
0.5.0
|
0.5.0
|
||||||
---
|
---
|
||||||
- **new devices**
|
- **new devices**
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ smoke-test:
|
|||||||
@mkdir -p build
|
@mkdir -p build
|
||||||
tinygo build -size short -o ./build/test.elf -target=itsybitsy-m0 ./examples/adxl345/main.go
|
tinygo build -size short -o ./build/test.elf -target=itsybitsy-m0 ./examples/adxl345/main.go
|
||||||
tinygo build -size short -o ./build/test.elf -target=itsybitsy-m0 ./examples/apa102/main.go
|
tinygo build -size short -o ./build/test.elf -target=itsybitsy-m0 ./examples/apa102/main.go
|
||||||
|
tinygo build -size short -o ./build/test.elf -target=itsybitsy-m0 ./examples/apa102/itsybitsy-m0/main.go
|
||||||
tinygo build -size short -o ./build/test.elf -target=microbit ./examples/at24cx/main.go
|
tinygo build -size short -o ./build/test.elf -target=microbit ./examples/at24cx/main.go
|
||||||
tinygo build -size short -o ./build/test.elf -target=itsybitsy-m0 ./examples/bh1750/main.go
|
tinygo build -size short -o ./build/test.elf -target=itsybitsy-m0 ./examples/bh1750/main.go
|
||||||
tinygo build -size short -o ./build/test.elf -target=itsybitsy-m0 ./examples/blinkm/main.go
|
tinygo build -size short -o ./build/test.elf -target=itsybitsy-m0 ./examples/blinkm/main.go
|
||||||
@@ -19,9 +20,9 @@ smoke-test:
|
|||||||
tinygo build -size short -o ./build/test.elf -target=bluepill ./examples/ds1307/time/main.go
|
tinygo build -size short -o ./build/test.elf -target=bluepill ./examples/ds1307/time/main.go
|
||||||
tinygo build -size short -o ./build/test.elf -target=itsybitsy-m0 ./examples/ds3231/main.go
|
tinygo build -size short -o ./build/test.elf -target=itsybitsy-m0 ./examples/ds3231/main.go
|
||||||
tinygo build -size short -o ./build/test.elf -target=microbit ./examples/easystepper/main.go
|
tinygo build -size short -o ./build/test.elf -target=microbit ./examples/easystepper/main.go
|
||||||
tinygo build -size short -o ./build/test.elf -target=itsybitsy-m0 ./examples/espat/espconsole/main.go
|
tinygo build -size short -o ./build/test.elf -target=arduino-nano33 ./examples/espat/espconsole/main.go
|
||||||
tinygo build -size short -o ./build/test.elf -target=itsybitsy-m0 ./examples/espat/esphub/main.go
|
tinygo build -size short -o ./build/test.elf -target=arduino-nano33 ./examples/espat/esphub/main.go
|
||||||
tinygo build -size short -o ./build/test.elf -target=itsybitsy-m0 ./examples/espat/espstation/main.go
|
tinygo build -size short -o ./build/test.elf -target=arduino-nano33 ./examples/espat/espstation/main.go
|
||||||
tinygo build -size short -o ./build/test.elf -target=feather-m0 ./examples/gps/i2c/main.go
|
tinygo build -size short -o ./build/test.elf -target=feather-m0 ./examples/gps/i2c/main.go
|
||||||
tinygo build -size short -o ./build/test.elf -target=feather-m0 ./examples/gps/uart/main.go
|
tinygo build -size short -o ./build/test.elf -target=feather-m0 ./examples/gps/uart/main.go
|
||||||
tinygo build -size short -o ./build/test.elf -target=microbit ./examples/hd44780/customchar/main.go
|
tinygo build -size short -o ./build/test.elf -target=microbit ./examples/hd44780/customchar/main.go
|
||||||
@@ -30,6 +31,7 @@ smoke-test:
|
|||||||
tinygo build -size short -o ./build/test.elf -target=circuitplay-express ./examples/lis3dh/main.go
|
tinygo build -size short -o ./build/test.elf -target=circuitplay-express ./examples/lis3dh/main.go
|
||||||
tinygo build -size short -o ./build/test.elf -target=arduino-nano33 ./examples/lsm6ds3/main.go
|
tinygo build -size short -o ./build/test.elf -target=arduino-nano33 ./examples/lsm6ds3/main.go
|
||||||
tinygo build -size short -o ./build/test.elf -target=itsybitsy-m0 ./examples/mag3110/main.go
|
tinygo build -size short -o ./build/test.elf -target=itsybitsy-m0 ./examples/mag3110/main.go
|
||||||
|
tinygo build -size short -o ./build/test.elf -target=itsybitsy-m0 ./examples/mcp3008/main.go
|
||||||
tinygo build -size short -o ./build/test.elf -target=microbit ./examples/microbitmatrix/main.go
|
tinygo build -size short -o ./build/test.elf -target=microbit ./examples/microbitmatrix/main.go
|
||||||
tinygo build -size short -o ./build/test.elf -target=itsybitsy-m0 ./examples/mma8653/main.go
|
tinygo build -size short -o ./build/test.elf -target=itsybitsy-m0 ./examples/mma8653/main.go
|
||||||
tinygo build -size short -o ./build/test.elf -target=itsybitsy-m0 ./examples/mpu6050/main.go
|
tinygo build -size short -o ./build/test.elf -target=itsybitsy-m0 ./examples/mpu6050/main.go
|
||||||
@@ -49,5 +51,6 @@ smoke-test:
|
|||||||
tinygo build -size short -o ./build/test.elf -target=trinket-m0 ./examples/bme280/main.go
|
tinygo build -size short -o ./build/test.elf -target=trinket-m0 ./examples/bme280/main.go
|
||||||
tinygo build -size short -o ./build/test.elf -target=circuitplay-express ./examples/microphone/main.go
|
tinygo build -size short -o ./build/test.elf -target=circuitplay-express ./examples/microphone/main.go
|
||||||
tinygo build -size short -o ./build/test.elf -target=circuitplay-express ./examples/buzzer/main.go
|
tinygo build -size short -o ./build/test.elf -target=circuitplay-express ./examples/buzzer/main.go
|
||||||
|
tinygo build -size short -o ./build/test.elf -target=trinket-m0 ./examples/veml6070/main.go
|
||||||
|
|
||||||
test: clean fmt-check smoke-test
|
test: clean fmt-check smoke-test
|
||||||
|
|||||||
@@ -52,6 +52,8 @@ func main() {
|
|||||||
|
|
||||||
## Currently supported devices
|
## Currently supported devices
|
||||||
|
|
||||||
|
The following 34 devices are supported.
|
||||||
|
|
||||||
| Device Name | Interface Type |
|
| Device Name | Interface Type |
|
||||||
|----------|-------------|
|
|----------|-------------|
|
||||||
| [ADXL345 accelerometer](http://www.analog.com/media/en/technical-documentation/data-sheets/ADXL345.pdf) | I2C |
|
| [ADXL345 accelerometer](http://www.analog.com/media/en/technical-documentation/data-sheets/ADXL345.pdf) | I2C |
|
||||||
@@ -71,6 +73,7 @@ func main() {
|
|||||||
| [LIS3DH accelerometer](https://www.st.com/resource/en/datasheet/lis3dh.pdf) | I2C |
|
| [LIS3DH accelerometer](https://www.st.com/resource/en/datasheet/lis3dh.pdf) | I2C |
|
||||||
| [LSM6DS3 accelerometer](https://www.st.com/resource/en/datasheet/lsm6ds3.pdf) | I2C |
|
| [LSM6DS3 accelerometer](https://www.st.com/resource/en/datasheet/lsm6ds3.pdf) | I2C |
|
||||||
| [MAG3110 magnetometer](https://www.nxp.com/docs/en/data-sheet/MAG3110.pdf) | I2C |
|
| [MAG3110 magnetometer](https://www.nxp.com/docs/en/data-sheet/MAG3110.pdf) | I2C |
|
||||||
|
| [MCP3008 analog to digital converter (ADC)](http://ww1.microchip.com/downloads/en/DeviceDoc/21295d.pdf) | SPI |
|
||||||
| [BBC micro:bit LED matrix](https://github.com/bbcmicrobit/hardware/blob/master/SCH_BBC-Microbit_V1.3B.pdf) | GPIO |
|
| [BBC micro:bit LED matrix](https://github.com/bbcmicrobit/hardware/blob/master/SCH_BBC-Microbit_V1.3B.pdf) | GPIO |
|
||||||
| [Microphone - PDM](https://cdn-learn.adafruit.com/assets/assets/000/049/977/original/MP34DT01-M.pdf) | I2S/PDM |
|
| [Microphone - PDM](https://cdn-learn.adafruit.com/assets/assets/000/049/977/original/MP34DT01-M.pdf) | I2S/PDM |
|
||||||
| [MMA8653 accelerometer](https://www.nxp.com/docs/en/data-sheet/MMA8653FC.pdf) | I2C |
|
| [MMA8653 accelerometer](https://www.nxp.com/docs/en/data-sheet/MMA8653FC.pdf) | I2C |
|
||||||
@@ -82,6 +85,7 @@ func main() {
|
|||||||
| [ST7735 TFT color display](https://www.crystalfontz.com/controllers/Sitronix/ST7735R/319/) | SPI |
|
| [ST7735 TFT color display](https://www.crystalfontz.com/controllers/Sitronix/ST7735R/319/) | SPI |
|
||||||
| [ST7789 TFT color display](https://cdn-shop.adafruit.com/product-files/3787/3787_tft_QT154H2201__________20190228182902.pdf) | SPI |
|
| [ST7789 TFT color display](https://cdn-shop.adafruit.com/product-files/3787/3787_tft_QT154H2201__________20190228182902.pdf) | SPI |
|
||||||
| [Thermistor](https://www.farnell.com/datasheets/33552.pdf) | ADC |
|
| [Thermistor](https://www.farnell.com/datasheets/33552.pdf) | ADC |
|
||||||
|
| [VEML6070 UV light sensor](https://www.vishay.com/docs/84277/veml6070.pdf) | I2C |
|
||||||
| [VL53L1X time-of-flight distance sensor](https://www.st.com/resource/en/datasheet/vl53l1x.pdf) | I2C |
|
| [VL53L1X time-of-flight distance sensor](https://www.st.com/resource/en/datasheet/vl53l1x.pdf) | I2C |
|
||||||
| [Waveshare 2.13" e-paper display](https://www.waveshare.com/w/upload/e/e6/2.13inch_e-Paper_Datasheet.pdf) | SPI |
|
| [Waveshare 2.13" e-paper display](https://www.waveshare.com/w/upload/e/e6/2.13inch_e-Paper_Datasheet.pdf) | SPI |
|
||||||
| [Waveshare 2.13" (B & C) e-paper display](https://www.waveshare.com/w/upload/d/d3/2.13inch-e-paper-b-Specification.pdf) | SPI |
|
| [Waveshare 2.13" (B & C) e-paper display](https://www.waveshare.com/w/upload/d/d3/2.13inch-e-paper-b-Specification.pdf) | SPI |
|
||||||
|
|||||||
+15
-2
@@ -21,15 +21,28 @@ const (
|
|||||||
|
|
||||||
// Device wraps APA102 SPI LEDs.
|
// Device wraps APA102 SPI LEDs.
|
||||||
type Device struct {
|
type Device struct {
|
||||||
bus machine.SPI
|
bus SPI
|
||||||
Order int
|
Order int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The SPI interface specifies the minimum functionality that a bus
|
||||||
|
// implementation needs to provide for use by the APA102 driver. Hardware
|
||||||
|
// SPI from the TinyGo "machine" package implements this already.
|
||||||
|
type SPI interface {
|
||||||
|
Tx(w, r []byte) error
|
||||||
|
}
|
||||||
|
|
||||||
// New returns a new APA102 driver. Pass in a fully configured SPI bus.
|
// New returns a new APA102 driver. Pass in a fully configured SPI bus.
|
||||||
func New(b machine.SPI) Device {
|
func New(b SPI) Device {
|
||||||
return Device{bus: b, Order: BGR}
|
return Device{bus: b, Order: BGR}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewSoftwareSPI returns a new APA102 driver that will use a software based
|
||||||
|
// implementation of the SPI protocol.
|
||||||
|
func NewSoftwareSPI(sckPin, mosiPin machine.Pin, delay uint32) Device {
|
||||||
|
return New(&bbSPI{SCK: sckPin, MOSI: mosiPin, Delay: delay})
|
||||||
|
}
|
||||||
|
|
||||||
// WriteColors writes the given RGBA color slice out using the APA102 protocol.
|
// WriteColors writes the given RGBA color slice out using the APA102 protocol.
|
||||||
// The A value (Alpha channel) is used for brightness, set to 0xff (255) for maximum.
|
// The A value (Alpha channel) is used for brightness, set to 0xff (255) for maximum.
|
||||||
func (d Device) WriteColors(cs []color.RGBA) (n int, err error) {
|
func (d Device) WriteColors(cs []color.RGBA) (n int, err error) {
|
||||||
|
|||||||
@@ -0,0 +1,68 @@
|
|||||||
|
package apa102
|
||||||
|
|
||||||
|
import "machine"
|
||||||
|
|
||||||
|
// bbSPI is a dumb bit-bang implementation of SPI protocol that is hardcoded
|
||||||
|
// to mode 0 and ignores trying to receive data. Just enough for the APA102.
|
||||||
|
// Note: making this unexported for now because it is probable not suitable
|
||||||
|
// most purposes other than the APA102 package. It might be desirable to make
|
||||||
|
// this more generic and include it in the TinyGo "machine" package instead.
|
||||||
|
type bbSPI struct {
|
||||||
|
SCK machine.Pin
|
||||||
|
MOSI machine.Pin
|
||||||
|
Delay uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure sets up the SCK and MOSI pins as outputs and sets them low
|
||||||
|
func (s *bbSPI) Configure() {
|
||||||
|
s.SCK.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||||
|
s.MOSI.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||||
|
s.SCK.Low()
|
||||||
|
s.MOSI.Low()
|
||||||
|
if s.Delay == 0 {
|
||||||
|
s.Delay = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tx matches signature of machine.SPI.Tx() and is used to send multiple bytes.
|
||||||
|
// The r slice is ignored and no error will ever be returned.
|
||||||
|
func (s *bbSPI) Tx(w []byte, r []byte) error {
|
||||||
|
s.Configure()
|
||||||
|
for _, b := range w {
|
||||||
|
s.Transfer(b)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// delay represents a quarter of the clock cycle
|
||||||
|
func (s *bbSPI) delay() {
|
||||||
|
for i := uint32(0); i < s.Delay; {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transfer is used to send a single byte.
|
||||||
|
func (s *bbSPI) Transfer(b byte) {
|
||||||
|
for i := uint8(0); i < 8; i++ {
|
||||||
|
|
||||||
|
// half clock cycle high to start
|
||||||
|
s.SCK.High()
|
||||||
|
s.delay()
|
||||||
|
|
||||||
|
// write the value to MOSI (MSB first)
|
||||||
|
if b&(1<<(7-i)) == 0 {
|
||||||
|
s.MOSI.Low()
|
||||||
|
} else {
|
||||||
|
s.MOSI.High()
|
||||||
|
}
|
||||||
|
s.delay()
|
||||||
|
|
||||||
|
// half clock cycle low
|
||||||
|
s.SCK.Low()
|
||||||
|
s.delay()
|
||||||
|
|
||||||
|
// for actual SPI would try to read the MISO value here
|
||||||
|
s.delay()
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
+3
-3
@@ -1,5 +1,5 @@
|
|||||||
// Package drivers provides a collection of hardware drivers for devices that
|
// Package drivers provides a collection of hardware drivers for TinyGo (https://tinygo.org)
|
||||||
// can be used together with TinyGo (https://tinygo.org).
|
// for devices such as sensors and displays.
|
||||||
//
|
//
|
||||||
// Here is an example in TinyGo that uses the BMP180 digital barometer:
|
// Here is an example in TinyGo that uses the BMP180 digital barometer:
|
||||||
//
|
//
|
||||||
@@ -9,7 +9,7 @@
|
|||||||
// "time"
|
// "time"
|
||||||
// "machine"
|
// "machine"
|
||||||
//
|
//
|
||||||
// "github.com/tinygo-org/drivers/bmp180"
|
// "tinygo.org/x/drivers/bmp180"
|
||||||
// )
|
// )
|
||||||
//
|
//
|
||||||
// func main() {
|
// func main() {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Simple driver to rotate a 4-wire stepper motor
|
// Package easystepper provides a simple driver to rotate a 4-wire stepper motor.
|
||||||
package easystepper // import "tinygo.org/x/drivers/easystepper"
|
package easystepper // import "tinygo.org/x/drivers/easystepper"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
+55
-95
@@ -19,6 +19,7 @@
|
|||||||
package espat // import "tinygo.org/x/drivers/espat"
|
package espat // import "tinygo.org/x/drivers/espat"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"machine"
|
"machine"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -54,11 +55,11 @@ func (d *Device) Connected() bool {
|
|||||||
d.Execute(Test)
|
d.Execute(Test)
|
||||||
|
|
||||||
// handle response here, should include "OK"
|
// handle response here, should include "OK"
|
||||||
r := d.Response(100)
|
_, err := d.Response(100)
|
||||||
if strings.Contains(string(r), "OK") {
|
if err != nil {
|
||||||
return true
|
return false
|
||||||
}
|
}
|
||||||
return false
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write raw bytes to the UART.
|
// Write raw bytes to the UART.
|
||||||
@@ -72,7 +73,7 @@ func (d *Device) Read(b []byte) (n int, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// how long in milliseconds to pause after sending AT commands
|
// how long in milliseconds to pause after sending AT commands
|
||||||
const pause = 100
|
const pause = 300
|
||||||
|
|
||||||
// Execute sends an AT command to the ESP8266/ESP32.
|
// Execute sends an AT command to the ESP8266/ESP32.
|
||||||
func (d Device) Execute(cmd string) error {
|
func (d Device) Execute(cmd string) error {
|
||||||
@@ -97,7 +98,11 @@ func (d Device) Set(cmd, params string) error {
|
|||||||
// Version returns the ESP8266/ESP32 firmware version info.
|
// Version returns the ESP8266/ESP32 firmware version info.
|
||||||
func (d Device) Version() []byte {
|
func (d Device) Version() []byte {
|
||||||
d.Execute(Version)
|
d.Execute(Version)
|
||||||
return d.Response(100)
|
r, err := d.Response(100)
|
||||||
|
if err != nil {
|
||||||
|
return []byte("unknown")
|
||||||
|
}
|
||||||
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
// Echo sets the ESP8266/ESP32 echo setting.
|
// Echo sets the ESP8266/ESP32 echo setting.
|
||||||
@@ -122,7 +127,7 @@ func (d Device) Reset() {
|
|||||||
// ReadSocket returns the data that has already been read in from the responses.
|
// ReadSocket returns the data that has already been read in from the responses.
|
||||||
func (d *Device) ReadSocket(b []byte) (n int, err error) {
|
func (d *Device) ReadSocket(b []byte) (n int, err error) {
|
||||||
// make sure no data in buffer
|
// make sure no data in buffer
|
||||||
d.Response(100)
|
d.Response(300)
|
||||||
|
|
||||||
count := len(b)
|
count := len(b)
|
||||||
if len(b) >= len(d.socketdata) {
|
if len(b) >= len(d.socketdata) {
|
||||||
@@ -142,118 +147,73 @@ func (d *Device) ReadSocket(b []byte) (n int, err error) {
|
|||||||
|
|
||||||
// Response gets the next response bytes from the ESP8266/ESP32.
|
// Response gets the next response bytes from the ESP8266/ESP32.
|
||||||
// The call will retry for up to timeout milliseconds before returning nothing.
|
// The call will retry for up to timeout milliseconds before returning nothing.
|
||||||
func (d *Device) Response(timeout int) []byte {
|
func (d *Device) Response(timeout int) ([]byte, error) {
|
||||||
var i int
|
// read data
|
||||||
pause := 10 // pause to wait for 10 ms
|
var size int
|
||||||
|
var start, end int
|
||||||
|
pause := 100 // pause to wait for 100 ms
|
||||||
retries := timeout / pause
|
retries := timeout / pause
|
||||||
|
|
||||||
header := make([]byte, 2)
|
|
||||||
for {
|
for {
|
||||||
for d.bus.Buffered() > 0 {
|
size = d.bus.Buffered()
|
||||||
// get the first 2 bytes
|
|
||||||
header[0], _ = d.bus.ReadByte()
|
|
||||||
header[1], _ = d.bus.ReadByte()
|
|
||||||
|
|
||||||
if d.isLeadingCRLF(header) {
|
if size > 0 {
|
||||||
// skip it
|
end += size
|
||||||
header[0], _ = d.bus.ReadByte()
|
d.bus.Read(d.response[start:end])
|
||||||
header[1], _ = d.bus.ReadByte()
|
|
||||||
|
// if "+IPD" then read socket data
|
||||||
|
if strings.Contains(string(d.response[:end]), "+IPD") {
|
||||||
|
// handle socket data
|
||||||
|
return nil, d.parseIPD(end)
|
||||||
}
|
}
|
||||||
|
|
||||||
if d.isIPD(header) {
|
// if "OK" then the command worked
|
||||||
// is socket data packet
|
if strings.Contains(string(d.response[:end]), "OK") {
|
||||||
d.parseIPD()
|
return d.response[start:end], nil
|
||||||
} else {
|
|
||||||
// no, so put into response
|
|
||||||
d.response[i] = header[0]
|
|
||||||
i++
|
|
||||||
d.response[i] = header[1]
|
|
||||||
i++
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// read the rest of normal command response
|
// if "Error" then the command failed
|
||||||
for d.bus.Buffered() > 0 {
|
if strings.Contains(string(d.response[:end]), "ERROR") {
|
||||||
data, err := d.bus.ReadByte()
|
return d.response[start:end], errors.New("response error:" + string(d.response[start:end]))
|
||||||
if err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
d.response[i] = data
|
|
||||||
i++
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if anything else, then keep reading data in?
|
||||||
|
start = end
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// wait longer?
|
||||||
retries--
|
retries--
|
||||||
if retries == 0 {
|
if retries == 0 {
|
||||||
break
|
return nil, errors.New("response timeout error:" + string(d.response[start:end]))
|
||||||
}
|
}
|
||||||
|
|
||||||
// pause to make sure is no more data to be read
|
|
||||||
time.Sleep(time.Duration(pause) * time.Millisecond)
|
time.Sleep(time.Duration(pause) * time.Millisecond)
|
||||||
}
|
}
|
||||||
return d.response[:i]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Device) isLeadingCRLF(b []byte) bool {
|
func (d *Device) parseIPD(end int) error {
|
||||||
if len(b) < 2 {
|
// find the "+IPD," to get length
|
||||||
return false
|
s := strings.Index(string(d.response[:end]), "+IPD,")
|
||||||
}
|
|
||||||
if b[0] == 13 && b[1] == 10 {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *Device) isIPD(b []byte) bool {
|
// find the ":"
|
||||||
if len(b) < 2 {
|
e := strings.Index(string(d.response[:end]), ":")
|
||||||
return false
|
|
||||||
}
|
|
||||||
if b[0] == '+' && b[1] == 'I' {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *Device) parseIPD() bool {
|
// find the data length
|
||||||
data, _ := d.bus.ReadByte()
|
val := string(d.response[s+5 : e])
|
||||||
if data != 'P' {
|
|
||||||
// error
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
data, _ = d.bus.ReadByte()
|
|
||||||
if data != 'D' {
|
|
||||||
// error
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
data, _ = d.bus.ReadByte()
|
|
||||||
if data != ',' {
|
|
||||||
// error
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// get the expected data length
|
// TODO: verify count
|
||||||
// skip remaining header up to the ":"
|
_, err := strconv.Atoi(val)
|
||||||
buf := []byte{}
|
|
||||||
data, _ = d.bus.ReadByte()
|
|
||||||
for data != ':' {
|
|
||||||
// put into the buffer with int value here
|
|
||||||
buf = append(buf, data)
|
|
||||||
|
|
||||||
// read next value
|
|
||||||
data, _ = d.bus.ReadByte()
|
|
||||||
}
|
|
||||||
|
|
||||||
val := string(buf)
|
|
||||||
count, err := strconv.Atoi(val)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// not expected data here. what to do?
|
// not expected data here. what to do?
|
||||||
return false
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// load up the socket data
|
// load up the socket data
|
||||||
// only read the expected amount of data
|
d.socketdata = append(d.socketdata, d.response[e+1:end]...)
|
||||||
for m := 0; m < count; m++ {
|
return nil
|
||||||
data, _ = d.bus.ReadByte()
|
}
|
||||||
d.socketdata = append(d.socketdata, data)
|
|
||||||
}
|
// IsSocketDataAvailable returns of there is socket data available
|
||||||
|
func (d *Device) IsSocketDataAvailable() bool {
|
||||||
return true
|
return len(d.socketdata) > 0 || d.bus.Buffered() > 0
|
||||||
}
|
}
|
||||||
|
|||||||
+146
-30
@@ -1,3 +1,5 @@
|
|||||||
|
// Package mqtt is intended to provide compatible interfaces with the
|
||||||
|
// Paho mqtt library.
|
||||||
package mqtt
|
package mqtt
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -17,15 +19,21 @@ import (
|
|||||||
// connection) are created before the application is actually ready.
|
// connection) are created before the application is actually ready.
|
||||||
func NewClient(o *ClientOptions) Client {
|
func NewClient(o *ClientOptions) Client {
|
||||||
c := &mqttclient{opts: o, adaptor: o.Adaptor}
|
c := &mqttclient{opts: o, adaptor: o.Adaptor}
|
||||||
|
c.msgRouter, c.stopRouter = newRouter()
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
type mqttclient struct {
|
type mqttclient struct {
|
||||||
adaptor *espat.Device
|
adaptor *espat.Device
|
||||||
conn net.Conn
|
conn net.Conn
|
||||||
connected bool
|
connected bool
|
||||||
opts *ClientOptions
|
opts *ClientOptions
|
||||||
mid uint16
|
mid uint16
|
||||||
|
inbound chan packets.ControlPacket
|
||||||
|
stop chan struct{}
|
||||||
|
msgRouter *router
|
||||||
|
stopRouter chan bool
|
||||||
|
incomingPubChan chan *packets.PublishPacket
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddRoute allows you to add a handler for messages on a specific topic
|
// AddRoute allows you to add a handler for messages on a specific topic
|
||||||
@@ -69,6 +77,12 @@ func (c *mqttclient) Connect() Token {
|
|||||||
return &mqtttoken{err: errors.New("invalid protocol")}
|
return &mqtttoken{err: errors.New("invalid protocol")}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
c.mid = 1
|
||||||
|
c.inbound = make(chan packets.ControlPacket)
|
||||||
|
c.stop = make(chan struct{})
|
||||||
|
c.incomingPubChan = make(chan *packets.PublishPacket)
|
||||||
|
c.msgRouter.matchAndDispatch(c.incomingPubChan, c.opts.Order, c)
|
||||||
|
|
||||||
// send the MQTT connect message
|
// send the MQTT connect message
|
||||||
connectPkt := packets.NewControlPacket(packets.Connect).(*packets.ConnectPacket)
|
connectPkt := packets.NewControlPacket(packets.Connect).(*packets.ConnectPacket)
|
||||||
connectPkt.Qos = 0
|
connectPkt.Qos = 0
|
||||||
@@ -82,7 +96,7 @@ func (c *mqttclient) Connect() Token {
|
|||||||
connectPkt.PasswordFlag = true
|
connectPkt.PasswordFlag = true
|
||||||
}
|
}
|
||||||
|
|
||||||
connectPkt.ClientIdentifier = c.opts.ClientID //"tinygo-client-" + randomString(10)
|
connectPkt.ClientIdentifier = c.opts.ClientID
|
||||||
connectPkt.ProtocolVersion = byte(c.opts.ProtocolVersion)
|
connectPkt.ProtocolVersion = byte(c.opts.ProtocolVersion)
|
||||||
connectPkt.ProtocolName = "MQTT"
|
connectPkt.ProtocolName = "MQTT"
|
||||||
connectPkt.Keepalive = 30
|
connectPkt.Keepalive = 30
|
||||||
@@ -92,26 +106,25 @@ func (c *mqttclient) Connect() Token {
|
|||||||
return &mqtttoken{err: err}
|
return &mqtttoken{err: err}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: handle timeout
|
// TODO: handle timeout as ReadPacket blocks until it gets a packet.
|
||||||
for {
|
// CONNECT response.
|
||||||
packet, _ := packets.ReadPacket(c.conn)
|
packet, err := packets.ReadPacket(c.conn)
|
||||||
|
if err != nil {
|
||||||
if packet != nil {
|
return &mqtttoken{err: err}
|
||||||
ack, ok := packet.(*packets.ConnackPacket)
|
}
|
||||||
if ok {
|
if packet != nil {
|
||||||
if ack.ReturnCode == 0 {
|
ack, ok := packet.(*packets.ConnackPacket)
|
||||||
// success
|
if ok {
|
||||||
return &mqtttoken{}
|
if ack.ReturnCode != 0 {
|
||||||
}
|
|
||||||
// otherwise something went wrong
|
|
||||||
return &mqtttoken{err: errors.New(packet.String())}
|
return &mqtttoken{err: errors.New(packet.String())}
|
||||||
}
|
}
|
||||||
|
c.connected = true
|
||||||
}
|
}
|
||||||
|
|
||||||
time.Sleep(100 * time.Millisecond)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
c.connected = true
|
go readMessages(c)
|
||||||
|
go processInbound(c)
|
||||||
|
|
||||||
return &mqtttoken{}
|
return &mqtttoken{}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,6 +140,10 @@ func (c *mqttclient) Disconnect(quiesce uint) {
|
|||||||
// to the specified topic.
|
// to the specified topic.
|
||||||
// Returns a token to track delivery of the message to the broker
|
// Returns a token to track delivery of the message to the broker
|
||||||
func (c *mqttclient) Publish(topic string, qos byte, retained bool, payload interface{}) Token {
|
func (c *mqttclient) Publish(topic string, qos byte, retained bool, payload interface{}) Token {
|
||||||
|
if !c.IsConnected() {
|
||||||
|
return &mqtttoken{err: errors.New("MQTT client not connected")}
|
||||||
|
}
|
||||||
|
|
||||||
pub := packets.NewControlPacket(packets.Publish).(*packets.PublishPacket)
|
pub := packets.NewControlPacket(packets.Publish).(*packets.PublishPacket)
|
||||||
pub.Qos = qos
|
pub.Qos = qos
|
||||||
pub.TopicName = topic
|
pub.TopicName = topic
|
||||||
@@ -142,12 +159,37 @@ func (c *mqttclient) Publish(topic string, qos byte, retained bool, payload inte
|
|||||||
c.mid++
|
c.mid++
|
||||||
|
|
||||||
err := pub.Write(c.conn)
|
err := pub.Write(c.conn)
|
||||||
return &mqtttoken{err: err}
|
if err != nil {
|
||||||
|
return &mqtttoken{err: err}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &mqtttoken{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Subscribe starts a new subscription. Provide a MessageHandler to be executed when
|
// Subscribe starts a new subscription. Provide a MessageHandler to be executed when
|
||||||
// a message is published on the topic provided.
|
// a message is published on the topic provided.
|
||||||
func (c *mqttclient) Subscribe(topic string, qos byte, callback MessageHandler) Token {
|
func (c *mqttclient) Subscribe(topic string, qos byte, callback MessageHandler) Token {
|
||||||
|
if !c.IsConnected() {
|
||||||
|
return &mqtttoken{err: errors.New("MQTT client not connected")}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub := packets.NewControlPacket(packets.Subscribe).(*packets.SubscribePacket)
|
||||||
|
sub.Topics = append(sub.Topics, topic)
|
||||||
|
sub.Qoss = append(sub.Qoss, qos)
|
||||||
|
|
||||||
|
if callback != nil {
|
||||||
|
c.msgRouter.addRoute(topic, callback)
|
||||||
|
}
|
||||||
|
|
||||||
|
sub.MessageID = c.mid
|
||||||
|
c.mid++
|
||||||
|
|
||||||
|
// drop in the channel to send
|
||||||
|
err := sub.Write(c.conn)
|
||||||
|
if err != nil {
|
||||||
|
return &mqtttoken{err: err}
|
||||||
|
}
|
||||||
|
|
||||||
return &mqtttoken{}
|
return &mqtttoken{}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -171,18 +213,92 @@ func (c *mqttclient) OptionsReader() ClientOptionsReader {
|
|||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
type mqtttoken struct {
|
func processInbound(c *mqttclient) {
|
||||||
err error
|
for {
|
||||||
|
select {
|
||||||
|
case msg := <-c.inbound:
|
||||||
|
switch m := msg.(type) {
|
||||||
|
case *packets.PingrespPacket:
|
||||||
|
// TODO: handle this
|
||||||
|
case *packets.SubackPacket:
|
||||||
|
// TODO: handle this
|
||||||
|
case *packets.UnsubackPacket:
|
||||||
|
// TODO: handle this
|
||||||
|
case *packets.PublishPacket:
|
||||||
|
// TODO: handle Qos
|
||||||
|
c.incomingPubChan <- m
|
||||||
|
case *packets.PubackPacket:
|
||||||
|
// TODO: handle this
|
||||||
|
case *packets.PubrecPacket:
|
||||||
|
// TODO: handle this
|
||||||
|
case *packets.PubrelPacket:
|
||||||
|
// TODO: handle this
|
||||||
|
case *packets.PubcompPacket:
|
||||||
|
// TODO: handle this
|
||||||
|
}
|
||||||
|
case <-c.stop:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *mqtttoken) Wait() bool {
|
// readMessages reads incoming messages off the wire.
|
||||||
return true
|
// incoming messages are then send into inbound channel.
|
||||||
|
func readMessages(c *mqttclient) {
|
||||||
|
var err error
|
||||||
|
var cp packets.ControlPacket
|
||||||
|
|
||||||
|
PROCESS:
|
||||||
|
for {
|
||||||
|
if cp, err = c.ReadPacket(); err != nil {
|
||||||
|
break PROCESS
|
||||||
|
}
|
||||||
|
if cp != nil {
|
||||||
|
c.inbound <- cp
|
||||||
|
// TODO: Notify keepalive logic that we recently received a packet
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: handle if we received an error on read.
|
||||||
|
// If disconnect is in progress, swallow error and return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *mqtttoken) WaitTimeout(time.Duration) bool {
|
func (c *mqttclient) ackFunc(packet *packets.PublishPacket) func() {
|
||||||
return true
|
return func() {
|
||||||
|
switch packet.Qos {
|
||||||
|
case 2:
|
||||||
|
// pr := packets.NewControlPacket(packets.Pubrec).(*packets.PubrecPacket)
|
||||||
|
// pr.MessageID = packet.MessageID
|
||||||
|
// DEBUG.Println(NET, "putting pubrec msg on obound")
|
||||||
|
// select {
|
||||||
|
// case c.oboundP <- &PacketAndToken{p: pr, t: nil}:
|
||||||
|
// case <-c.stop:
|
||||||
|
// }
|
||||||
|
// DEBUG.Println(NET, "done putting pubrec msg on obound")
|
||||||
|
case 1:
|
||||||
|
// pa := packets.NewControlPacket(packets.Puback).(*packets.PubackPacket)
|
||||||
|
// pa.MessageID = packet.MessageID
|
||||||
|
// DEBUG.Println(NET, "putting puback msg on obound")
|
||||||
|
// persistOutbound(c.persist, pa)
|
||||||
|
// select {
|
||||||
|
// case c.oboundP <- &PacketAndToken{p: pa, t: nil}:
|
||||||
|
// case <-c.stop:
|
||||||
|
// }
|
||||||
|
// DEBUG.Println(NET, "done putting puback msg on obound")
|
||||||
|
case 0:
|
||||||
|
// do nothing, since there is no need to send an ack packet back
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *mqtttoken) Error() error {
|
// ReadPacket tries to read the next incoming packet from the MQTT broker.
|
||||||
return t.err
|
// If there is no data yet but also is no error, it returns nil for both values.
|
||||||
|
func (c *mqttclient) ReadPacket() (packets.ControlPacket, error) {
|
||||||
|
// check for data first...
|
||||||
|
if espat.ActiveDevice.IsSocketDataAvailable() {
|
||||||
|
return packets.ReadPacket(c.conn)
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/eclipse/paho.mqtt.golang/packets"
|
||||||
"tinygo.org/x/drivers/espat"
|
"tinygo.org/x/drivers/espat"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -155,6 +156,18 @@ func (m *message) Ack() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func messageFromPublish(p *packets.PublishPacket, ack func()) Message {
|
||||||
|
return &message{
|
||||||
|
duplicate: p.Dup,
|
||||||
|
qos: p.Qos,
|
||||||
|
retained: p.Retain,
|
||||||
|
topic: p.TopicName,
|
||||||
|
messageID: p.MessageID,
|
||||||
|
payload: p.Payload,
|
||||||
|
ack: ack,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ClientOptionsReader provides an interface for reading ClientOptions after the client has been initialized.
|
// ClientOptionsReader provides an interface for reading ClientOptions after the client has been initialized.
|
||||||
type ClientOptionsReader struct {
|
type ClientOptionsReader struct {
|
||||||
options *ClientOptions
|
options *ClientOptions
|
||||||
|
|||||||
@@ -0,0 +1,182 @@
|
|||||||
|
// The following code is a slightly modified version of code taken from the Paho MQTT library.
|
||||||
|
// It is here until TinyGo can compile the "net" package from the standard library, at which time
|
||||||
|
// it can be removed.
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2013 IBM Corp.
|
||||||
|
*
|
||||||
|
* All rights reserved. This program and the accompanying materials
|
||||||
|
* are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
* which accompanies this distribution, and is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
*
|
||||||
|
* Contributors:
|
||||||
|
* Seth Hoenig
|
||||||
|
* Allan Stockdill-Mander
|
||||||
|
* Mike Robertson
|
||||||
|
*/
|
||||||
|
|
||||||
|
package mqtt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"container/list"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/eclipse/paho.mqtt.golang/packets"
|
||||||
|
)
|
||||||
|
|
||||||
|
// route is a type which associates MQTT Topic strings with a
|
||||||
|
// callback to be executed upon the arrival of a message associated
|
||||||
|
// with a subscription to that topic.
|
||||||
|
type route struct {
|
||||||
|
topic string
|
||||||
|
callback MessageHandler
|
||||||
|
}
|
||||||
|
|
||||||
|
// match takes a slice of strings which represent the route being tested having been split on '/'
|
||||||
|
// separators, and a slice of strings representing the topic string in the published message, similarly
|
||||||
|
// split.
|
||||||
|
// The function determines if the topic string matches the route according to the MQTT topic rules
|
||||||
|
// and returns a boolean of the outcome
|
||||||
|
func match(route []string, topic []string) bool {
|
||||||
|
if len(route) == 0 {
|
||||||
|
if len(topic) == 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(topic) == 0 {
|
||||||
|
if route[0] == "#" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if route[0] == "#" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (route[0] == "+") || (route[0] == topic[0]) {
|
||||||
|
return match(route[1:], topic[1:])
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func routeIncludesTopic(route, topic string) bool {
|
||||||
|
return match(routeSplit(route), strings.Split(topic, "/"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// removes $share and sharename when splitting the route to allow
|
||||||
|
// shared subscription routes to correctly match the topic
|
||||||
|
func routeSplit(route string) []string {
|
||||||
|
var result []string
|
||||||
|
if strings.HasPrefix(route, "$share") {
|
||||||
|
result = strings.Split(route, "/")[2:]
|
||||||
|
} else {
|
||||||
|
result = strings.Split(route, "/")
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// match takes the topic string of the published message and does a basic compare to the
|
||||||
|
// string of the current Route, if they match it returns true
|
||||||
|
func (r *route) match(topic string) bool {
|
||||||
|
return r.topic == topic || routeIncludesTopic(r.topic, topic)
|
||||||
|
}
|
||||||
|
|
||||||
|
type router struct {
|
||||||
|
//sync.RWMutex
|
||||||
|
routes *list.List
|
||||||
|
defaultHandler MessageHandler
|
||||||
|
messages chan *packets.PublishPacket
|
||||||
|
stop chan bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// newRouter returns a new instance of a Router and channel which can be used to tell the Router
|
||||||
|
// to stop
|
||||||
|
func newRouter() (*router, chan bool) {
|
||||||
|
router := &router{routes: list.New(), messages: make(chan *packets.PublishPacket), stop: make(chan bool)}
|
||||||
|
stop := router.stop
|
||||||
|
return router, stop
|
||||||
|
}
|
||||||
|
|
||||||
|
// addRoute takes a topic string and MessageHandler callback. It looks in the current list of
|
||||||
|
// routes to see if there is already a matching Route. If there is it replaces the current
|
||||||
|
// callback with the new one. If not it add a new entry to the list of Routes.
|
||||||
|
func (r *router) addRoute(topic string, callback MessageHandler) {
|
||||||
|
for e := r.routes.Front(); e != nil; e = e.Next() {
|
||||||
|
if e.Value.(*route).match(topic) {
|
||||||
|
r := e.Value.(*route)
|
||||||
|
r.callback = callback
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
r.routes.PushBack(&route{topic: topic, callback: callback})
|
||||||
|
}
|
||||||
|
|
||||||
|
// deleteRoute takes a route string, looks for a matching Route in the list of Routes. If
|
||||||
|
// found it removes the Route from the list.
|
||||||
|
func (r *router) deleteRoute(topic string) {
|
||||||
|
for e := r.routes.Front(); e != nil; e = e.Next() {
|
||||||
|
if e.Value.(*route).match(topic) {
|
||||||
|
r.routes.Remove(e)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// setDefaultHandler assigns a default callback that will be called if no matching Route
|
||||||
|
// is found for an incoming Publish.
|
||||||
|
func (r *router) setDefaultHandler(handler MessageHandler) {
|
||||||
|
r.defaultHandler = handler
|
||||||
|
}
|
||||||
|
|
||||||
|
// matchAndDispatch takes a channel of Message pointers as input and starts a go routine that
|
||||||
|
// takes messages off the channel, matches them against the internal route list and calls the
|
||||||
|
// associated callback (or the defaultHandler, if one exists and no other route matched). If
|
||||||
|
// anything is sent down the stop channel the function will end.
|
||||||
|
func (r *router) matchAndDispatch(messages <-chan *packets.PublishPacket, order bool, client *mqttclient) {
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case message := <-messages:
|
||||||
|
sent := false
|
||||||
|
m := messageFromPublish(message, client.ackFunc(message))
|
||||||
|
handlers := []MessageHandler{}
|
||||||
|
for e := r.routes.Front(); e != nil; e = e.Next() {
|
||||||
|
if e.Value.(*route).match(message.TopicName) {
|
||||||
|
if order {
|
||||||
|
handlers = append(handlers, e.Value.(*route).callback)
|
||||||
|
} else {
|
||||||
|
hd := e.Value.(*route).callback
|
||||||
|
go func() {
|
||||||
|
hd(client, m)
|
||||||
|
//TODO: m.Ack()
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
sent = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !sent && r.defaultHandler != nil {
|
||||||
|
if order {
|
||||||
|
handlers = append(handlers, r.defaultHandler)
|
||||||
|
} else {
|
||||||
|
go func() {
|
||||||
|
r.defaultHandler(client, m)
|
||||||
|
//TODO: m.Ack()
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, handler := range handlers {
|
||||||
|
func() {
|
||||||
|
handler(client, m)
|
||||||
|
//TODO: m.Ack()
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
case <-r.stop:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package mqtt
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
type mqtttoken struct {
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *mqtttoken) Wait() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *mqtttoken) WaitTimeout(time.Duration) bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *mqtttoken) Error() error {
|
||||||
|
return t.err
|
||||||
|
}
|
||||||
+27
-7
@@ -23,7 +23,10 @@ func DialUDP(network string, laddr, raddr *UDPAddr) (*UDPSerialConn, error) {
|
|||||||
espat.ActiveDevice.DisconnectSocket()
|
espat.ActiveDevice.DisconnectSocket()
|
||||||
|
|
||||||
// connect new socket
|
// connect new socket
|
||||||
espat.ActiveDevice.ConnectUDPSocket(addr, sendport, listenport)
|
err := espat.ActiveDevice.ConnectUDPSocket(addr, sendport, listenport)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
return &UDPSerialConn{SerialConn: SerialConn{Adaptor: espat.ActiveDevice}, laddr: laddr, raddr: raddr}, nil
|
return &UDPSerialConn{SerialConn: SerialConn{Adaptor: espat.ActiveDevice}, laddr: laddr, raddr: raddr}, nil
|
||||||
}
|
}
|
||||||
@@ -38,7 +41,10 @@ func ListenUDP(network string, laddr *UDPAddr) (*UDPSerialConn, error) {
|
|||||||
espat.ActiveDevice.DisconnectSocket()
|
espat.ActiveDevice.DisconnectSocket()
|
||||||
|
|
||||||
// connect new socket
|
// connect new socket
|
||||||
espat.ActiveDevice.ConnectUDPSocket(addr, sendport, listenport)
|
err := espat.ActiveDevice.ConnectUDPSocket(addr, sendport, listenport)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
return &UDPSerialConn{SerialConn: SerialConn{Adaptor: espat.ActiveDevice}, laddr: laddr}, nil
|
return &UDPSerialConn{SerialConn: SerialConn{Adaptor: espat.ActiveDevice}, laddr: laddr}, nil
|
||||||
}
|
}
|
||||||
@@ -50,11 +56,14 @@ func DialTCP(network string, laddr, raddr *TCPAddr) (*TCPSerialConn, error) {
|
|||||||
addr := raddr.IP.String()
|
addr := raddr.IP.String()
|
||||||
sendport := strconv.Itoa(raddr.Port)
|
sendport := strconv.Itoa(raddr.Port)
|
||||||
|
|
||||||
// disconnect any old socket
|
// disconnect any old socket?
|
||||||
espat.ActiveDevice.DisconnectSocket()
|
//espat.ActiveDevice.DisconnectSocket()
|
||||||
|
|
||||||
// connect new socket
|
// connect new socket
|
||||||
espat.ActiveDevice.ConnectTCPSocket(addr, sendport)
|
err := espat.ActiveDevice.ConnectTCPSocket(addr, sendport)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
return &TCPSerialConn{SerialConn: SerialConn{Adaptor: espat.ActiveDevice}, laddr: laddr, raddr: raddr}, nil
|
return &TCPSerialConn{SerialConn: SerialConn{Adaptor: espat.ActiveDevice}, laddr: laddr, raddr: raddr}, nil
|
||||||
}
|
}
|
||||||
@@ -132,8 +141,19 @@ func (c *SerialConn) Read(b []byte) (n int, err error) {
|
|||||||
func (c *SerialConn) Write(b []byte) (n int, err error) {
|
func (c *SerialConn) Write(b []byte) (n int, err error) {
|
||||||
// specify that is a data transfer to the
|
// specify that is a data transfer to the
|
||||||
// currently open socket, not commands to the ESP8266/ESP32.
|
// currently open socket, not commands to the ESP8266/ESP32.
|
||||||
c.Adaptor.StartSocketSend(len(b))
|
err = c.Adaptor.StartSocketSend(len(b))
|
||||||
return c.Adaptor.Write(b)
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
n, err = c.Adaptor.Write(b)
|
||||||
|
if err != nil {
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
_, err = c.Adaptor.Response(1000)
|
||||||
|
if err != nil {
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
return n, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close closes the connection.
|
// Close closes the connection.
|
||||||
|
|||||||
+48
-26
@@ -17,7 +17,14 @@ const (
|
|||||||
// GetDNS returns the IP address for a domain name.
|
// GetDNS returns the IP address for a domain name.
|
||||||
func (d *Device) GetDNS(domain string) (string, error) {
|
func (d *Device) GetDNS(domain string) (string, error) {
|
||||||
d.Set(TCPDNSLookup, "\""+domain+"\"")
|
d.Set(TCPDNSLookup, "\""+domain+"\"")
|
||||||
r := strings.Split(string(d.Response(1000)), ":")
|
resp, err := d.Response(1000)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if !strings.Contains(string(resp), ":") {
|
||||||
|
return "", errors.New("GetDNS error:" + string(resp))
|
||||||
|
}
|
||||||
|
r := strings.Split(string(resp), ":")
|
||||||
if len(r) != 2 {
|
if len(r) != 2 {
|
||||||
return "", errors.New("Invalid domain lookup result")
|
return "", errors.New("Invalid domain lookup result")
|
||||||
}
|
}
|
||||||
@@ -30,24 +37,30 @@ func (d *Device) GetDNS(domain string) (string, error) {
|
|||||||
func (d *Device) ConnectTCPSocket(addr, port string) error {
|
func (d *Device) ConnectTCPSocket(addr, port string) error {
|
||||||
protocol := "TCP"
|
protocol := "TCP"
|
||||||
val := "\"" + protocol + "\",\"" + addr + "\"," + port + ",120"
|
val := "\"" + protocol + "\",\"" + addr + "\"," + port + ",120"
|
||||||
d.Set(TCPConnect, val)
|
err := d.Set(TCPConnect, val)
|
||||||
r := d.Response(1000)
|
if err != nil {
|
||||||
if strings.Contains(string(r), "OK") {
|
return err
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
return errors.New("ConnectTCPSocket error:" + string(r))
|
_, e := d.Response(3000)
|
||||||
|
if e != nil {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ConnectUDPSocket creates a new UDP connection for the ESP8266/ESP32.
|
// ConnectUDPSocket creates a new UDP connection for the ESP8266/ESP32.
|
||||||
func (d *Device) ConnectUDPSocket(addr, sendport, listenport string) error {
|
func (d *Device) ConnectUDPSocket(addr, sendport, listenport string) error {
|
||||||
protocol := "UDP"
|
protocol := "UDP"
|
||||||
val := "\"" + protocol + "\",\"" + addr + "\"," + sendport + "," + listenport + ",2"
|
val := "\"" + protocol + "\",\"" + addr + "\"," + sendport + "," + listenport + ",2"
|
||||||
d.Set(TCPConnect, val)
|
err := d.Set(TCPConnect, val)
|
||||||
r := d.Response(pause)
|
if err != nil {
|
||||||
if strings.Contains(string(r), "OK") {
|
return err
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
return errors.New("ConnectUDPSocket error:" + string(r))
|
_, e := d.Response(3000)
|
||||||
|
if e != nil {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ConnectSSLSocket creates a new SSL socket connection for the ESP8266/ESP32.
|
// ConnectSSLSocket creates a new SSL socket connection for the ESP8266/ESP32.
|
||||||
@@ -57,17 +70,23 @@ func (d *Device) ConnectSSLSocket(addr, port string) error {
|
|||||||
val := "\"" + protocol + "\",\"" + addr + "\"," + port + ",120"
|
val := "\"" + protocol + "\",\"" + addr + "\"," + port + ",120"
|
||||||
d.Set(TCPConnect, val)
|
d.Set(TCPConnect, val)
|
||||||
// this operation takes longer, so wait up to 6 seconds to complete.
|
// this operation takes longer, so wait up to 6 seconds to complete.
|
||||||
r := d.Response(6000)
|
_, err := d.Response(6000)
|
||||||
if strings.Contains(string(r), "CONNECT") {
|
if err != nil {
|
||||||
return nil
|
return err
|
||||||
}
|
}
|
||||||
return errors.New("ConnectSSLSocket error:" + string(r))
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// DisconnectSocket disconnects the ESP8266/ESP32 from the current TCP/UDP connection.
|
// DisconnectSocket disconnects the ESP8266/ESP32 from the current TCP/UDP connection.
|
||||||
func (d *Device) DisconnectSocket() error {
|
func (d *Device) DisconnectSocket() error {
|
||||||
d.Execute(TCPClose)
|
err := d.Execute(TCPClose)
|
||||||
d.Response(pause)
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, e := d.Response(pause)
|
||||||
|
if e != nil {
|
||||||
|
return e
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,14 +95,14 @@ func (d *Device) DisconnectSocket() error {
|
|||||||
func (d *Device) SetMux(mode int) error {
|
func (d *Device) SetMux(mode int) error {
|
||||||
val := strconv.Itoa(mode)
|
val := strconv.Itoa(mode)
|
||||||
d.Set(TCPMultiple, val)
|
d.Set(TCPMultiple, val)
|
||||||
d.Response(pause)
|
_, err := d.Response(pause)
|
||||||
return nil
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetMux returns the ESP8266/ESP32 current client TCP/UDP configuration for concurrent connections.
|
// GetMux returns the ESP8266/ESP32 current client TCP/UDP configuration for concurrent connections.
|
||||||
func (d *Device) GetMux() ([]byte, error) {
|
func (d *Device) GetMux() ([]byte, error) {
|
||||||
d.Query(TCPMultiple)
|
d.Query(TCPMultiple)
|
||||||
return d.Response(pause), nil
|
return d.Response(pause)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetTCPTransferMode sets the ESP8266/ESP32 current client TCP/UDP transfer mode.
|
// SetTCPTransferMode sets the ESP8266/ESP32 current client TCP/UDP transfer mode.
|
||||||
@@ -91,12 +110,12 @@ func (d *Device) GetMux() ([]byte, error) {
|
|||||||
func (d *Device) SetTCPTransferMode(mode int) error {
|
func (d *Device) SetTCPTransferMode(mode int) error {
|
||||||
val := strconv.Itoa(mode)
|
val := strconv.Itoa(mode)
|
||||||
d.Set(TransmissionMode, val)
|
d.Set(TransmissionMode, val)
|
||||||
d.Response(pause)
|
_, err := d.Response(pause)
|
||||||
return nil
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTCPTransferMode returns the ESP8266/ESP32 current client TCP/UDP transfer mode.
|
// GetTCPTransferMode returns the ESP8266/ESP32 current client TCP/UDP transfer mode.
|
||||||
func (d *Device) GetTCPTransferMode() []byte {
|
func (d *Device) GetTCPTransferMode() ([]byte, error) {
|
||||||
d.Query(TransmissionMode)
|
d.Query(TransmissionMode)
|
||||||
return d.Response(pause)
|
return d.Response(pause)
|
||||||
}
|
}
|
||||||
@@ -108,7 +127,10 @@ func (d *Device) StartSocketSend(size int) error {
|
|||||||
|
|
||||||
// when ">" is received, it indicates
|
// when ">" is received, it indicates
|
||||||
// ready to receive data
|
// ready to receive data
|
||||||
r := d.Response(pause)
|
r, err := d.Response(2000)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
if strings.Contains(string(r), ">") {
|
if strings.Contains(string(r), ">") {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -120,6 +142,6 @@ func (d *Device) StartSocketSend(size int) error {
|
|||||||
func (d *Device) EndSocketSend() error {
|
func (d *Device) EndSocketSend() error {
|
||||||
d.Write([]byte("+++"))
|
d.Write([]byte("+++"))
|
||||||
|
|
||||||
d.Response(pause)
|
_, err := d.Response(pause)
|
||||||
return nil
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
+40
-30
@@ -16,7 +16,7 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// GetWifiMode returns the ESP8266/ESP32 wifi mode.
|
// GetWifiMode returns the ESP8266/ESP32 wifi mode.
|
||||||
func (d *Device) GetWifiMode() []byte {
|
func (d *Device) GetWifiMode() ([]byte, error) {
|
||||||
d.Query(WifiMode)
|
d.Query(WifiMode)
|
||||||
return d.Response(100)
|
return d.Response(100)
|
||||||
}
|
}
|
||||||
@@ -25,14 +25,14 @@ func (d *Device) GetWifiMode() []byte {
|
|||||||
func (d *Device) SetWifiMode(mode int) error {
|
func (d *Device) SetWifiMode(mode int) error {
|
||||||
val := strconv.Itoa(mode)
|
val := strconv.Itoa(mode)
|
||||||
d.Set(WifiMode, val)
|
d.Set(WifiMode, val)
|
||||||
d.Response(pause)
|
_, err := d.Response(pause)
|
||||||
return nil
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wifi Client
|
// Wifi Client
|
||||||
|
|
||||||
// GetConnectedAP returns the ESP8266/ESP32 is currently connected to as a client.
|
// GetConnectedAP returns the ESP8266/ESP32 is currently connected to as a client.
|
||||||
func (d *Device) GetConnectedAP() []byte {
|
func (d *Device) GetConnectedAP() ([]byte, error) {
|
||||||
d.Query(ConnectAP)
|
d.Query(ConnectAP)
|
||||||
return d.Response(100)
|
return d.Response(100)
|
||||||
}
|
}
|
||||||
@@ -42,37 +42,43 @@ func (d *Device) GetConnectedAP() []byte {
|
|||||||
func (d *Device) ConnectToAP(ssid, pwd string, ws int) error {
|
func (d *Device) ConnectToAP(ssid, pwd string, ws int) error {
|
||||||
val := "\"" + ssid + "\",\"" + pwd + "\""
|
val := "\"" + ssid + "\",\"" + pwd + "\""
|
||||||
d.Set(ConnectAP, val)
|
d.Set(ConnectAP, val)
|
||||||
d.Response(ws * 1000)
|
|
||||||
|
_, err := d.Response(ws * 1000)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// DisconnectFromAP disconnects the ESP8266/ESP32 from the current access point.
|
// DisconnectFromAP disconnects the ESP8266/ESP32 from the current access point.
|
||||||
func (d *Device) DisconnectFromAP() error {
|
func (d *Device) DisconnectFromAP() error {
|
||||||
d.Execute(Disconnect)
|
d.Execute(Disconnect)
|
||||||
d.Response(1000)
|
_, err := d.Response(1000)
|
||||||
return nil
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetClientIP returns the ESP8266/ESP32 current client IP addess when connected to an Access Point.
|
// GetClientIP returns the ESP8266/ESP32 current client IP addess when connected to an Access Point.
|
||||||
func (d *Device) GetClientIP() string {
|
func (d *Device) GetClientIP() (string, error) {
|
||||||
d.Query(SetStationIP)
|
d.Query(SetStationIP)
|
||||||
return string(d.Response(100))
|
r, err := d.Response(1000)
|
||||||
|
return string(r), err
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetClientIP sets the ESP8266/ESP32 current client IP addess when connected to an Access Point.
|
// SetClientIP sets the ESP8266/ESP32 current client IP addess when connected to an Access Point.
|
||||||
func (d *Device) SetClientIP(ipaddr string) []byte {
|
func (d *Device) SetClientIP(ipaddr string) error {
|
||||||
val := "\"" + ipaddr + "\""
|
val := "\"" + ipaddr + "\""
|
||||||
d.Set(ConnectAP, val)
|
d.Set(ConnectAP, val)
|
||||||
d.Response(500)
|
_, err := d.Response(500)
|
||||||
return nil
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Access Point
|
// Access Point
|
||||||
|
|
||||||
// GetAPConfig returns the ESP8266/ESP32 current configuration when acting as an Access Point.
|
// GetAPConfig returns the ESP8266/ESP32 current configuration when acting as an Access Point.
|
||||||
func (d *Device) GetAPConfig() string {
|
func (d *Device) GetAPConfig() (string, error) {
|
||||||
d.Query(SoftAPConfigCurrent)
|
d.Query(SoftAPConfigCurrent)
|
||||||
return string(d.Response(100))
|
r, err := d.Response(100)
|
||||||
|
return string(r), err
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetAPConfig sets the ESP8266/ESP32 current configuration when acting as an Access Point.
|
// SetAPConfig sets the ESP8266/ESP32 current configuration when acting as an Access Point.
|
||||||
@@ -83,35 +89,38 @@ func (d *Device) SetAPConfig(ssid, pwd string, ch, security int) error {
|
|||||||
ecnval := strconv.Itoa(security)
|
ecnval := strconv.Itoa(security)
|
||||||
val := "\"" + ssid + "\",\"" + pwd + "\"," + chval + "," + ecnval
|
val := "\"" + ssid + "\",\"" + pwd + "\"," + chval + "," + ecnval
|
||||||
d.Set(SoftAPConfigCurrent, val)
|
d.Set(SoftAPConfigCurrent, val)
|
||||||
d.Response(1000)
|
_, err := d.Response(1000)
|
||||||
return nil
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetAPClients returns the ESP8266/ESP32 current clients when acting as an Access Point.
|
// GetAPClients returns the ESP8266/ESP32 current clients when acting as an Access Point.
|
||||||
func (d *Device) GetAPClients() string {
|
func (d *Device) GetAPClients() (string, error) {
|
||||||
d.Query(ListConnectedIP)
|
d.Query(ListConnectedIP)
|
||||||
return string(d.Response(100))
|
r, err := d.Response(100)
|
||||||
|
return string(r), err
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetAPIP returns the ESP8266/ESP32 current IP addess when configured as an Access Point.
|
// GetAPIP returns the ESP8266/ESP32 current IP addess when configured as an Access Point.
|
||||||
func (d *Device) GetAPIP() string {
|
func (d *Device) GetAPIP() (string, error) {
|
||||||
d.Query(SetSoftAPIPCurrent)
|
d.Query(SetSoftAPIPCurrent)
|
||||||
return string(d.Response(100))
|
r, err := d.Response(100)
|
||||||
|
return string(r), err
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetAPIP sets the ESP8266/ESP32 current IP addess when configured as an Access Point.
|
// SetAPIP sets the ESP8266/ESP32 current IP addess when configured as an Access Point.
|
||||||
func (d *Device) SetAPIP(ipaddr string) error {
|
func (d *Device) SetAPIP(ipaddr string) error {
|
||||||
val := "\"" + ipaddr + "\""
|
val := "\"" + ipaddr + "\""
|
||||||
d.Set(SetSoftAPIPCurrent, val)
|
d.Set(SetSoftAPIPCurrent, val)
|
||||||
d.Response(500)
|
_, err := d.Response(500)
|
||||||
return nil
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetAPConfigFlash returns the ESP8266/ESP32 current configuration acting as an Access Point
|
// GetAPConfigFlash returns the ESP8266/ESP32 current configuration acting as an Access Point
|
||||||
// from flash storage. These settings are those used after a reset.
|
// from flash storage. These settings are those used after a reset.
|
||||||
func (d *Device) GetAPConfigFlash() string {
|
func (d *Device) GetAPConfigFlash() (string, error) {
|
||||||
d.Query(SoftAPConfigFlash)
|
d.Query(SoftAPConfigFlash)
|
||||||
return string(d.Response(100))
|
r, err := d.Response(100)
|
||||||
|
return string(r), err
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetAPConfigFlash sets the ESP8266/ESP32 current configuration acting as an Access Point,
|
// SetAPConfigFlash sets the ESP8266/ESP32 current configuration acting as an Access Point,
|
||||||
@@ -123,15 +132,16 @@ func (d *Device) SetAPConfigFlash(ssid, pwd string, ch, security int) error {
|
|||||||
ecnval := strconv.Itoa(security)
|
ecnval := strconv.Itoa(security)
|
||||||
val := "\"" + ssid + "\",\"" + pwd + "\"," + chval + "," + ecnval
|
val := "\"" + ssid + "\",\"" + pwd + "\"," + chval + "," + ecnval
|
||||||
d.Set(SoftAPConfigFlash, val)
|
d.Set(SoftAPConfigFlash, val)
|
||||||
d.Response(1000)
|
_, err := d.Response(1000)
|
||||||
return nil
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetAPIPFlash returns the ESP8266/ESP32 IP address as saved to flash storage.
|
// GetAPIPFlash returns the ESP8266/ESP32 IP address as saved to flash storage.
|
||||||
// This is the IP address that will be used after a reset.
|
// This is the IP address that will be used after a reset.
|
||||||
func (d *Device) GetAPIPFlash() string {
|
func (d *Device) GetAPIPFlash() (string, error) {
|
||||||
d.Query(SetSoftAPIPFlash)
|
d.Query(SetSoftAPIPFlash)
|
||||||
return string(d.Response(100))
|
r, err := d.Response(100)
|
||||||
|
return string(r), err
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetAPIPFlash sets the ESP8266/ESP32 current IP addess when configured as an Access Point.
|
// SetAPIPFlash sets the ESP8266/ESP32 current IP addess when configured as an Access Point.
|
||||||
@@ -139,6 +149,6 @@ func (d *Device) GetAPIPFlash() string {
|
|||||||
func (d *Device) SetAPIPFlash(ipaddr string) error {
|
func (d *Device) SetAPIPFlash(ipaddr string) error {
|
||||||
val := "\"" + ipaddr + "\""
|
val := "\"" + ipaddr + "\""
|
||||||
d.Set(SetSoftAPIPFlash, val)
|
d.Set(SetSoftAPIPFlash, val)
|
||||||
d.Response(500)
|
_, err := d.Response(500)
|
||||||
return nil
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,84 @@
|
|||||||
|
// This example demostrates how to control the "Dotstar" (APA102) LED included
|
||||||
|
// on the Adafruit Itsy Bitsy M0 board. It implements a "rainbow effect" based
|
||||||
|
// on the following example:
|
||||||
|
// https://github.com/adafruit/Adafruit_Learning_System_Guides/blob/master/CircuitPython_Essentials/CircuitPython_Internal_RGB_LED_rainbow.py
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image/color"
|
||||||
|
"machine"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"tinygo.org/x/drivers/apa102"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
apa apa102.Device
|
||||||
|
|
||||||
|
led = machine.PWM{machine.LED}
|
||||||
|
leds = make([]color.RGBA, 1)
|
||||||
|
wheel = &Wheel{Brightness: 0x10}
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
|
||||||
|
// APA102 on Itsy Bitsy is connected to pins that require a software-based
|
||||||
|
// SPI implementation.
|
||||||
|
apa = apa102.NewSoftwareSPI(machine.PA00, machine.PA01, 1)
|
||||||
|
|
||||||
|
// Configure the regular on-board LED for PWM fading
|
||||||
|
machine.InitPWM()
|
||||||
|
led.Configure()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
|
||||||
|
// We'll fade the on-board LED in a goroutine to show/ensure that the APA102
|
||||||
|
// works fine with the scheduler enabled. Comment this out to test this code
|
||||||
|
// with the scheduler disabled.
|
||||||
|
go func() {
|
||||||
|
for i, brightening := uint8(0), false; ; i++ {
|
||||||
|
if i == 0 {
|
||||||
|
brightening = !brightening
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
var brightness uint16 = uint16(i) << 8
|
||||||
|
if !brightening {
|
||||||
|
brightness = 0xFFFF - brightness
|
||||||
|
}
|
||||||
|
led.Set(brightness)
|
||||||
|
time.Sleep(5 * time.Millisecond)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Use the "wheel" function from Adafruit's example to cycle the APA102
|
||||||
|
for {
|
||||||
|
leds[0] = wheel.Next()
|
||||||
|
apa.WriteColors(leds)
|
||||||
|
time.Sleep(25 * time.Millisecond)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wheel is a port of Adafruit's Circuit Python example referenced above.
|
||||||
|
type Wheel struct {
|
||||||
|
Brightness uint8
|
||||||
|
pos uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next increments the internal state of the color and returns the new RGBA
|
||||||
|
func (w *Wheel) Next() (c color.RGBA) {
|
||||||
|
pos := w.pos
|
||||||
|
if w.pos < 85 {
|
||||||
|
c = color.RGBA{R: 0xFF - pos*3, G: pos * 3, B: 0x0, A: w.Brightness}
|
||||||
|
} else if w.pos < 170 {
|
||||||
|
pos -= 85
|
||||||
|
c = color.RGBA{R: 0x0, G: 0xFF - pos*3, B: pos * 3, A: w.Brightness}
|
||||||
|
} else {
|
||||||
|
pos -= 170
|
||||||
|
c = color.RGBA{R: pos * 3, G: 0x0, B: 0xFF - pos*3, A: w.Brightness}
|
||||||
|
}
|
||||||
|
w.pos++
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -23,11 +23,12 @@ const actAsAP = false
|
|||||||
const ssid = "YOURSSID"
|
const ssid = "YOURSSID"
|
||||||
const pass = "YOURPASS"
|
const pass = "YOURPASS"
|
||||||
|
|
||||||
|
// these are the default pins for the Arduino Nano33 IoT.
|
||||||
// change these to connect to a different UART or pins for the ESP8266/ESP32
|
// change these to connect to a different UART or pins for the ESP8266/ESP32
|
||||||
var (
|
var (
|
||||||
uart = machine.UART1
|
uart = machine.UART1
|
||||||
tx = machine.D10
|
tx = machine.PA22
|
||||||
rx = machine.D11
|
rx = machine.PA23
|
||||||
|
|
||||||
console = machine.UART0
|
console = machine.UART0
|
||||||
|
|
||||||
@@ -42,28 +43,20 @@ func main() {
|
|||||||
adaptor.Configure()
|
adaptor.Configure()
|
||||||
|
|
||||||
// first check if connected
|
// first check if connected
|
||||||
if adaptor.Connected() {
|
if connectToESP() {
|
||||||
|
println("Connected to wifi adaptor.")
|
||||||
adaptor.Echo(false)
|
adaptor.Echo(false)
|
||||||
console.Write([]byte("\r\n"))
|
|
||||||
console.Write([]byte("ESP-AT console enabled.\r\n"))
|
|
||||||
console.Write([]byte("Firmware version:\r\n"))
|
|
||||||
console.Write(adaptor.Version())
|
|
||||||
console.Write([]byte("\r\n"))
|
|
||||||
|
|
||||||
if actAsAP {
|
connectToAP()
|
||||||
provideAP()
|
|
||||||
} else {
|
|
||||||
connectToAP()
|
|
||||||
}
|
|
||||||
|
|
||||||
console.Write([]byte("Type an AT command then press enter:\r\n"))
|
|
||||||
prompt()
|
|
||||||
} else {
|
} else {
|
||||||
console.Write([]byte("\r\n"))
|
println("")
|
||||||
console.Write([]byte("Unable to connect to wifi adaptor.\r\n"))
|
failMessage("Unable to connect to wifi adaptor.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
println("Type an AT command then press enter:")
|
||||||
|
prompt()
|
||||||
|
|
||||||
input := make([]byte, 64)
|
input := make([]byte, 64)
|
||||||
i := 0
|
i := 0
|
||||||
for {
|
for {
|
||||||
@@ -81,7 +74,8 @@ func main() {
|
|||||||
adaptor.Write(input[:i+2])
|
adaptor.Write(input[:i+2])
|
||||||
|
|
||||||
// display response
|
// display response
|
||||||
console.Write(adaptor.Response(100))
|
r, _ := adaptor.Response(500)
|
||||||
|
console.Write(r)
|
||||||
|
|
||||||
// prompt
|
// prompt
|
||||||
prompt()
|
prompt()
|
||||||
@@ -100,28 +94,50 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func prompt() {
|
func prompt() {
|
||||||
console.Write([]byte("ESPAT>"))
|
print("ESPAT>")
|
||||||
|
}
|
||||||
|
|
||||||
|
// connect to ESP8266/ESP32
|
||||||
|
func connectToESP() bool {
|
||||||
|
for i := 0; i < 5; i++ {
|
||||||
|
println("Connecting to wifi adaptor...")
|
||||||
|
if adaptor.Connected() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// connect to access point
|
// connect to access point
|
||||||
func connectToAP() {
|
func connectToAP() {
|
||||||
console.Write([]byte("Connecting to wifi network...\r\n"))
|
println("Connecting to wifi network '" + ssid + "'")
|
||||||
|
|
||||||
adaptor.SetWifiMode(espat.WifiModeClient)
|
adaptor.SetWifiMode(espat.WifiModeClient)
|
||||||
adaptor.ConnectToAP(ssid, pass, 10)
|
adaptor.ConnectToAP(ssid, pass, 10)
|
||||||
console.Write([]byte("Connected.\r\n"))
|
|
||||||
console.Write([]byte(adaptor.GetClientIP()))
|
println("Connected.")
|
||||||
console.Write([]byte("\r\n"))
|
ip, err := adaptor.GetClientIP()
|
||||||
|
if err != nil {
|
||||||
|
failMessage(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
println(ip)
|
||||||
}
|
}
|
||||||
|
|
||||||
// provide access point
|
// provide access point
|
||||||
func provideAP() {
|
func provideAP() {
|
||||||
time.Sleep(500 * time.Millisecond)
|
println("Starting wifi network as access point '" + ssid + "'...")
|
||||||
console.Write([]byte("Starting wifi network as access point '"))
|
|
||||||
console.Write([]byte(ssid))
|
|
||||||
console.Write([]byte("'...\r\n"))
|
|
||||||
adaptor.SetWifiMode(espat.WifiModeAP)
|
adaptor.SetWifiMode(espat.WifiModeAP)
|
||||||
adaptor.SetAPConfig(ssid, pass, 7, espat.WifiAPSecurityWPA2_PSK)
|
adaptor.SetAPConfig(ssid, pass, 7, espat.WifiAPSecurityWPA2_PSK)
|
||||||
console.Write([]byte("Ready.\r\n"))
|
println("Ready.")
|
||||||
console.Write([]byte(adaptor.GetAPIP()))
|
ip, _ := adaptor.GetAPIP()
|
||||||
console.Write([]byte("\r\n"))
|
println(ip)
|
||||||
|
}
|
||||||
|
|
||||||
|
func failMessage(msg string) {
|
||||||
|
for {
|
||||||
|
println(msg)
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,11 +21,12 @@ const actAsAP = false
|
|||||||
const ssid = "YOURSSID"
|
const ssid = "YOURSSID"
|
||||||
const pass = "YOURPASS"
|
const pass = "YOURPASS"
|
||||||
|
|
||||||
|
// these are the default pins for the Arduino Nano33 IoT.
|
||||||
// change these to connect to a different UART or pins for the ESP8266/ESP32
|
// change these to connect to a different UART or pins for the ESP8266/ESP32
|
||||||
var (
|
var (
|
||||||
uart = machine.UART1
|
uart = machine.UART1
|
||||||
tx = machine.D10
|
tx = machine.PA22
|
||||||
rx = machine.D11
|
rx = machine.PA23
|
||||||
|
|
||||||
adaptor *espat.Device
|
adaptor *espat.Device
|
||||||
)
|
)
|
||||||
@@ -42,17 +43,14 @@ func main() {
|
|||||||
readyled.High()
|
readyled.High()
|
||||||
|
|
||||||
// first check if connected
|
// first check if connected
|
||||||
if adaptor.Connected() {
|
if connectToESP() {
|
||||||
println("Connected to wifi adaptor.")
|
println("Connected to wifi adaptor.")
|
||||||
adaptor.Echo(false)
|
adaptor.Echo(false)
|
||||||
|
|
||||||
if actAsAP {
|
connectToAP()
|
||||||
provideAP()
|
|
||||||
} else {
|
|
||||||
connectToAP()
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
println("Unable to connect to wifi adaptor.")
|
println("")
|
||||||
|
failMessage("Unable to connect to wifi adaptor.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,21 +83,47 @@ func main() {
|
|||||||
println("Done.")
|
println("Done.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// connect to ESP8266/ESP32
|
||||||
|
func connectToESP() bool {
|
||||||
|
for i := 0; i < 5; i++ {
|
||||||
|
println("Connecting to wifi adaptor...")
|
||||||
|
if adaptor.Connected() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// connect to access point
|
// connect to access point
|
||||||
func connectToAP() {
|
func connectToAP() {
|
||||||
println("Connecting to wifi network...")
|
println("Connecting to wifi network '" + ssid + "'")
|
||||||
|
|
||||||
adaptor.SetWifiMode(espat.WifiModeClient)
|
adaptor.SetWifiMode(espat.WifiModeClient)
|
||||||
adaptor.ConnectToAP(ssid, pass, 10)
|
adaptor.ConnectToAP(ssid, pass, 10)
|
||||||
|
|
||||||
println("Connected.")
|
println("Connected.")
|
||||||
println(adaptor.GetClientIP())
|
ip, err := adaptor.GetClientIP()
|
||||||
|
if err != nil {
|
||||||
|
failMessage(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
println(ip)
|
||||||
}
|
}
|
||||||
|
|
||||||
// provide access point
|
// provide access point
|
||||||
func provideAP() {
|
func provideAP() {
|
||||||
println("Starting wifi network as access point:")
|
println("Starting wifi network as access point '" + ssid + "'...")
|
||||||
println(ssid)
|
|
||||||
adaptor.SetWifiMode(espat.WifiModeAP)
|
adaptor.SetWifiMode(espat.WifiModeAP)
|
||||||
adaptor.SetAPConfig(ssid, pass, 7, espat.WifiAPSecurityWPA2_PSK)
|
adaptor.SetAPConfig(ssid, pass, 7, espat.WifiAPSecurityWPA2_PSK)
|
||||||
println("Ready.")
|
println("Ready.")
|
||||||
println(adaptor.GetAPIP())
|
ip, _ := adaptor.GetAPIP()
|
||||||
|
println(ip)
|
||||||
|
}
|
||||||
|
|
||||||
|
func failMessage(msg string) {
|
||||||
|
for {
|
||||||
|
println(msg)
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,11 +21,12 @@ const pass = "YOURPASS"
|
|||||||
// IP address of the listener aka "hub". Replace with your own info.
|
// IP address of the listener aka "hub". Replace with your own info.
|
||||||
const hubIP = "0.0.0.0"
|
const hubIP = "0.0.0.0"
|
||||||
|
|
||||||
|
// these are the default pins for the Arduino Nano33 IoT.
|
||||||
// change these to connect to a different UART or pins for the ESP8266/ESP32
|
// change these to connect to a different UART or pins for the ESP8266/ESP32
|
||||||
var (
|
var (
|
||||||
uart = machine.UART1
|
uart = machine.UART1
|
||||||
tx = machine.D10
|
tx = machine.PA22
|
||||||
rx = machine.D11
|
rx = machine.PA23
|
||||||
|
|
||||||
adaptor *espat.Device
|
adaptor *espat.Device
|
||||||
)
|
)
|
||||||
@@ -38,13 +39,14 @@ func main() {
|
|||||||
adaptor.Configure()
|
adaptor.Configure()
|
||||||
|
|
||||||
// first check if connected
|
// first check if connected
|
||||||
if adaptor.Connected() {
|
if connectToESP() {
|
||||||
println("Connected to wifi adaptor.")
|
println("Connected to wifi adaptor.")
|
||||||
adaptor.Echo(false)
|
adaptor.Echo(false)
|
||||||
|
|
||||||
connectToAP()
|
connectToAP()
|
||||||
} else {
|
} else {
|
||||||
println("Unable to connect to wifi adaptor.")
|
println("")
|
||||||
|
failMessage("Unable to connect to wifi adaptor.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,11 +72,37 @@ func main() {
|
|||||||
println("Done.")
|
println("Done.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// connect to ESP8266/ESP32
|
||||||
|
func connectToESP() bool {
|
||||||
|
for i := 0; i < 5; i++ {
|
||||||
|
println("Connecting to wifi adaptor...")
|
||||||
|
if adaptor.Connected() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// connect to access point
|
// connect to access point
|
||||||
func connectToAP() {
|
func connectToAP() {
|
||||||
println("Connecting to wifi network...")
|
println("Connecting to wifi network '" + ssid + "'")
|
||||||
|
|
||||||
adaptor.SetWifiMode(espat.WifiModeClient)
|
adaptor.SetWifiMode(espat.WifiModeClient)
|
||||||
adaptor.ConnectToAP(ssid, pass, 10)
|
adaptor.ConnectToAP(ssid, pass, 10)
|
||||||
|
|
||||||
println("Connected.")
|
println("Connected.")
|
||||||
println(adaptor.GetClientIP())
|
ip, err := adaptor.GetClientIP()
|
||||||
|
if err != nil {
|
||||||
|
failMessage(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
println(ip)
|
||||||
|
}
|
||||||
|
|
||||||
|
func failMessage(msg string) {
|
||||||
|
for {
|
||||||
|
println(msg)
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,14 +25,16 @@ const ssid = "YOURSSID"
|
|||||||
const pass = "YOURPASS"
|
const pass = "YOURPASS"
|
||||||
|
|
||||||
// IP address of the MQTT broker to use. Replace with your own info.
|
// IP address of the MQTT broker to use. Replace with your own info.
|
||||||
//const server = "tcp://test.mosquitto.org:1883"
|
const server = "tcp://test.mosquitto.org:1883"
|
||||||
const server = "ssl://test.mosquitto.org:8883"
|
|
||||||
|
|
||||||
|
//const server = "ssl://test.mosquitto.org:8883"
|
||||||
|
|
||||||
|
// these are the default pins for the Arduino Nano33 IoT.
|
||||||
// change these to connect to a different UART or pins for the ESP8266/ESP32
|
// change these to connect to a different UART or pins for the ESP8266/ESP32
|
||||||
var (
|
var (
|
||||||
uart = machine.UART1
|
uart = machine.UART2
|
||||||
tx = machine.D10
|
tx = machine.PA22
|
||||||
rx = machine.D11
|
rx = machine.PA23
|
||||||
|
|
||||||
console = machine.UART0
|
console = machine.UART0
|
||||||
|
|
||||||
@@ -65,7 +67,7 @@ func main() {
|
|||||||
opts := mqtt.NewClientOptions(adaptor)
|
opts := mqtt.NewClientOptions(adaptor)
|
||||||
opts.AddBroker(server).SetClientID("tinygo-client-" + randomString(10))
|
opts.AddBroker(server).SetClientID("tinygo-client-" + randomString(10))
|
||||||
|
|
||||||
println("Connectng to MQTT...")
|
println("Connecting to MQTT broker at", server)
|
||||||
cl := mqtt.NewClient(opts)
|
cl := mqtt.NewClient(opts)
|
||||||
if token := cl.Connect(); token.Wait() && token.Error() != nil {
|
if token := cl.Connect(); token.Wait() && token.Error() != nil {
|
||||||
failMessage(token.Error().Error())
|
failMessage(token.Error().Error())
|
||||||
@@ -104,13 +106,18 @@ func connectToESP() bool {
|
|||||||
|
|
||||||
// connect to access point
|
// connect to access point
|
||||||
func connectToAP() {
|
func connectToAP() {
|
||||||
println("Connecting to wifi network...")
|
println("Connecting to wifi network '" + ssid + "'")
|
||||||
|
|
||||||
adaptor.SetWifiMode(espat.WifiModeClient)
|
adaptor.SetWifiMode(espat.WifiModeClient)
|
||||||
adaptor.ConnectToAP(ssid, pass, 10)
|
adaptor.ConnectToAP(ssid, pass, 10)
|
||||||
|
|
||||||
println("Connected.")
|
println("Connected.")
|
||||||
println(adaptor.GetClientIP())
|
ip, err := adaptor.GetClientIP()
|
||||||
|
if err != nil {
|
||||||
|
failMessage(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
println(ip)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns an int >= min, < max
|
// Returns an int >= min, < max
|
||||||
|
|||||||
@@ -0,0 +1,163 @@
|
|||||||
|
// This is a sensor station that uses a ESP8266 or ESP32 running on the device UART1.
|
||||||
|
// It creates an MQTT connection that publishes a message every second
|
||||||
|
// to an MQTT broker.
|
||||||
|
//
|
||||||
|
// In other words:
|
||||||
|
// Your computer <--> UART0 <--> MCU <--> UART1 <--> ESP8266 <--> Internet <--> MQTT broker.
|
||||||
|
//
|
||||||
|
// You must also install the Paho MQTT package to build this program:
|
||||||
|
//
|
||||||
|
// go get -u github.com/eclipse/paho.mqtt.golang
|
||||||
|
//
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"machine"
|
||||||
|
"math/rand"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"tinygo.org/x/drivers/espat"
|
||||||
|
"tinygo.org/x/drivers/espat/mqtt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// access point info
|
||||||
|
const ssid = "YOURSSID"
|
||||||
|
const pass = "YOURPASS"
|
||||||
|
|
||||||
|
// IP address of the MQTT broker to use. Replace with your own info.
|
||||||
|
//const server = "tcp://test.mosquitto.org:1883"
|
||||||
|
|
||||||
|
const server = "ssl://test.mosquitto.org:8883"
|
||||||
|
|
||||||
|
// change these to connect to a different UART or pins for the ESP8266/ESP32
|
||||||
|
var (
|
||||||
|
// these are defaults for the Arduino Nano33 IoT.
|
||||||
|
uart = machine.UART1
|
||||||
|
tx = machine.PA22
|
||||||
|
rx = machine.PA23
|
||||||
|
|
||||||
|
console = machine.UART0
|
||||||
|
|
||||||
|
adaptor *espat.Device
|
||||||
|
cl mqtt.Client
|
||||||
|
topicTx = "tinygo/tx"
|
||||||
|
topicRx = "tinygo/rx"
|
||||||
|
)
|
||||||
|
|
||||||
|
func subHandler(client mqtt.Client, msg mqtt.Message) {
|
||||||
|
fmt.Printf("[%s] ", msg.Topic())
|
||||||
|
fmt.Printf("%s\r\n", msg.Payload())
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
time.Sleep(3000 * time.Millisecond)
|
||||||
|
|
||||||
|
uart.Configure(machine.UARTConfig{TX: tx, RX: rx})
|
||||||
|
rand.Seed(time.Now().UnixNano())
|
||||||
|
|
||||||
|
// Init esp8266/esp32
|
||||||
|
adaptor = espat.New(uart)
|
||||||
|
adaptor.Configure()
|
||||||
|
|
||||||
|
// first check if connected
|
||||||
|
if connectToESP() {
|
||||||
|
println("Connected to wifi adaptor.")
|
||||||
|
adaptor.Echo(false)
|
||||||
|
|
||||||
|
connectToAP()
|
||||||
|
} else {
|
||||||
|
println("")
|
||||||
|
failMessage("Unable to connect to wifi adaptor.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
opts := mqtt.NewClientOptions(adaptor)
|
||||||
|
opts.AddBroker(server).SetClientID("tinygo-client-" + randomString(10))
|
||||||
|
|
||||||
|
println("Connecting to MQTT broker at", server)
|
||||||
|
cl = mqtt.NewClient(opts)
|
||||||
|
if token := cl.Connect(); token.Wait() && token.Error() != nil {
|
||||||
|
failMessage(token.Error().Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// subscribe
|
||||||
|
token := cl.Subscribe(topicRx, 0, subHandler)
|
||||||
|
token.Wait()
|
||||||
|
if token.Error() != nil {
|
||||||
|
failMessage(token.Error().Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
go publishing()
|
||||||
|
|
||||||
|
select {}
|
||||||
|
|
||||||
|
// Right now this code is never reached. Need a way to trigger it...
|
||||||
|
println("Disconnecting MQTT...")
|
||||||
|
cl.Disconnect(100)
|
||||||
|
|
||||||
|
println("Done.")
|
||||||
|
}
|
||||||
|
|
||||||
|
func publishing() {
|
||||||
|
for {
|
||||||
|
println("Publishing MQTT message...")
|
||||||
|
data := []byte("{\"e\":[{ \"n\":\"hello\", \"v\":101 }]}")
|
||||||
|
token := cl.Publish(topicTx, 0, false, data)
|
||||||
|
token.Wait()
|
||||||
|
if token.Error() != nil {
|
||||||
|
println(token.Error().Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(1000 * time.Millisecond)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// connect to ESP8266/ESP32
|
||||||
|
func connectToESP() bool {
|
||||||
|
for i := 0; i < 5; i++ {
|
||||||
|
println("Connecting to wifi adaptor...")
|
||||||
|
if adaptor.Connected() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// connect to access point
|
||||||
|
func connectToAP() {
|
||||||
|
println("Connecting to wifi network '" + ssid + "'")
|
||||||
|
|
||||||
|
adaptor.SetWifiMode(espat.WifiModeClient)
|
||||||
|
adaptor.ConnectToAP(ssid, pass, 10)
|
||||||
|
|
||||||
|
println("Connected.")
|
||||||
|
ip, err := adaptor.GetClientIP()
|
||||||
|
if err != nil {
|
||||||
|
failMessage(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
println(ip)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns an int >= min, < max
|
||||||
|
func randomInt(min, max int) int {
|
||||||
|
return min + rand.Intn(max-min)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate a random string of A-Z chars with len = l
|
||||||
|
func randomString(len int) string {
|
||||||
|
bytes := make([]byte, len)
|
||||||
|
for i := 0; i < len; i++ {
|
||||||
|
bytes[i] = byte(randomInt(65, 90))
|
||||||
|
}
|
||||||
|
return string(bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
func failMessage(msg string) {
|
||||||
|
for {
|
||||||
|
println(msg)
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -21,6 +21,7 @@ const pass = "YOURPASS"
|
|||||||
// IP address of the server aka "hub". Replace with your own info.
|
// IP address of the server aka "hub". Replace with your own info.
|
||||||
const serverIP = "0.0.0.0"
|
const serverIP = "0.0.0.0"
|
||||||
|
|
||||||
|
// these are the default pins for the Arduino Nano33 IoT.
|
||||||
// change these to connect to a different UART or pins for the ESP8266/ESP32
|
// change these to connect to a different UART or pins for the ESP8266/ESP32
|
||||||
var (
|
var (
|
||||||
uart = machine.UART1
|
uart = machine.UART1
|
||||||
@@ -38,13 +39,14 @@ func main() {
|
|||||||
adaptor.Configure()
|
adaptor.Configure()
|
||||||
|
|
||||||
// first check if connected
|
// first check if connected
|
||||||
if adaptor.Connected() {
|
if connectToESP() {
|
||||||
println("Connected to wifi adaptor.")
|
println("Connected to wifi adaptor.")
|
||||||
adaptor.Echo(false)
|
adaptor.Echo(false)
|
||||||
|
|
||||||
connectToAP()
|
connectToAP()
|
||||||
} else {
|
} else {
|
||||||
println("Unable to connect to wifi adaptor.")
|
println("")
|
||||||
|
failMessage("Unable to connect to wifi adaptor.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,7 +56,10 @@ func main() {
|
|||||||
laddr := &net.TCPAddr{Port: 8080}
|
laddr := &net.TCPAddr{Port: 8080}
|
||||||
|
|
||||||
println("Dialing TCP connection...")
|
println("Dialing TCP connection...")
|
||||||
conn, _ := net.DialTCP("tcp", laddr, raddr)
|
conn, err := net.DialTCP("tcp", laddr, raddr)
|
||||||
|
if err != nil {
|
||||||
|
failMessage(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
for {
|
for {
|
||||||
// send data
|
// send data
|
||||||
@@ -70,11 +75,37 @@ func main() {
|
|||||||
println("Done.")
|
println("Done.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// connect to ESP8266/ESP32
|
||||||
|
func connectToESP() bool {
|
||||||
|
for i := 0; i < 5; i++ {
|
||||||
|
println("Connecting to wifi adaptor...")
|
||||||
|
if adaptor.Connected() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// connect to access point
|
// connect to access point
|
||||||
func connectToAP() {
|
func connectToAP() {
|
||||||
println("Connecting to wifi network...")
|
println("Connecting to wifi network '" + ssid + "'")
|
||||||
|
|
||||||
adaptor.SetWifiMode(espat.WifiModeClient)
|
adaptor.SetWifiMode(espat.WifiModeClient)
|
||||||
adaptor.ConnectToAP(ssid, pass, 10)
|
adaptor.ConnectToAP(ssid, pass, 10)
|
||||||
|
|
||||||
println("Connected.")
|
println("Connected.")
|
||||||
println(adaptor.GetClientIP())
|
ip, err := adaptor.GetClientIP()
|
||||||
|
if err != nil {
|
||||||
|
failMessage(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
println(ip)
|
||||||
|
}
|
||||||
|
|
||||||
|
func failMessage(msg string) {
|
||||||
|
for {
|
||||||
|
println(msg)
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import (
|
|||||||
var i2c = machine.I2C1
|
var i2c = machine.I2C1
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
i2c.Configure(machine.I2CConfig{})
|
i2c.Configure(machine.I2CConfig{SCL: machine.SCL1_PIN, SDA: machine.SDA1_PIN})
|
||||||
|
|
||||||
accel := lis3dh.New(i2c)
|
accel := lis3dh.New(i2c)
|
||||||
accel.Address = lis3dh.Address1 // address on the Circuit Playground Express
|
accel.Address = lis3dh.Address1 // address on the Circuit Playground Express
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
// Connects to a MCP3008 ADC via SPI.
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"machine"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"tinygo.org/x/drivers/mcp3008"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
spi = machine.SPI0
|
||||||
|
csPin = machine.D12
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
spi.Configure(machine.SPIConfig{
|
||||||
|
Frequency: 4000000,
|
||||||
|
Mode: 3})
|
||||||
|
|
||||||
|
adc := mcp3008.New(spi, csPin)
|
||||||
|
adc.Configure()
|
||||||
|
|
||||||
|
// get "CH0" aka "machine.ADC" interface to channel 0 from ADC.
|
||||||
|
p := adc.CH0
|
||||||
|
|
||||||
|
for {
|
||||||
|
val := p.Get()
|
||||||
|
println(val)
|
||||||
|
time.Sleep(50 * time.Millisecond)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"machine"
|
||||||
|
|
||||||
|
"tinygo.org/x/drivers/veml6070"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
machine.I2C0.Configure(machine.I2CConfig{})
|
||||||
|
sensor := veml6070.New(machine.I2C0)
|
||||||
|
|
||||||
|
if !sensor.Configure() {
|
||||||
|
println("VEML6070 could not be configured")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
println("VEML6070 configured")
|
||||||
|
|
||||||
|
for {
|
||||||
|
intensity, _ := sensor.ReadUVALightIntensity()
|
||||||
|
println("UVA light intensity:", float32(intensity)/1000.0, "W/(m*m)")
|
||||||
|
|
||||||
|
switch sensor.GetEstimatedRiskLevel(intensity) {
|
||||||
|
case veml6070.UVI_RISK_LOW:
|
||||||
|
println("UV risk level: low")
|
||||||
|
case veml6070.UVI_RISK_MODERATE:
|
||||||
|
println("UV risk level: moderate")
|
||||||
|
case veml6070.UVI_RISK_HIGH:
|
||||||
|
println("UV risk level: high")
|
||||||
|
case veml6070.UVI_RISK_VERY_HIGH:
|
||||||
|
println("UV risk level: very high")
|
||||||
|
case veml6070.UVI_RISK_EXTREME:
|
||||||
|
println("UV risk level: extreme")
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(2 * time.Second)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
// Package mcp3008 implements a driver for the MCP3008 Analog to Digital Converter.
|
||||||
|
//
|
||||||
|
// Datasheet: http://ww1.microchip.com/downloads/en/DeviceDoc/21295d.pdf
|
||||||
|
//
|
||||||
|
package mcp3008 // import "tinygo.org/x/drivers/mcp3008"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"machine"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Device wraps MCP3008 SPI ADC.
|
||||||
|
type Device struct {
|
||||||
|
bus machine.SPI
|
||||||
|
cs machine.Pin
|
||||||
|
tx []byte
|
||||||
|
rx []byte
|
||||||
|
CH0 ADCPin
|
||||||
|
CH1 ADCPin
|
||||||
|
CH2 ADCPin
|
||||||
|
CH3 ADCPin
|
||||||
|
CH4 ADCPin
|
||||||
|
CH5 ADCPin
|
||||||
|
CH6 ADCPin
|
||||||
|
CH7 ADCPin
|
||||||
|
}
|
||||||
|
|
||||||
|
// ADCPin is the implementation of the ADConverter interface.
|
||||||
|
type ADCPin struct {
|
||||||
|
machine.Pin
|
||||||
|
d *Device
|
||||||
|
}
|
||||||
|
|
||||||
|
// New returns a new MCP3008 driver. Pass in a fully configured SPI bus.
|
||||||
|
func New(b machine.SPI, csPin machine.Pin) *Device {
|
||||||
|
d := &Device{bus: b,
|
||||||
|
cs: csPin,
|
||||||
|
tx: make([]byte, 3),
|
||||||
|
rx: make([]byte, 3),
|
||||||
|
}
|
||||||
|
|
||||||
|
// setup all channels
|
||||||
|
d.CH0 = d.GetADC(0)
|
||||||
|
d.CH1 = d.GetADC(1)
|
||||||
|
d.CH2 = d.GetADC(2)
|
||||||
|
d.CH3 = d.GetADC(3)
|
||||||
|
d.CH4 = d.GetADC(4)
|
||||||
|
d.CH5 = d.GetADC(5)
|
||||||
|
d.CH6 = d.GetADC(6)
|
||||||
|
d.CH7 = d.GetADC(7)
|
||||||
|
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure sets up the device for communication
|
||||||
|
func (d *Device) Configure() {
|
||||||
|
d.cs.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read analog data from channel
|
||||||
|
func (d *Device) Read(ch int) (uint16, error) {
|
||||||
|
if ch < 0 || ch > 7 {
|
||||||
|
return 0, errors.New("invalid channel for MCP3008 Read")
|
||||||
|
}
|
||||||
|
|
||||||
|
return d.GetADC(ch).Get(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetADC returns an ADC for a specific channel.
|
||||||
|
func (d *Device) GetADC(ch int) ADCPin {
|
||||||
|
return ADCPin{machine.Pin(ch), d}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the current reading for a specific ADCPin.
|
||||||
|
func (p ADCPin) Get() uint16 {
|
||||||
|
p.d.tx[0] = 0x01
|
||||||
|
p.d.tx[1] = byte(8+p.Pin) << 4
|
||||||
|
p.d.tx[2] = 0x00
|
||||||
|
|
||||||
|
p.d.cs.Low()
|
||||||
|
p.d.bus.Tx(p.d.tx, p.d.rx)
|
||||||
|
|
||||||
|
// scale result to 16bit value like other ADCs
|
||||||
|
result := uint16((p.d.rx[1]&0x3))<<8 + uint16(p.d.rx[2])<<6
|
||||||
|
p.d.cs.High()
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure here just for interface compatibility.
|
||||||
|
func (p ADCPin) Configure() {
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
// Package semihosting implements parts of the ARM semihosting specification,
|
||||||
|
// for communicating over a debug connection.
|
||||||
|
//
|
||||||
|
// If you want to use it in OpenOCD, you have to enable it first with the
|
||||||
|
// following command:
|
||||||
|
//
|
||||||
|
// arm semihosting enable
|
||||||
|
package semihosting
|
||||||
|
|
||||||
|
import (
|
||||||
|
"device/arm"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// IOError is returned by I/O operations when they fail.
|
||||||
|
type IOError struct {
|
||||||
|
BytesWritten int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *IOError) Error() string {
|
||||||
|
return "semihosting: I/O error"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write writes the given data to the given file descriptor. It returns an
|
||||||
|
// *IOError if the write was not successful.
|
||||||
|
func Write(fd uintptr, data []byte) error {
|
||||||
|
if len(data) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
params := struct {
|
||||||
|
fd uintptr
|
||||||
|
data unsafe.Pointer
|
||||||
|
len int
|
||||||
|
}{
|
||||||
|
fd: fd,
|
||||||
|
data: unsafe.Pointer(&data[0]),
|
||||||
|
len: len(data),
|
||||||
|
}
|
||||||
|
unwritten := arm.SemihostingCall(arm.SemihostingWrite, uintptr(unsafe.Pointer(¶ms)))
|
||||||
|
if unwritten != 0 {
|
||||||
|
// Error: unwritten is the number of bytes not written.
|
||||||
|
return &IOError{
|
||||||
|
BytesWritten: len(data) - unwritten,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package semihosting
|
||||||
|
|
||||||
|
// These three file descriptors are connected to the host stdin/stdout/stderr,
|
||||||
|
// and can be used for logging.
|
||||||
|
var (
|
||||||
|
Stdin = File{fd: 0}
|
||||||
|
Stdout = File{fd: 1}
|
||||||
|
Stderr = File{fd: 2}
|
||||||
|
)
|
||||||
|
|
||||||
|
// File represents a semihosting file descriptor.
|
||||||
|
type File struct {
|
||||||
|
fd uintptr
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write writes the given data buffer to the file descriptor, returning an error
|
||||||
|
// if the write could not complete successfully.
|
||||||
|
func (f *File) Write(buf []byte) error {
|
||||||
|
return Write(f.fd, buf)
|
||||||
|
}
|
||||||
+3
-16
@@ -34,7 +34,7 @@ type Config struct {
|
|||||||
Height int16
|
Height int16
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates a new ST7735 connection. The SPI wire must already be configured.
|
// New creates a new SSD1331 connection. The SPI wire must already be configured.
|
||||||
func New(bus machine.SPI, resetPin, dcPin, csPin machine.Pin) Device {
|
func New(bus machine.SPI, resetPin, dcPin, csPin machine.Pin) Device {
|
||||||
dcPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
dcPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||||
resetPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
resetPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||||
@@ -250,21 +250,8 @@ func (d *Device) Data(data uint8) {
|
|||||||
|
|
||||||
// Tx sends data to the display
|
// Tx sends data to the display
|
||||||
func (d *Device) Tx(data []byte, isCommand bool) {
|
func (d *Device) Tx(data []byte, isCommand bool) {
|
||||||
if isCommand {
|
d.dcPin.Set(!isCommand)
|
||||||
d.csPin.High()
|
d.bus.Tx(data, nil)
|
||||||
d.dcPin.Low()
|
|
||||||
d.csPin.Low()
|
|
||||||
|
|
||||||
d.bus.Tx(data, nil)
|
|
||||||
d.csPin.High()
|
|
||||||
} else {
|
|
||||||
d.csPin.High()
|
|
||||||
d.dcPin.High()
|
|
||||||
d.csPin.Low()
|
|
||||||
|
|
||||||
d.bus.Tx(data, nil)
|
|
||||||
d.csPin.High()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Size returns the current size of the display.
|
// Size returns the current size of the display.
|
||||||
|
|||||||
+2
-15
@@ -383,21 +383,8 @@ func (d *Device) Data(data uint8) {
|
|||||||
|
|
||||||
// Tx sends data to the display
|
// Tx sends data to the display
|
||||||
func (d *Device) Tx(data []byte, isCommand bool) {
|
func (d *Device) Tx(data []byte, isCommand bool) {
|
||||||
if isCommand {
|
d.dcPin.Set(!isCommand)
|
||||||
d.csPin.High()
|
d.bus.Tx(data, nil)
|
||||||
d.dcPin.Low()
|
|
||||||
d.csPin.Low()
|
|
||||||
|
|
||||||
d.bus.Tx(data, nil)
|
|
||||||
d.csPin.High()
|
|
||||||
} else {
|
|
||||||
d.csPin.High()
|
|
||||||
d.dcPin.High()
|
|
||||||
d.csPin.Low()
|
|
||||||
|
|
||||||
d.bus.Tx(data, nil)
|
|
||||||
d.csPin.High()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Size returns the current size of the display.
|
// Size returns the current size of the display.
|
||||||
|
|||||||
+7
-6
@@ -194,12 +194,13 @@ func (d *Device) FillRectangleWithBuffer(x, y, width, height int16, buffer []col
|
|||||||
offset := int32(0)
|
offset := int32(0)
|
||||||
for k > 0 {
|
for k > 0 {
|
||||||
for i := int32(0); i < d.batchLength; i++ {
|
for i := int32(0); i < d.batchLength; i++ {
|
||||||
|
if offset+i < int32(len(buffer)) {
|
||||||
c565 := RGBATo565(buffer[offset+i])
|
c565 := RGBATo565(buffer[offset+i])
|
||||||
c1 := uint8(c565 >> 8)
|
c1 := uint8(c565 >> 8)
|
||||||
c2 := uint8(c565)
|
c2 := uint8(c565)
|
||||||
data[i*2] = c1
|
data[i*2] = c1
|
||||||
data[i*2+1] = c2
|
data[i*2+1] = c2
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if k >= d.batchLength {
|
if k >= d.batchLength {
|
||||||
d.Tx(data, false)
|
d.Tx(data, false)
|
||||||
|
|||||||
@@ -0,0 +1,65 @@
|
|||||||
|
package veml6070
|
||||||
|
|
||||||
|
// I2C addresses and other constants
|
||||||
|
|
||||||
|
const (
|
||||||
|
ADDR_L = 0x38 // 7bit address of the VEML6070 (write, read)
|
||||||
|
ADDR_H = 0x39 // 7bit address of the VEML6070 (read)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Some possible values for resistance value (in ohm) of VEML6070 calibration resistor
|
||||||
|
const (
|
||||||
|
RSET_240K = 240000
|
||||||
|
RSET_270K = 270000
|
||||||
|
RSET_300K = 300000
|
||||||
|
RSET_600K = 600000
|
||||||
|
)
|
||||||
|
|
||||||
|
// Possible values for integration time of VEML6070
|
||||||
|
// (internally represents the config register bit mask)
|
||||||
|
const (
|
||||||
|
IT_HALF = 0x00
|
||||||
|
IT_1 = 0x04
|
||||||
|
IT_2 = 0x08
|
||||||
|
IT_4 = 0x0C
|
||||||
|
)
|
||||||
|
|
||||||
|
// Possible values for UVI (UV index) risk level estimations - the VEML6070 can
|
||||||
|
// only estimate UVI risk levels since it can only sense UVA rays but the vendor
|
||||||
|
// tried to come up with some coarse thresholds, from application notes
|
||||||
|
const (
|
||||||
|
UVI_RISK_LOW = iota
|
||||||
|
UVI_RISK_MODERATE
|
||||||
|
UVI_RISK_HIGH
|
||||||
|
UVI_RISK_VERY_HIGH
|
||||||
|
UVI_RISK_EXTREME
|
||||||
|
)
|
||||||
|
|
||||||
|
// Scale factor in milliseconds / ohm to determine refresh time
|
||||||
|
// (aka sampling time) without IT_FACTOR for any given RSET, from datasheet.
|
||||||
|
// Note: 100.0 milliseconds are applicable for RSET=240 kOhm and IT_FACTOR=1
|
||||||
|
const RSET_TO_REFRESHTIME_SCALE = 100.0 / RSET_240K
|
||||||
|
|
||||||
|
// The refresh time in milliseconds for which NORMALIZED_UVA_SENSITIVITY
|
||||||
|
// is applicable to a step count
|
||||||
|
const NORMALIZED_REFRESHTIME = 100.0
|
||||||
|
|
||||||
|
// The UVA sensitivity in mW/(m*m)/step which is applicable to a step count
|
||||||
|
// normalized to the NORMALIZED_REFRESHTIME, from datasheet for RSET=240 kOhm
|
||||||
|
// and IT_FACTOR=1
|
||||||
|
const NORMALIZED_UVA_SENSITIVITY = 50.0
|
||||||
|
|
||||||
|
// Config register
|
||||||
|
|
||||||
|
// Possible values for shutdown
|
||||||
|
const (
|
||||||
|
CONFIG_SD_DISABLE = 0x00
|
||||||
|
CONFIG_SD_ENABLE = 0x01
|
||||||
|
)
|
||||||
|
|
||||||
|
// Enable / disable
|
||||||
|
const (
|
||||||
|
CONFIG_DEFAULTS = 0x02
|
||||||
|
CONFIG_ENABLE = CONFIG_SD_DISABLE | CONFIG_DEFAULTS
|
||||||
|
CONFIG_DISABLE = CONFIG_SD_ENABLE | CONFIG_DEFAULTS
|
||||||
|
)
|
||||||
@@ -0,0 +1,140 @@
|
|||||||
|
// Package veml6070 provides a driver for the VEML6070 digital UV light sensor
|
||||||
|
// by Vishay.
|
||||||
|
//
|
||||||
|
// Datasheet:
|
||||||
|
// https://www.vishay.com/docs/84277/veml6070.pdf
|
||||||
|
// Application Notes:
|
||||||
|
// https://www.vishay.com/docs/84310/designingveml6070.pdf
|
||||||
|
//
|
||||||
|
package veml6070 // import "tinygo.org/x/drivers/veml6070"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"machine"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Device wraps an I2C connection to a VEML6070 device.
|
||||||
|
type Device struct {
|
||||||
|
bus machine.I2C
|
||||||
|
AddressLow uint16
|
||||||
|
AddressHigh uint16
|
||||||
|
RSET uint32
|
||||||
|
IT uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
// New creates a new VEML6070 connection. The I2C bus must already be
|
||||||
|
// configured.
|
||||||
|
//
|
||||||
|
// This function only creates the Device object, it does not initialize the device.
|
||||||
|
// You must call Configure() first in order to use the device itself.
|
||||||
|
func New(bus machine.I2C) Device {
|
||||||
|
return Device{
|
||||||
|
bus: bus,
|
||||||
|
AddressLow: ADDR_L,
|
||||||
|
AddressHigh: ADDR_H,
|
||||||
|
RSET: RSET_240K,
|
||||||
|
// Note: default to maximum to get as much precision as possible since
|
||||||
|
// raw data values larger than 16 bit can hardly occur with RSET below
|
||||||
|
// 300 kOhm in real world applications. Power saving due to shorter
|
||||||
|
// sampling time might be a reason to reduce this.
|
||||||
|
IT: IT_4,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure sets up the device for communication
|
||||||
|
func (d *Device) Configure() bool {
|
||||||
|
// save power by shutdown as early as possible, also serves as presence test
|
||||||
|
if err := d.disable(); err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadUVALightIntensity returns the UVA light intensity (irradiance)
|
||||||
|
// in milli Watt per square meter (mW/(m*m))
|
||||||
|
func (d *Device) ReadUVALightIntensity() (uint32, error) {
|
||||||
|
var err2 error
|
||||||
|
|
||||||
|
if err := d.enable(); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// wait two times the refresh time to allow completion of a previous cycle
|
||||||
|
// with old settings (worst case)
|
||||||
|
time.Sleep(time.Duration(d.getRefreshTime()) * 2 * time.Millisecond)
|
||||||
|
|
||||||
|
msb, err2 := d.readData(d.AddressHigh)
|
||||||
|
if err2 != nil {
|
||||||
|
return 0, err2
|
||||||
|
}
|
||||||
|
|
||||||
|
lsb, err2 := d.readData(d.AddressLow)
|
||||||
|
if err2 != nil {
|
||||||
|
return 0, err2
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.disable(); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
rawData := (uint32(msb) << 8) | uint32(lsb)
|
||||||
|
|
||||||
|
// normalize raw data (step count sampled in d.getRefreshTime()) into the
|
||||||
|
// linearly scaled normalized data (step count sampled in 100ms) for which
|
||||||
|
// we know the UVA sensitivity
|
||||||
|
normalizedData := float32(rawData) * NORMALIZED_REFRESHTIME / d.getRefreshTime()
|
||||||
|
|
||||||
|
// now we can calculate the absolute UVA power detected combining normalized
|
||||||
|
// data with known UVA sensitivity for this data, from datasheet
|
||||||
|
intensity := normalizedData * NORMALIZED_UVA_SENSITIVITY // mW/(m*m)
|
||||||
|
|
||||||
|
return uint32(intensity + 0.5), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetEstimatedRiskLevel returns estimated risk level from comparing UVA light
|
||||||
|
// intensity values in mW/(m*m) with thresholds calculated from application notes
|
||||||
|
func (d *Device) GetEstimatedRiskLevel(intensity uint32) uint8 {
|
||||||
|
if intensity <= 24888 {
|
||||||
|
return UVI_RISK_LOW
|
||||||
|
} else if intensity <= 49800 {
|
||||||
|
return UVI_RISK_MODERATE
|
||||||
|
} else if intensity <= 66400 {
|
||||||
|
return UVI_RISK_HIGH
|
||||||
|
} else if intensity <= 91288 {
|
||||||
|
return UVI_RISK_VERY_HIGH
|
||||||
|
} else {
|
||||||
|
return UVI_RISK_EXTREME
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Device) disable() error {
|
||||||
|
return d.bus.Tx(uint16(d.AddressLow), []byte{CONFIG_DISABLE}, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Device) enable() error {
|
||||||
|
return d.bus.Tx(uint16(d.AddressLow), []byte{CONFIG_ENABLE | d.IT}, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Device) readData(address uint16) (byte, error) {
|
||||||
|
data := []byte{0}
|
||||||
|
err := machine.I2C0.Tx(address, []byte{}, data)
|
||||||
|
return data[0], err
|
||||||
|
}
|
||||||
|
|
||||||
|
// getRefreshTime returns the refresh time (aka sample time) in milliseconds
|
||||||
|
func (d *Device) getRefreshTime() float32 {
|
||||||
|
var it float32
|
||||||
|
switch d.IT {
|
||||||
|
case IT_HALF:
|
||||||
|
it = 0.5
|
||||||
|
case IT_1:
|
||||||
|
it = 1
|
||||||
|
case IT_2:
|
||||||
|
it = 2
|
||||||
|
case IT_4:
|
||||||
|
it = 4
|
||||||
|
}
|
||||||
|
return float32(d.RSET) * RSET_TO_REFRESHTIME_SCALE * it
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
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.8.0"
|
||||||
Reference in New Issue
Block a user