Compare commits

...

33 Commits

Author SHA1 Message Date
Ron Evans c12f78eb50 Release: v0.9.0
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-12-23 17:09:00 +01:00
Daniel Esteban adb0c2c261 added generic 8bit shift register (#107)
* shifter: added generic 8bit shift register driver with ShiftPin Pin-compatible GPIO interface
2019-12-22 21:32:49 +01:00
Daniel Esteban 18f0722728 Dual stepper (#101)
* easystepper: added _DualDevice_ to move two motors at the same time
2019-12-22 10:33:02 +01:00
BCG e80a22d0ba Client driver for WiFiNINA firmware (#98)
* wifinina: implementation of WiFiNINA driver, including:
   - TCP client example is working
   - reading sockets and mqtt working
   - switched over to common net package also used by espat package
   - smoke tests and updated README for wifinina
2019-12-22 10:25:10 +01:00
BCG cc5ecafacf Decoupled net package from espat 2019-12-17 22:09:23 +01:00
Ayke van Laethem 7b56e61d52 ws2812: add support for simulation
Call a special __tinygo_ws2812_write_byte function to send a single
byte.
2019-12-14 22:16:41 +01:00
Ayke van Laethem 514b436889 ws2812: fix "invalid symbol redefinition" error
Named local labels shouldn't be used in LLVM because they might get
duplicated resulting in multiple symbol definitions and a compiler
error. Instead, use numeric labels.
2019-12-14 22:11:10 +01:00
BCG 7233452819 Added Feather M0 and Trinket M0 to build tags for WS2812 2019-12-12 14:28:21 +01:00
Ayke van Laethem 7929aa10ea all: add md5sum to smoke tests
This makes it easier to quickly compare all smoke tests for any changes.
2019-12-11 22:13:48 +01:00
Daniel Esteban 02084fd8a5 improvements in st7735/st7789 drivers 2019-12-08 16:38:05 +01:00
Jonathan Basseri f822da51fe Add support for Cortex-M4 120MHz
This adjusts the timing for 120MHz as seen on atsamd51 boards. The
timing was kept as close as possible to the Cortex-M4 64MHz logic in
ws2812_m4_64m.go.

Tested: This works on ItsyBitsy-M4
2019-12-06 11:33:55 +01:00
Jonathan Basseri c62d7db35d Fix number in comment
assembly comment was counting the # of nops.
2019-12-06 11:33:55 +01:00
Ron Evans 93372474a2 release: prepare for v0.8.0
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-11-26 20:40:16 +01:00
Ron Evans 7dcbfbecc6 espat: implement MQTT subscribe functionality via blocking select/channels.
also refactor response processing for greater speed and efficiency.

Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-11-25 15:56:09 +01:00
Ayke van Laethem 2e606b090a semihosting: initial implementation of ARM semihosting
Useful for logging output to the host console.
2019-11-06 18:53:33 +01:00
Ron Evans e0cdc931e7 mcp3008: add implementation for MCP3008 ADC with SPI interface
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-10-31 11:49:49 +01:00
Daniel Esteban 130d9de03b Merge pull request #92 from tinygo-org/st7789-buffer-overflow
st7789: fix index out of bounds error
2019-10-28 12:30:44 +01:00
Ayke van Laethem 2c2f1d3db4 st7789: fix index out of bounds error
This commit adds the same check to the st7789 that also exists in the
st7735.
2019-10-28 12:23:56 +01:00
Ron Evans 2413eb86e0 release: update versions to 0.7.0
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-10-17 13:18:24 +02:00
Ron Evans c7555a1469 espat: change all examples to use Arduino Nano33 IoT by default
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-10-17 13:16:39 +02:00
Ron Evans 3fca96e0ef docs: complete missing GoDocs for main and sub-packages
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-10-17 11:50:03 +02:00
Ron Evans c7981f72ec core: add Version string for support purposes
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-10-16 23:23:10 +02:00
Ron Evans 5df157230f lis3dh: example uses I2C1 so requires config to specify pins since they are not default
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-10-16 21:44:58 +02:00
Daniel Esteban bcb291992c ssd1331: make SPI TX faster
same change as PR #89
2019-10-13 02:02:22 +02:00
Daniel Esteban 7b710e3a48 Merge pull request #89 from tinygo-org/st7735-shrink-tx
st7735: make SPI Tx faster
2019-10-11 18:12:20 +02:00
Ayke van Laethem dcfd9c066d st7735: make SPI Tx faster
Most of the ceremony around pin toggling was in fact unnecessary. This
improves performance measurably (rougly 6% in one measurement) and cuts
down on binary size by 80 bytes.
2019-10-10 20:35:50 +02:00
cn 955b3a56e8 veml6070: add Vishay UV light sensor 2019-09-21 10:58:28 +02:00
Ron Evans 21b8d953f4 Update for 0.6.0 release
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-09-09 12:48:57 +02:00
BCG d1b917b835 Support software SPI for APA102 (Itsy Bitsy M0 on-board "Dotstar" LED as example) (#86)
* Added implementation and example to support software-based SPI for APA102, for use with boards like Adafruit Itsy Bitsy M0 for instance.
2019-09-09 12:31:17 +02:00
Ron Evans 2cd73e3204 release: update changelog for 0.5.0
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-08-26 11:34:59 +02:00
Daniel Esteban 3ae5895183 Initial support for LSM6DS3 IMU (#83)
* Initial support for LSM6DS3 IMU
2019-08-26 08:56:48 +02:00
Ayke van Laethem d80f619c9f ws2812: fix timings for the nrf51
The timings needed to be changed slightly for ws2811 chips which are
slightly slower.
2019-08-19 10:55:00 +02:00
Brad Erickson c91888a099 ws2812: Add build tag for Arduino Nano33 IoT
Uses `arduino_nano33` to handle only this board. Other boards with
the same chip will need a separate tag.
2019-08-15 17:38:27 +02:00
55 changed files with 4367 additions and 430 deletions
+60
View File
@@ -1,3 +1,63 @@
0.9.0
---
- **new devices**
- net: shared implementation of net package for serial wifi devices
- shifter: add support for bit Parallel In Serial Out (PISO) shifter
- stepper: add support for dual stepper motor
- wifinina: add implementation for WiFiNINA firmware
- **enhancements**
- st7735: improvements in st7735 driver
- st7789: improvements in st7789 driver
- ws2812: add support for 120Mhz Cortex-M4
- ws2812: added Feather M0 and Trinket M0 to build tags for WS2812
- ws2812: add support for simulation
- **bugfixes**
- ws2812: fix "invalid symbol redefinition" error
- **examples**
- Add examples for wifinina drivers
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
---
- **new devices**
- LSM6DS3 accelerometer
- **bugfixes**
- ws2812: fix timings for the nrf51
- **enhancements**
- ws2812: Add build tag for Arduino Nano33 IoT
0.4.0
---
- **new devices**
+92 -39
View File
@@ -9,44 +9,97 @@ fmt-check:
smoke-test:
@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/apa102/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/blinkm/main.go
tinygo build -size short -o ./build/test.elf -target=itsybitsy-m0 ./examples/bmp180/main.go
tinygo build -size short -o ./build/test.elf -target=bluepill ./examples/ds1307/sram/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=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=itsybitsy-m0 ./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=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=microbit ./examples/hd44780/customchar/main.go
tinygo build -size short -o ./build/test.elf -target=microbit ./examples/hd44780/text/main.go
tinygo build -size short -o ./build/test.elf -target=microbit ./examples/hub75/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=itsybitsy-m0 ./examples/mag3110/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/mpu6050/main.go
tinygo build -size short -o ./build/test.elf -target=microbit ./examples/pcd8544/setbuffer/main.go
tinygo build -size short -o ./build/test.elf -target=microbit ./examples/pcd8544/setpixel/main.go
tinygo build -size short -o ./build/test.elf -target=microbit ./examples/sht3x/main.go
tinygo build -size short -o ./build/test.elf -target=microbit ./examples/ssd1306/i2c_128x32/main.go
tinygo build -size short -o ./build/test.elf -target=microbit ./examples/ssd1306/spi_128x64/main.go
tinygo build -size short -o ./build/test.elf -target=microbit ./examples/ssd1331/main.go
tinygo build -size short -o ./build/test.elf -target=microbit ./examples/st7735/main.go
tinygo build -size short -o ./build/test.elf -target=microbit ./examples/st7789/main.go
tinygo build -size short -o ./build/test.elf -target=circuitplay-express ./examples/thermistor/main.go
tinygo build -size short -o ./build/test.elf -target=itsybitsy-m0 ./examples/vl53l1x/main.go
tinygo build -size short -o ./build/test.elf -target=microbit ./examples/waveshare-epd/epd2in13/main.go
tinygo build -size short -o ./build/test.elf -target=microbit ./examples/waveshare-epd/epd2in13x/main.go
tinygo build -size short -o ./build/test.elf -target=circuitplay-express ./examples/ws2812/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/buzzer/main.go
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/adxl345/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/apa102/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/apa102/itsybitsy-m0/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/at24cx/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/bh1750/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/blinkm/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/bmp180/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=bluepill ./examples/ds1307/sram/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=bluepill ./examples/ds1307/time/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/ds3231/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/easystepper/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/espat/espconsole/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/espat/esphub/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/espat/espstation/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=feather-m0 ./examples/gps/i2c/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=feather-m0 ./examples/gps/uart/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/hd44780/customchar/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/hd44780/text/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/hub75/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=circuitplay-express ./examples/lis3dh/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/lsm6ds3/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/mag3110/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/mcp3008/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/microbitmatrix/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/mma8653/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/mpu6050/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/pcd8544/setbuffer/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/pcd8544/setpixel/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=pybadge ./examples/shifter/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/sht3x/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/ssd1306/i2c_128x32/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/ssd1306/spi_128x64/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/ssd1331/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/st7735/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/st7789/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=circuitplay-express ./examples/thermistor/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/vl53l1x/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/waveshare-epd/epd2in13/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/waveshare-epd/epd2in13x/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/wifinina/tcpclient/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/wifinina/webclient/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=circuitplay-express ./examples/ws2812/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=trinket-m0 ./examples/bme280/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=circuitplay-express ./examples/microphone/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=circuitplay-express ./examples/buzzer/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=trinket-m0 ./examples/veml6070/main.go
@md5sum ./build/test.hex
test: clean fmt-check smoke-test
+7
View File
@@ -52,6 +52,8 @@ func main() {
## Currently supported devices
The following 34 devices are supported.
| Device Name | Interface Type |
|----------|-------------|
| [ADXL345 accelerometer](http://www.analog.com/media/en/technical-documentation/data-sheets/ADXL345.pdf) | I2C |
@@ -65,22 +67,27 @@ func main() {
| [DS1307 real time clock](https://datasheets.maximintegrated.com/en/ds/DS1307.pdf) | I2C |
| [DS3231 real time clock](https://datasheets.maximintegrated.com/en/ds/DS3231.pdf) | I2C |
| ["Easystepper" stepper motor controller](https://en.wikipedia.org/wiki/Stepper_motor) | GPIO |
| [ESP32 as WiFi Coprocessor with Arduino nina-fw](https://github.com/arduino/nina-fw) | SPI |
| [ESP8266/ESP32 AT Command set for WiFi/TCP/UDP](https://github.com/espressif/esp32-at) | UART |
| [GPS module](https://www.u-blox.com/en/product/neo-6-series) | I2C/UART |
| [HUB75 RGB led matrix](https://cdn-learn.adafruit.com/downloads/pdf/32x16-32x32-rgb-led-matrix.pdf) | SPI |
| [LIS3DH accelerometer](https://www.st.com/resource/en/datasheet/lis3dh.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 |
| [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 |
| [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 |
| [MPU6050 accelerometer/gyroscope](https://store.invensense.com/datasheets/invensense/MPU-6050_DataSheet_V3%204.pdf) | I2C |
| [PCD8544 display](http://eia.udg.edu/~forest/PCD8544_1.pdf) | SPI |
| [Shift register](https://en.wikipedia.org/wiki/Shift_register#Parallel-in_serial-out_\(PISO\)) | GPIO |
| [SHT3x Digital Humidity Sensor](https://www.sensirion.com/fileadmin/user_upload/customers/sensirion/Dokumente/0_Datasheets/Humidity/Sensirion_Humidity_Sensors_SHT3x_Datasheet_digital.pdf) | I2C |
| [SSD1306 OLED display](https://cdn-shop.adafruit.com/datasheets/SSD1306.pdf) | I2C / SPI |
| [SSD1331 TFT color display](https://www.crystalfontz.com/controllers/SolomonSystech/SSD1331/381/) | 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 |
| [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 |
| [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 |
+15 -2
View File
@@ -21,15 +21,28 @@ const (
// Device wraps APA102 SPI LEDs.
type Device struct {
bus machine.SPI
bus SPI
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.
func New(b machine.SPI) Device {
func New(b SPI) Device {
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.
// 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) {
+68
View File
@@ -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
View File
@@ -1,5 +1,5 @@
// Package drivers provides a collection of hardware drivers for devices that
// can be used together with TinyGo (https://tinygo.org).
// Package drivers provides a collection of hardware drivers for TinyGo (https://tinygo.org)
// for devices such as sensors and displays.
//
// Here is an example in TinyGo that uses the BMP180 digital barometer:
//
@@ -9,7 +9,7 @@
// "time"
// "machine"
//
// "github.com/tinygo-org/drivers/bmp180"
// "tinygo.org/x/drivers/bmp180"
// )
//
// func main() {
+101 -19
View File
@@ -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"
import (
@@ -10,47 +10,117 @@ import (
type Device struct {
pins [4]machine.Pin
stepDelay int32
stepNumber int32
stepNumber uint8
}
// DualDevice holds information for controlling 2 motors
type DualDevice struct {
devices [2]Device
}
// New returns a new easystepper driver given 4 pins numbers (not pin object),
// number of steps and rpm
func New(pin1, pin2, pin3, pin4 machine.Pin, steps int32, rpm int32) Device {
pin1.Configure(machine.PinConfig{Mode: machine.PinOutput})
pin2.Configure(machine.PinConfig{Mode: machine.PinOutput})
pin3.Configure(machine.PinConfig{Mode: machine.PinOutput})
pin4.Configure(machine.PinConfig{Mode: machine.PinOutput})
return Device{
pins: [4]machine.Pin{pin1, pin2, pin3, pin4},
stepDelay: 60000000 / (steps * rpm),
}
}
// Configure configures the pins of the Device
func (d *Device) Configure() {
for _, pin := range d.pins {
pin.Configure(machine.PinConfig{Mode: machine.PinOutput})
}
}
// NewDual returns a new dual easystepper driver given 8 pins numbers (not pin object),
// number of steps and rpm
func NewDual(pin1, pin2, pin3, pin4, pin5, pin6, pin7, pin8 machine.Pin, steps int32, rpm int32) DualDevice {
var dual DualDevice
dual.devices[0] = Device{
pins: [4]machine.Pin{pin1, pin2, pin3, pin4},
stepDelay: 60000000 / (steps * rpm),
}
dual.devices[1] = Device{
pins: [4]machine.Pin{pin5, pin6, pin7, pin8},
stepDelay: 60000000 / (steps * rpm),
}
return dual
}
// Configure configures the pins of the DualDevice
func (d *DualDevice) Configure() {
d.devices[0].Configure()
d.devices[1].Configure()
}
// Move rotates the motor the number of given steps
// (negative steps will rotate it the opposite direction)
func (d *Device) Move(steps int32) {
direction := steps > 0
if steps < 0 {
steps = -steps - d.stepNumber
} else {
steps += d.stepNumber
steps = -steps
}
var stepN int8
steps += int32(d.stepNumber)
var s int32
for s = d.stepNumber; s < steps; s++ {
d.stepMotor(d.stepNumber)
for s = int32(d.stepNumber); s < steps; s++ {
time.Sleep(time.Duration(d.stepDelay) * time.Microsecond)
if direction {
stepN = int8(s % 4)
} else {
stepN = int8((s + 2*(s%2)) % 4)
}
d.stepMotor(stepN)
d.moveDirectionSteps(direction, s)
}
d.stepNumber = int32(stepN)
}
// Off turns off all motor pins
func (d *Device) Off() {
for _, pin := range d.pins {
pin.Low()
}
}
// Move rotates the motors the number of given steps
// (negative steps will rotate it the opposite direction)
func (d *DualDevice) Move(stepsA, stepsB int32) {
min := uint8(1)
max := uint8(0)
var directions [2]bool
var minStep int32
directions[0] = stepsA > 0
directions[1] = stepsB > 0
if stepsA < 0 {
stepsA = -stepsA
}
if stepsB < 0 {
stepsB = -stepsB
}
if stepsB > stepsA {
stepsA, stepsB = stepsB, stepsA
max, min = min, max
}
d.devices[0].stepMotor(d.devices[0].stepNumber)
d.devices[1].stepMotor(d.devices[1].stepNumber)
stepsA += int32(d.devices[max].stepNumber)
minStep = int32(d.devices[min].stepNumber)
for s := int32(d.devices[max].stepNumber); s < stepsA; s++ {
time.Sleep(time.Duration(d.devices[0].stepDelay) * time.Microsecond)
d.devices[max].moveDirectionSteps(directions[max], s)
if ((s * stepsB) / stepsA) > minStep {
minStep++
d.devices[min].moveDirectionSteps(directions[min], minStep)
}
}
}
// Off turns off all motor pins
func (d *DualDevice) Off() {
d.devices[0].Off()
d.devices[1].Off()
}
// stepMotor changes the pins' state to the correct step
func (d *Device) stepMotor(step int8) {
func (d *Device) stepMotor(step uint8) {
switch step {
case 0:
d.pins[0].High()
@@ -77,4 +147,16 @@ func (d *Device) stepMotor(step int8) {
d.pins[3].High()
break
}
d.stepNumber = step
}
// moveDirectionSteps uses the direction to calculate the correct step and change the motor to it.
// Direction true: 0, 1, 2, 3, 0, 1, 2, ...
// Direction false: 0, 3, 2, 1, 0, 3, 2, ...
func (d *Device) moveDirectionSteps(direction bool, step int32) {
if direction {
d.stepMotor(uint8(step % 4))
} else {
d.stepMotor(uint8((step + 2*(step%2)) % 4))
}
}
+58 -95
View File
@@ -19,10 +19,13 @@
package espat // import "tinygo.org/x/drivers/espat"
import (
"errors"
"machine"
"strconv"
"strings"
"time"
"tinygo.org/x/drivers/net"
)
// Device wraps UART connection to the ESP8266/ESP32.
@@ -47,6 +50,7 @@ func New(b machine.UART) *Device {
// Configure sets up the device for communication.
func (d Device) Configure() {
ActiveDevice = &d
net.ActiveDevice = ActiveDevice
}
// Connected checks if there is communication with the ESP8266/ESP32.
@@ -54,11 +58,11 @@ func (d *Device) Connected() bool {
d.Execute(Test)
// handle response here, should include "OK"
r := d.Response(100)
if strings.Contains(string(r), "OK") {
return true
_, err := d.Response(100)
if err != nil {
return false
}
return false
return true
}
// Write raw bytes to the UART.
@@ -72,7 +76,7 @@ func (d *Device) Read(b []byte) (n int, err error) {
}
// 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.
func (d Device) Execute(cmd string) error {
@@ -97,7 +101,11 @@ func (d Device) Set(cmd, params string) error {
// Version returns the ESP8266/ESP32 firmware version info.
func (d Device) Version() []byte {
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.
@@ -122,7 +130,7 @@ func (d Device) Reset() {
// ReadSocket returns the data that has already been read in from the responses.
func (d *Device) ReadSocket(b []byte) (n int, err error) {
// make sure no data in buffer
d.Response(100)
d.Response(300)
count := len(b)
if len(b) >= len(d.socketdata) {
@@ -142,118 +150,73 @@ func (d *Device) ReadSocket(b []byte) (n int, err error) {
// Response gets the next response bytes from the ESP8266/ESP32.
// The call will retry for up to timeout milliseconds before returning nothing.
func (d *Device) Response(timeout int) []byte {
var i int
pause := 10 // pause to wait for 10 ms
func (d *Device) Response(timeout int) ([]byte, error) {
// read data
var size int
var start, end int
pause := 100 // pause to wait for 100 ms
retries := timeout / pause
header := make([]byte, 2)
for {
for d.bus.Buffered() > 0 {
// get the first 2 bytes
header[0], _ = d.bus.ReadByte()
header[1], _ = d.bus.ReadByte()
size = d.bus.Buffered()
if d.isLeadingCRLF(header) {
// skip it
header[0], _ = d.bus.ReadByte()
header[1], _ = d.bus.ReadByte()
if size > 0 {
end += size
d.bus.Read(d.response[start:end])
// 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) {
// is socket data packet
d.parseIPD()
} else {
// no, so put into response
d.response[i] = header[0]
i++
d.response[i] = header[1]
i++
// if "OK" then the command worked
if strings.Contains(string(d.response[:end]), "OK") {
return d.response[start:end], nil
}
// read the rest of normal command response
for d.bus.Buffered() > 0 {
data, err := d.bus.ReadByte()
if err != nil {
return nil
}
d.response[i] = data
i++
// if "Error" then the command failed
if strings.Contains(string(d.response[:end]), "ERROR") {
return d.response[start:end], errors.New("response error:" + string(d.response[start:end]))
}
// if anything else, then keep reading data in?
start = end
}
// wait longer?
retries--
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)
}
return d.response[:i]
}
func (d *Device) isLeadingCRLF(b []byte) bool {
if len(b) < 2 {
return false
}
if b[0] == 13 && b[1] == 10 {
return true
}
return false
}
func (d *Device) parseIPD(end int) error {
// find the "+IPD," to get length
s := strings.Index(string(d.response[:end]), "+IPD,")
func (d *Device) isIPD(b []byte) bool {
if len(b) < 2 {
return false
}
if b[0] == '+' && b[1] == 'I' {
return true
}
return false
}
// find the ":"
e := strings.Index(string(d.response[:end]), ":")
func (d *Device) parseIPD() bool {
data, _ := d.bus.ReadByte()
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
}
// find the data length
val := string(d.response[s+5 : e])
// get the expected data length
// skip remaining header up to the ":"
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)
// TODO: verify count
_, err := strconv.Atoi(val)
if err != nil {
// not expected data here. what to do?
return false
return err
}
// load up the socket data
// only read the expected amount of data
for m := 0; m < count; m++ {
data, _ = d.bus.ReadByte()
d.socketdata = append(d.socketdata, data)
}
return true
d.socketdata = append(d.socketdata, d.response[e+1:end]...)
return nil
}
// IsSocketDataAvailable returns of there is socket data available
func (d *Device) IsSocketDataAvailable() bool {
return len(d.socketdata) > 0 || d.bus.Buffered() > 0
}
+48 -26
View File
@@ -17,7 +17,14 @@ const (
// GetDNS returns the IP address for a domain name.
func (d *Device) GetDNS(domain string) (string, error) {
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 {
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 {
protocol := "TCP"
val := "\"" + protocol + "\",\"" + addr + "\"," + port + ",120"
d.Set(TCPConnect, val)
r := d.Response(1000)
if strings.Contains(string(r), "OK") {
return nil
err := d.Set(TCPConnect, val)
if err != nil {
return err
}
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.
func (d *Device) ConnectUDPSocket(addr, sendport, listenport string) error {
protocol := "UDP"
val := "\"" + protocol + "\",\"" + addr + "\"," + sendport + "," + listenport + ",2"
d.Set(TCPConnect, val)
r := d.Response(pause)
if strings.Contains(string(r), "OK") {
return nil
err := d.Set(TCPConnect, val)
if err != nil {
return err
}
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.
@@ -57,17 +70,23 @@ func (d *Device) ConnectSSLSocket(addr, port string) error {
val := "\"" + protocol + "\",\"" + addr + "\"," + port + ",120"
d.Set(TCPConnect, val)
// this operation takes longer, so wait up to 6 seconds to complete.
r := d.Response(6000)
if strings.Contains(string(r), "CONNECT") {
return nil
_, err := d.Response(6000)
if err != nil {
return err
}
return errors.New("ConnectSSLSocket error:" + string(r))
return nil
}
// DisconnectSocket disconnects the ESP8266/ESP32 from the current TCP/UDP connection.
func (d *Device) DisconnectSocket() error {
d.Execute(TCPClose)
d.Response(pause)
err := d.Execute(TCPClose)
if err != nil {
return err
}
_, e := d.Response(pause)
if e != nil {
return e
}
return nil
}
@@ -76,14 +95,14 @@ func (d *Device) DisconnectSocket() error {
func (d *Device) SetMux(mode int) error {
val := strconv.Itoa(mode)
d.Set(TCPMultiple, val)
d.Response(pause)
return nil
_, err := d.Response(pause)
return err
}
// GetMux returns the ESP8266/ESP32 current client TCP/UDP configuration for concurrent connections.
func (d *Device) GetMux() ([]byte, error) {
d.Query(TCPMultiple)
return d.Response(pause), nil
return d.Response(pause)
}
// 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 {
val := strconv.Itoa(mode)
d.Set(TransmissionMode, val)
d.Response(pause)
return nil
_, err := d.Response(pause)
return err
}
// 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)
return d.Response(pause)
}
@@ -108,7 +127,10 @@ func (d *Device) StartSocketSend(size int) error {
// when ">" is received, it indicates
// ready to receive data
r := d.Response(pause)
r, err := d.Response(2000)
if err != nil {
return err
}
if strings.Contains(string(r), ">") {
return nil
}
@@ -120,6 +142,6 @@ func (d *Device) StartSocketSend(size int) error {
func (d *Device) EndSocketSend() error {
d.Write([]byte("+++"))
d.Response(pause)
return nil
_, err := d.Response(pause)
return err
}
+40 -30
View File
@@ -16,7 +16,7 @@ const (
)
// GetWifiMode returns the ESP8266/ESP32 wifi mode.
func (d *Device) GetWifiMode() []byte {
func (d *Device) GetWifiMode() ([]byte, error) {
d.Query(WifiMode)
return d.Response(100)
}
@@ -25,14 +25,14 @@ func (d *Device) GetWifiMode() []byte {
func (d *Device) SetWifiMode(mode int) error {
val := strconv.Itoa(mode)
d.Set(WifiMode, val)
d.Response(pause)
return nil
_, err := d.Response(pause)
return err
}
// Wifi 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)
return d.Response(100)
}
@@ -42,37 +42,43 @@ func (d *Device) GetConnectedAP() []byte {
func (d *Device) ConnectToAP(ssid, pwd string, ws int) error {
val := "\"" + ssid + "\",\"" + pwd + "\""
d.Set(ConnectAP, val)
d.Response(ws * 1000)
_, err := d.Response(ws * 1000)
if err != nil {
return err
}
return nil
}
// DisconnectFromAP disconnects the ESP8266/ESP32 from the current access point.
func (d *Device) DisconnectFromAP() error {
d.Execute(Disconnect)
d.Response(1000)
return nil
_, err := d.Response(1000)
return err
}
// 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)
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.
func (d *Device) SetClientIP(ipaddr string) []byte {
func (d *Device) SetClientIP(ipaddr string) error {
val := "\"" + ipaddr + "\""
d.Set(ConnectAP, val)
d.Response(500)
return nil
_, err := d.Response(500)
return err
}
// 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)
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.
@@ -83,35 +89,38 @@ func (d *Device) SetAPConfig(ssid, pwd string, ch, security int) error {
ecnval := strconv.Itoa(security)
val := "\"" + ssid + "\",\"" + pwd + "\"," + chval + "," + ecnval
d.Set(SoftAPConfigCurrent, val)
d.Response(1000)
return nil
_, err := d.Response(1000)
return err
}
// 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)
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.
func (d *Device) GetAPIP() string {
func (d *Device) GetAPIP() (string, error) {
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.
func (d *Device) SetAPIP(ipaddr string) error {
val := "\"" + ipaddr + "\""
d.Set(SetSoftAPIPCurrent, val)
d.Response(500)
return nil
_, err := d.Response(500)
return err
}
// GetAPConfigFlash returns the ESP8266/ESP32 current configuration acting as an Access Point
// 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)
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,
@@ -123,15 +132,16 @@ func (d *Device) SetAPConfigFlash(ssid, pwd string, ch, security int) error {
ecnval := strconv.Itoa(security)
val := "\"" + ssid + "\",\"" + pwd + "\"," + chval + "," + ecnval
d.Set(SoftAPConfigFlash, val)
d.Response(1000)
return nil
_, err := d.Response(1000)
return err
}
// GetAPIPFlash returns the ESP8266/ESP32 IP address as saved to flash storage.
// 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)
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.
@@ -139,6 +149,6 @@ func (d *Device) GetAPIPFlash() string {
func (d *Device) SetAPIPFlash(ipaddr string) error {
val := "\"" + ipaddr + "\""
d.Set(SetSoftAPIPFlash, val)
d.Response(500)
return nil
_, err := d.Response(500)
return err
}
+84
View File
@@ -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
}
+1
View File
@@ -9,6 +9,7 @@ import (
func main() {
motor := easystepper.New(machine.P13, machine.P15, machine.P14, machine.P16, 200, 75)
motor.Configure()
for {
println("CLOCKWISE")
+47 -31
View File
@@ -23,11 +23,12 @@ const actAsAP = false
const ssid = "YOURSSID"
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
var (
uart = machine.UART1
tx = machine.D10
rx = machine.D11
tx = machine.PA22
rx = machine.PA23
console = machine.UART0
@@ -42,28 +43,20 @@ func main() {
adaptor.Configure()
// first check if connected
if adaptor.Connected() {
if connectToESP() {
println("Connected to wifi adaptor.")
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 {
provideAP()
} else {
connectToAP()
}
console.Write([]byte("Type an AT command then press enter:\r\n"))
prompt()
connectToAP()
} else {
console.Write([]byte("\r\n"))
console.Write([]byte("Unable to connect to wifi adaptor.\r\n"))
println("")
failMessage("Unable to connect to wifi adaptor.")
return
}
println("Type an AT command then press enter:")
prompt()
input := make([]byte, 64)
i := 0
for {
@@ -81,7 +74,8 @@ func main() {
adaptor.Write(input[:i+2])
// display response
console.Write(adaptor.Response(100))
r, _ := adaptor.Response(500)
console.Write(r)
// prompt
prompt()
@@ -100,28 +94,50 @@ func main() {
}
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
func connectToAP() {
console.Write([]byte("Connecting to wifi network...\r\n"))
println("Connecting to wifi network '" + ssid + "'")
adaptor.SetWifiMode(espat.WifiModeClient)
adaptor.ConnectToAP(ssid, pass, 10)
console.Write([]byte("Connected.\r\n"))
console.Write([]byte(adaptor.GetClientIP()))
console.Write([]byte("\r\n"))
println("Connected.")
ip, err := adaptor.GetClientIP()
if err != nil {
failMessage(err.Error())
}
println(ip)
}
// provide access point
func provideAP() {
time.Sleep(500 * time.Millisecond)
console.Write([]byte("Starting wifi network as access point '"))
console.Write([]byte(ssid))
console.Write([]byte("'...\r\n"))
println("Starting wifi network as access point '" + ssid + "'...")
adaptor.SetWifiMode(espat.WifiModeAP)
adaptor.SetAPConfig(ssid, pass, 7, espat.WifiAPSecurityWPA2_PSK)
console.Write([]byte("Ready.\r\n"))
console.Write([]byte(adaptor.GetAPIP()))
console.Write([]byte("\r\n"))
println("Ready.")
ip, _ := adaptor.GetAPIP()
println(ip)
}
func failMessage(msg string) {
for {
println(msg)
time.Sleep(1 * time.Second)
}
}
+39 -15
View File
@@ -11,7 +11,7 @@ import (
"time"
"tinygo.org/x/drivers/espat"
"tinygo.org/x/drivers/espat/net"
"tinygo.org/x/drivers/net"
)
// change actAsAP to true to act as an access point instead of connecting to one.
@@ -21,11 +21,12 @@ const actAsAP = false
const ssid = "YOURSSID"
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
var (
uart = machine.UART1
tx = machine.D10
rx = machine.D11
tx = machine.PA22
rx = machine.PA23
adaptor *espat.Device
)
@@ -42,17 +43,14 @@ func main() {
readyled.High()
// first check if connected
if adaptor.Connected() {
if connectToESP() {
println("Connected to wifi adaptor.")
adaptor.Echo(false)
if actAsAP {
provideAP()
} else {
connectToAP()
}
connectToAP()
} else {
println("Unable to connect to wifi adaptor.")
println("")
failMessage("Unable to connect to wifi adaptor.")
return
}
@@ -85,21 +83,47 @@ func main() {
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
func connectToAP() {
println("Connecting to wifi network...")
println("Connecting to wifi network '" + ssid + "'")
adaptor.SetWifiMode(espat.WifiModeClient)
adaptor.ConnectToAP(ssid, pass, 10)
println("Connected.")
println(adaptor.GetClientIP())
ip, err := adaptor.GetClientIP()
if err != nil {
failMessage(err.Error())
}
println(ip)
}
// provide access point
func provideAP() {
println("Starting wifi network as access point:")
println(ssid)
println("Starting wifi network as access point '" + ssid + "'...")
adaptor.SetWifiMode(espat.WifiModeAP)
adaptor.SetAPConfig(ssid, pass, 7, espat.WifiAPSecurityWPA2_PSK)
println("Ready.")
println(adaptor.GetAPIP())
ip, _ := adaptor.GetAPIP()
println(ip)
}
func failMessage(msg string) {
for {
println(msg)
time.Sleep(1 * time.Second)
}
}
+35 -7
View File
@@ -11,7 +11,7 @@ import (
"time"
"tinygo.org/x/drivers/espat"
"tinygo.org/x/drivers/espat/net"
"tinygo.org/x/drivers/net"
)
// access point info
@@ -21,11 +21,12 @@ const pass = "YOURPASS"
// IP address of the listener aka "hub". Replace with your own info.
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
var (
uart = machine.UART1
tx = machine.D10
rx = machine.D11
tx = machine.PA22
rx = machine.PA23
adaptor *espat.Device
)
@@ -38,13 +39,14 @@ func main() {
adaptor.Configure()
// first check if connected
if adaptor.Connected() {
if connectToESP() {
println("Connected to wifi adaptor.")
adaptor.Echo(false)
connectToAP()
} else {
println("Unable to connect to wifi adaptor.")
println("")
failMessage("Unable to connect to wifi adaptor.")
return
}
@@ -70,11 +72,37 @@ func main() {
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
func connectToAP() {
println("Connecting to wifi network...")
println("Connecting to wifi network '" + ssid + "'")
adaptor.SetWifiMode(espat.WifiModeClient)
adaptor.ConnectToAP(ssid, pass, 10)
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)
}
}
+17 -10
View File
@@ -17,7 +17,7 @@ import (
"time"
"tinygo.org/x/drivers/espat"
"tinygo.org/x/drivers/espat/mqtt"
"tinygo.org/x/drivers/net/mqtt"
)
// access point info
@@ -25,14 +25,16 @@ 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"
const server = "tcp://test.mosquitto.org:1883"
//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
var (
uart = machine.UART1
tx = machine.D10
rx = machine.D11
uart = machine.UART2
tx = machine.PA22
rx = machine.PA23
console = machine.UART0
@@ -62,10 +64,10 @@ func main() {
return
}
opts := mqtt.NewClientOptions(adaptor)
opts := mqtt.NewClientOptions()
opts.AddBroker(server).SetClientID("tinygo-client-" + randomString(10))
println("Connectng to MQTT...")
println("Connecting to MQTT broker at", server)
cl := mqtt.NewClient(opts)
if token := cl.Connect(); token.Wait() && token.Error() != nil {
failMessage(token.Error().Error())
@@ -104,13 +106,18 @@ func connectToESP() bool {
// connect to access point
func connectToAP() {
println("Connecting to wifi network...")
println("Connecting to wifi network '" + ssid + "'")
adaptor.SetWifiMode(espat.WifiModeClient)
adaptor.ConnectToAP(ssid, pass, 10)
println("Connected.")
println(adaptor.GetClientIP())
ip, err := adaptor.GetClientIP()
if err != nil {
failMessage(err.Error())
}
println(ip)
}
// Returns an int >= min, < max
+163
View File
@@ -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/net/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()
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)
}
}
+37 -6
View File
@@ -11,7 +11,7 @@ import (
"time"
"tinygo.org/x/drivers/espat"
"tinygo.org/x/drivers/espat/net"
"tinygo.org/x/drivers/net"
)
// access point info
@@ -21,6 +21,7 @@ const pass = "YOURPASS"
// IP address of the server aka "hub". Replace with your own info.
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
var (
uart = machine.UART1
@@ -38,13 +39,14 @@ func main() {
adaptor.Configure()
// first check if connected
if adaptor.Connected() {
if connectToESP() {
println("Connected to wifi adaptor.")
adaptor.Echo(false)
connectToAP()
} else {
println("Unable to connect to wifi adaptor.")
println("")
failMessage("Unable to connect to wifi adaptor.")
return
}
@@ -54,7 +56,10 @@ func main() {
laddr := &net.TCPAddr{Port: 8080}
println("Dialing TCP connection...")
conn, _ := net.DialTCP("tcp", laddr, raddr)
conn, err := net.DialTCP("tcp", laddr, raddr)
if err != nil {
failMessage(err.Error())
}
for {
// send data
@@ -70,11 +75,37 @@ func main() {
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
func connectToAP() {
println("Connecting to wifi network...")
println("Connecting to wifi network '" + ssid + "'")
adaptor.SetWifiMode(espat.WifiModeClient)
adaptor.ConnectToAP(ssid, pass, 10)
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)
}
}
+1 -1
View File
@@ -11,7 +11,7 @@ import (
var i2c = machine.I2C1
func main() {
i2c.Configure(machine.I2CConfig{})
i2c.Configure(machine.I2CConfig{SCL: machine.SCL1_PIN, SDA: machine.SDA1_PIN})
accel := lis3dh.New(i2c)
accel.Address = lis3dh.Address1 // address on the Circuit Playground Express
+30
View File
@@ -0,0 +1,30 @@
// Connects to an LSM6DS3 I2C a 6 axis Inertial Measurement Unit (IMU)
package main
import (
"machine"
"time"
"tinygo.org/x/drivers/lsm6ds3"
)
func main() {
machine.I2C0.Configure(machine.I2CConfig{})
accel := lsm6ds3.New(machine.I2C0)
accel.Configure(lsm6ds3.Configuration{})
if !accel.Connected() {
println("LSM6DS3 not connected")
return
}
for {
x, y, z := accel.ReadAcceleration()
println("Acceleration:", float32(x)/1000000, float32(y)/1000000, float32(z)/1000000)
x, y, z = accel.ReadRotation()
println("Gyroscope:", float32(x)/1000000, float32(y)/1000000, float32(z)/1000000)
x, _ = accel.ReadTemperature()
println("Degrees C", float32(x)/1000, "\n\n")
time.Sleep(time.Millisecond * 1000)
}
}
+32
View File
@@ -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)
}
}
+50
View File
@@ -0,0 +1,50 @@
package main
import (
"machine"
"time"
"tinygo.org/x/drivers/shifter"
)
func main() {
buttons := shifter.New(shifter.EIGHT_BITS, machine.BUTTON_LATCH, machine.BUTTON_CLK, machine.BUTTON_OUT)
buttons.Configure()
for {
// Slower
for i := 0; i < 8; i++ {
if buttons.Pins[i].Get() {
println("Button", i, "pressed")
}
}
// Faster
pressed, _ := buttons.Read8Input()
if pressed&machine.BUTTON_LEFT_MASK > 0 {
println("Button LEFT pressed")
}
if pressed&machine.BUTTON_UP_MASK > 0 {
println("Button UP pressed")
}
if pressed&machine.BUTTON_DOWN_MASK > 0 {
println("Button DOWN pressed")
}
if pressed&machine.BUTTON_RIGHT_MASK > 0 {
println("Button RIGHT pressed")
}
if pressed&machine.BUTTON_SELECT_MASK > 0 {
println("Button SELECT pressed")
}
if pressed&machine.BUTTON_START_MASK > 0 {
println("Button START pressed")
}
if pressed&machine.BUTTON_A_MASK > 0 {
println("Button A pressed")
}
if pressed&machine.BUTTON_B_MASK > 0 {
println("Button B pressed")
}
time.Sleep(100 * time.Millisecond)
}
}
+41
View File
@@ -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)
}
}
+149
View File
@@ -0,0 +1,149 @@
// 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 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/net/mqtt"
"tinygo.org/x/drivers/wifinina"
)
// access point info
const ssid = ""
const pass = ""
// 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"
// these are the default pins for the Arduino Nano33 IoT.
// change these to connect to a different UART or pins for the ESP8266/ESP32
var (
// these are the default pins for the Arduino Nano33 IoT.
uart = machine.UART2
tx = machine.NINA_TX
rx = machine.NINA_RX
spi = machine.NINA_SPI
// this is the ESP chip that has the WIFININA firmware flashed on it
adaptor = &wifinina.Device{
SPI: spi,
CS: machine.NINA_CS,
ACK: machine.NINA_ACK,
GPIO0: machine.NINA_GPIO0,
RESET: machine.NINA_RESETN,
}
console = machine.UART0
topic = "tinygo"
)
func main() {
time.Sleep(3000 * time.Millisecond)
uart.Configure(machine.UARTConfig{TX: tx, RX: rx})
rand.Seed(time.Now().UnixNano())
// Configure SPI for 8Mhz, Mode 0, MSB First
spi.Configure(machine.SPIConfig{
Frequency: 8 * 1e6,
MOSI: machine.NINA_MOSI,
MISO: machine.NINA_MISO,
SCK: machine.NINA_SCK,
})
// Init esp8266/esp32
adaptor.Configure()
connectToAP()
opts := mqtt.NewClientOptions()
opts.AddBroker(server).SetClientID("tinygo-client-" + randomString(10))
println("Connectng to MQTT...")
cl := mqtt.NewClient(opts)
if token := cl.Connect(); token.Wait() && token.Error() != nil {
failMessage(token.Error().Error())
}
for i := 0; ; i++ {
println("Publishing MQTT message...")
data := []byte(fmt.Sprintf(`{"e":[{"n":"hello %d","v":101}]}`, i))
token := cl.Publish(topic, 0, false, data)
token.Wait()
if err := token.Error(); err != nil {
switch t := err.(type) {
case wifinina.Error:
println(t.Error(), "attempting to reconnect")
if token := cl.Connect(); token.Wait() && token.Error() != nil {
failMessage(token.Error().Error())
}
default:
println(err.Error())
}
}
time.Sleep(1 * time.Millisecond)
}
// Right now this code is never reached. Need a way to trigger it...
println("Disconnecting MQTT...")
cl.Disconnect(100)
println("Done.")
}
// connect to access point
func connectToAP() {
time.Sleep(2 * time.Second)
println("Connecting to " + ssid)
adaptor.SetPassphrase(ssid, pass)
for st, _ := adaptor.GetConnectionStatus(); st != wifinina.StatusConnected; {
println("Connection status: " + st.String())
time.Sleep(1 * time.Second)
st, _ = adaptor.GetConnectionStatus()
}
println("Connected.")
time.Sleep(2 * time.Second)
ip, _, _, err := adaptor.GetIP()
for ; err != nil; ip, _, _, err = adaptor.GetIP() {
println(err.Error())
time.Sleep(1 * time.Second)
}
println(ip.String())
}
// 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)
}
}
+162
View File
@@ -0,0 +1,162 @@
// 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/net/mqtt"
"tinygo.org/x/drivers/wifinina"
)
// access point info
const ssid = ""
const pass = ""
// 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 the default pins for the Arduino Nano33 IoT.
uart = machine.UART2
tx = machine.NINA_TX
rx = machine.NINA_RX
spi = machine.NINA_SPI
// this is the ESP chip that has the WIFININA firmware flashed on it
adaptor = &wifinina.Device{
SPI: spi,
CS: machine.NINA_CS,
ACK: machine.NINA_ACK,
GPIO0: machine.NINA_GPIO0,
RESET: machine.NINA_RESETN,
}
console = machine.UART0
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())
// Configure SPI for 8Mhz, Mode 0, MSB First
spi.Configure(machine.SPIConfig{
Frequency: 8 * 1e6,
MOSI: machine.NINA_MOSI,
MISO: machine.NINA_MISO,
SCK: machine.NINA_SCK,
})
// Init esp8266/esp32
adaptor.Configure()
connectToAP()
opts := mqtt.NewClientOptions()
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 i := 0; ; i++ {
println("Publishing MQTT message...")
data := []byte(fmt.Sprintf(`{"e":[{"n":"hello %d","v":101}]}`, i))
token := cl.Publish(topicRx, 0, false, data)
token.Wait()
if token.Error() != nil {
println(token.Error().Error())
}
time.Sleep(100 * time.Millisecond)
}
}
// connect to access point
func connectToAP() {
time.Sleep(2 * time.Second)
println("Connecting to " + ssid)
adaptor.SetPassphrase(ssid, pass)
for st, _ := adaptor.GetConnectionStatus(); st != wifinina.StatusConnected; {
println("Connection status: " + st.String())
time.Sleep(1 * time.Second)
st, _ = adaptor.GetConnectionStatus()
}
println("Connected.")
time.Sleep(2 * time.Second)
ip, _, _, err := adaptor.GetIP()
for ; err != nil; ip, _, _, err = adaptor.GetIP() {
println(err.Error())
time.Sleep(1 * time.Second)
}
println(ip.String())
}
// 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)
}
}
+143
View File
@@ -0,0 +1,143 @@
// This example opens a TCP connection using a device with WiFiNINA firmware
// and sends some data, for the purpose of testing speed and connectivity.
//
// You can open a server to accept connections from this program using:
//
// nc -w 5 -lk 8080
//
package main
import (
"bytes"
"fmt"
"machine"
"time"
"tinygo.org/x/drivers/net"
"tinygo.org/x/drivers/wifinina"
)
// access point info
const ssid = ""
const pass = ""
// IP address of the server aka "hub". Replace with your own info.
const serverIP = ""
// these are the default pins for the Arduino Nano33 IoT.
// change these to connect to a different UART or pins for the ESP8266/ESP32
var (
// these are the default pins for the Arduino Nano33 IoT.
uart = machine.UART2
tx = machine.NINA_TX
rx = machine.NINA_RX
spi = machine.NINA_SPI
// this is the ESP chip that has the WIFININA firmware flashed on it
adaptor = &wifinina.Device{
SPI: spi,
CS: machine.NINA_CS,
ACK: machine.NINA_ACK,
GPIO0: machine.NINA_GPIO0,
RESET: machine.NINA_RESETN,
}
console = machine.UART0
)
var buf = &bytes.Buffer{}
func main() {
uart.Configure(machine.UARTConfig{TX: tx, RX: rx})
// Configure SPI for 8Mhz, Mode 0, MSB First
spi.Configure(machine.SPIConfig{
Frequency: 8 * 1e6,
MOSI: machine.NINA_MOSI,
MISO: machine.NINA_MISO,
SCK: machine.NINA_SCK,
})
adaptor.Configure()
connectToAP()
for {
sendBatch()
time.Sleep(500 * time.Millisecond)
}
println("Done.")
}
func sendBatch() {
// make TCP connection
ip := net.ParseIP(serverIP)
raddr := &net.TCPAddr{IP: ip, Port: 8080}
laddr := &net.TCPAddr{Port: 8080}
message("---------------\r\nDialing TCP connection")
conn, err := net.DialTCP("tcp", laddr, raddr)
for ; err != nil; conn, err = net.DialTCP("tcp", laddr, raddr) {
message(err.Error())
time.Sleep(5 * time.Second)
}
n := 0
w := 0
start := time.Now()
// send data
message("Sending data")
for i := 0; i < 1000; i++ {
buf.Reset()
fmt.Fprint(buf,
"\r---------------------------- i == ", i, " ----------------------------"+
"\r---------------------------- i == ", i, " ----------------------------")
if w, err = conn.Write(buf.Bytes()); err != nil {
println("error:", err.Error(), "\r")
continue
}
n += w
}
buf.Reset()
ms := time.Now().Sub(start).Milliseconds()
fmt.Fprint(buf, "\nWrote ", n, " bytes in ", ms, " ms\r\n")
message(buf.String())
if _, err := conn.Write(buf.Bytes()); err != nil {
println("error:", err.Error(), "\r")
}
// Right now this code is never reached. Need a way to trigger it...
println("Disconnecting TCP...")
conn.Close()
}
// connect to access point
func connectToAP() {
time.Sleep(2 * time.Second)
message("Connecting to " + ssid)
adaptor.SetPassphrase(ssid, pass)
for st, _ := adaptor.GetConnectionStatus(); st != wifinina.StatusConnected; {
message("Connection status: " + st.String())
time.Sleep(1 * time.Second)
st, _ = adaptor.GetConnectionStatus()
}
message("Connected.")
time.Sleep(2 * time.Second)
ip, _, _, err := adaptor.GetIP()
for ; err != nil; ip, _, _, err = adaptor.GetIP() {
message(err.Error())
time.Sleep(1 * time.Second)
}
message(ip.String())
}
func message(msg string) {
println(msg, "\r")
}
+153
View File
@@ -0,0 +1,153 @@
// This example opens a TCP connection using a device with WiFiNINA firmware
// and sends a HTTP request to retrieve a webpage, based on the following
// Arduino example:
//
// https://github.com/arduino-libraries/WiFiNINA/blob/master/examples/WiFiWebClientRepeating/
//
package main
import (
"fmt"
"machine"
"time"
"tinygo.org/x/drivers/net"
"tinygo.org/x/drivers/wifinina"
)
// access point info
const ssid = ""
const pass = ""
// IP address of the server aka "hub". Replace with your own info.
const server = "tinygo.org"
// these are the default pins for the Arduino Nano33 IoT.
// change these to connect to a different UART or pins for the ESP8266/ESP32
var (
// these are the default pins for the Arduino Nano33 IoT.
uart = machine.UART2
tx = machine.NINA_TX
rx = machine.NINA_RX
spi = machine.NINA_SPI
// this is the ESP chip that has the WIFININA firmware flashed on it
adaptor = &wifinina.Device{
SPI: spi,
CS: machine.NINA_CS,
ACK: machine.NINA_ACK,
GPIO0: machine.NINA_GPIO0,
RESET: machine.NINA_RESETN,
}
console = machine.UART0
)
var buf [256]byte
var lastRequestTime time.Time
var conn net.Conn
func main() {
uart.Configure(machine.UARTConfig{TX: tx, RX: rx})
// Configure SPI for 8Mhz, Mode 0, MSB First
spi.Configure(machine.SPIConfig{
Frequency: 8 * 1e6,
MOSI: machine.NINA_MOSI,
MISO: machine.NINA_MISO,
SCK: machine.NINA_SCK,
})
adaptor.Configure()
connectToAP()
for {
loop()
}
println("Done.")
}
func loop() {
if conn != nil {
for n, err := conn.Read(buf[:]); n > 0; n, err = conn.Read(buf[:]) {
if err != nil {
println("Read error: " + err.Error())
} else {
print(string(buf[0:n]))
}
}
}
if time.Now().Sub(lastRequestTime).Milliseconds() >= 10000 {
makeHTTPRequest()
}
}
func makeHTTPRequest() {
var err error
if conn != nil {
conn.Close()
}
// make TCP connection
ip := net.ParseIP(server)
raddr := &net.TCPAddr{IP: ip, Port: 80}
laddr := &net.TCPAddr{Port: 8080}
message("\r\n---------------\r\nDialing TCP connection")
conn, err = net.DialTCP("tcp", laddr, raddr)
for ; err != nil; conn, err = net.DialTCP("tcp", laddr, raddr) {
message("connection failed: " + err.Error())
time.Sleep(5 * time.Second)
}
println("Connected!\r")
print("Sending HTTP request...")
fmt.Fprintln(conn, "GET / HTTP/1.1")
fmt.Fprintln(conn, "Host:", server)
fmt.Fprintln(conn, "User-Agent: TinyGo/0.10.0")
fmt.Fprintln(conn, "Connection: close")
fmt.Fprintln(conn)
println("Sent!\r\n\r")
lastRequestTime = time.Now()
}
func readLine(conn *net.TCPSerialConn) string {
println("Attempting to read...\r")
b := buf[:]
for expiry := time.Now().Unix() + 10; time.Now().Unix() > expiry; {
if n, err := conn.Read(b); n > 0 && err == nil {
return string(b[0:n])
}
}
return ""
}
// connect to access point
func connectToAP() {
time.Sleep(2 * time.Second)
message("Connecting to " + ssid)
adaptor.SetPassphrase(ssid, pass)
for st, _ := adaptor.GetConnectionStatus(); st != wifinina.StatusConnected; {
message("Connection status: " + st.String())
time.Sleep(1 * time.Second)
st, _ = adaptor.GetConnectionStatus()
}
message("Connected.")
time.Sleep(2 * time.Second)
ip, _, _, err := adaptor.GetIP()
for ; err != nil; ip, _, _, err = adaptor.GetIP() {
message(err.Error())
time.Sleep(1 * time.Second)
}
message(ip.String())
}
func message(msg string) {
println(msg, "\r")
}
+183
View File
@@ -0,0 +1,183 @@
// Package lsm6ds3 implements a driver for the LSM6DS3 a 6 axis Inertial
// Measurement Unit (IMU)
//
// Datasheet: https://www.st.com/resource/en/datasheet/lsm6ds3.pdf
//
package lsm6ds3 // import "tinygo.org/x/drivers/lsm6ds3"
import (
"machine"
)
type AccelRange uint8
type AccelSampleRate uint8
type AccelBandwidth uint8
type GyroRange uint8
type GyroSampleRate uint8
// Device wraps an I2C connection to a LSM6DS3 device.
type Device struct {
bus machine.I2C
Address uint16
accelRange AccelRange
accelSampleRate AccelSampleRate
accelBandWidth AccelBandwidth
gyroRange GyroRange
gyroSampleRate GyroSampleRate
dataBufferSix []uint8
dataBufferTwo []uint8
}
// Configuration for LSM6DS3 device.
type Configuration struct {
AccelRange AccelRange
AccelSampleRate AccelSampleRate
AccelBandWidth AccelBandwidth
GyroRange GyroRange
GyroSampleRate GyroSampleRate
IsPedometer bool
ResetStepCounter bool
}
// New creates a new LSM6DS3 connection. The I2C bus must already be
// configured.
//
// This function only creates the Device object, it does not touch the device.
func New(bus machine.I2C) Device {
return Device{bus: bus, Address: Address}
}
// Configure sets up the device for communication.
func (d *Device) Configure(cfg Configuration) {
if cfg.AccelRange != 0 {
d.accelRange = cfg.AccelRange
} else {
d.accelRange = ACCEL_2G
}
if cfg.AccelSampleRate != 0 {
d.accelSampleRate = cfg.AccelSampleRate
} else {
d.accelSampleRate = ACCEL_SR_104
}
if cfg.AccelBandWidth != 0 {
d.accelBandWidth = cfg.AccelBandWidth
} else {
d.accelBandWidth = ACCEL_BW_100
}
if cfg.GyroRange != 0 {
d.gyroRange = cfg.GyroRange
} else {
d.gyroRange = GYRO_2000DPS
}
if cfg.GyroSampleRate != 0 {
d.gyroSampleRate = cfg.GyroSampleRate
} else {
d.gyroSampleRate = GYRO_SR_104
}
d.dataBufferSix = make([]uint8, 6)
d.dataBufferTwo = make([]uint8, 2)
if cfg.IsPedometer { // CONFIGURE AS PEDOMETER
// Configure accelerometer: 2G + 26Hz
d.bus.WriteRegister(uint8(d.Address), CTRL1_XL, []byte{uint8(ACCEL_2G) | uint8(ACCEL_SR_26)})
// Configure Zen_G, Yen_G, Xen_G, reset steps
if cfg.ResetStepCounter {
d.bus.WriteRegister(uint8(d.Address), CTRL10_C, []byte{0x3E})
} else {
d.bus.WriteRegister(uint8(d.Address), CTRL10_C, []byte{0x3C})
}
// Enable pedometer
d.bus.WriteRegister(uint8(d.Address), TAP_CFG, []byte{0x40})
} else { // NORMAL USE
// Configure accelerometer
data := make([]uint8, 1)
data[0] = uint8(d.accelRange) | uint8(d.accelSampleRate) | uint8(d.accelBandWidth)
d.bus.WriteRegister(uint8(d.Address), CTRL1_XL, data)
// Set ODR bit
d.bus.ReadRegister(uint8(d.Address), CTRL4_C, data)
data[0] = data[0] &^ BW_SCAL_ODR_ENABLED
data[0] |= BW_SCAL_ODR_ENABLED
d.bus.WriteRegister(uint8(d.Address), CTRL4_C, data)
// Configure gyroscope
data[0] = uint8(d.gyroRange) | uint8(d.gyroSampleRate)
d.bus.WriteRegister(uint8(d.Address), CTRL2_G, data)
}
}
// Connected returns whether a LSM6DS3 has been found.
// It does a "who am I" request and checks the response.
func (d *Device) Connected() bool {
data := []byte{0}
d.bus.ReadRegister(uint8(d.Address), WHO_AM_I, data)
return data[0] == 0x69
}
// ReadAcceleration reads the current acceleration from the device and returns
// it in µg (micro-gravity). When one of the axes is pointing straight to Earth
// and the sensor is not moving the returned value will be around 1000000 or
// -1000000.
func (d *Device) ReadAcceleration() (x int32, y int32, z int32) {
d.bus.ReadRegister(uint8(d.Address), OUTX_L_XL, d.dataBufferSix)
// k comes from "Table 3. Mechanical characteristics" 3 of the datasheet * 1000
k := int32(61) // 2G
if d.accelRange == ACCEL_4G {
k = 122
} else if d.accelRange == ACCEL_8G {
k = 244
} else if d.accelRange == ACCEL_16G {
k = 488
}
x = int32(int16((uint16(d.dataBufferSix[1])<<8)|uint16(d.dataBufferSix[0]))) * k
y = int32(int16((uint16(d.dataBufferSix[3])<<8)|uint16(d.dataBufferSix[2]))) * k
z = int32(int16((uint16(d.dataBufferSix[5])<<8)|uint16(d.dataBufferSix[4]))) * k
return
}
// ReadRotation reads the current rotation from the device and returns it in
// µ°/s (micro-degrees/sec). This means that if you were to do a complete
// rotation along one axis and while doing so integrate all values over time,
// you would get a value close to 360000000.
func (d *Device) ReadRotation() (x int32, y int32, z int32) {
d.bus.ReadRegister(uint8(d.Address), OUTX_L_G, d.dataBufferSix)
// k comes from "Table 3. Mechanical characteristics" 3 of the datasheet * 1000
k := int32(4375) // 125DPS
if d.gyroRange == GYRO_250DPS {
k = 8750
} else if d.gyroRange == GYRO_500DPS {
k = 17500
} else if d.gyroRange == GYRO_1000DPS {
k = 35000
} else if d.gyroRange == GYRO_2000DPS {
k = 70000
}
x = int32(int16((uint16(d.dataBufferSix[1])<<8)|uint16(d.dataBufferSix[0]))) * k
y = int32(int16((uint16(d.dataBufferSix[3])<<8)|uint16(d.dataBufferSix[2]))) * k
z = int32(int16((uint16(d.dataBufferSix[5])<<8)|uint16(d.dataBufferSix[4]))) * k
return
}
// ReadTemperature returns the temperature in celsius milli degrees (ºC/1000)
func (d *Device) ReadTemperature() (int32, error) {
d.bus.ReadRegister(uint8(d.Address), OUT_TEMP_L, d.dataBufferTwo)
// From "Table 5. Temperature sensor characteristics"
// temp = value/16 + 25
t := 25000 + (int32(int16((int16(d.dataBufferTwo[1])<<8)|int16(d.dataBufferTwo[0])))*125)/2
return t, nil
}
// ReadSteps returns the steps of the pedometer
func (d *Device) ReadSteps() int32 {
d.bus.ReadRegister(uint8(d.Address), STEP_COUNTER_L, d.dataBufferTwo)
return int32(int16((uint16(d.dataBufferTwo[1]) << 8) | uint16(d.dataBufferTwo[0])))
}
+83
View File
@@ -0,0 +1,83 @@
package lsm6ds3
// Constants/addresses used for I2C.
// The I2C address which this device listens to.
const Address = 0x6A
const (
WHO_AM_I = 0x0F
STATUS = 0x1E
CTRL1_XL = 0x10
CTRL2_G = 0x11
CTRL3_C = 0x12
CTRL4_C = 0x13
CTRL5_C = 0x14
CTRL6_C = 0x15
CTRL7_G = 0x16
CTRL8_XL = 0x17
CTRL9_XL = 0x18
CTRL10_C = 0x19
OUTX_L_G = 0x22
OUTX_H_G = 0x23
OUTY_L_G = 0x24
OUTY_H_G = 0x25
OUTZ_L_G = 0x26
OUTZ_H_G = 0x27
OUTX_L_XL = 0x28
OUTX_H_XL = 0x29
OUTY_L_XL = 0x2A
OUTY_H_XL = 0x2B
OUTZ_L_XL = 0x2C
OUTZ_H_XL = 0x2D
OUT_TEMP_L = 0x20
OUT_TEMP_H = 0x21
BW_SCAL_ODR_DISABLED = 0x00
BW_SCAL_ODR_ENABLED = 0x80
STEP_TIMESTAMP_L = 0x49
STEP_TIMESTAMP_H = 0x4A
STEP_COUNTER_L = 0x4B
STEP_COUNTER_H = 0x4C
STEP_COUNT_DELTA = 0x15
TAP_CFG = 0x58
INT1_CTRL = 0x0D
ACCEL_2G AccelRange = 0x00
ACCEL_4G AccelRange = 0x08
ACCEL_8G AccelRange = 0x0C
ACCEL_16G AccelRange = 0x04
ACCEL_SR_OFF AccelSampleRate = 0x00
ACCEL_SR_13 AccelSampleRate = 0x10
ACCEL_SR_26 AccelSampleRate = 0x20
ACCEL_SR_52 AccelSampleRate = 0x30
ACCEL_SR_104 AccelSampleRate = 0x40
ACCEL_SR_208 AccelSampleRate = 0x50
ACCEL_SR_416 AccelSampleRate = 0x60
ACCEL_SR_833 AccelSampleRate = 0x70
ACCEL_SR_1666 AccelSampleRate = 0x80
ACCEL_SR_3332 AccelSampleRate = 0x90
ACCEL_SR_6664 AccelSampleRate = 0xA0
ACCEL_SR_13330 AccelSampleRate = 0xB0
ACCEL_BW_50 AccelBandwidth = 0x03
ACCEL_BW_100 AccelBandwidth = 0x02
ACCEL_BW_200 AccelBandwidth = 0x01
ACCEL_BW_400 AccelBandwidth = 0x00
//GYRO_125DPS GyroRange = 0x01
GYRO_250DPS GyroRange = 0x00
GYRO_500DPS GyroRange = 0x04
GYRO_1000DPS GyroRange = 0x08
GYRO_2000DPS GyroRange = 0x0C
GYRO_SR_OFF GyroSampleRate = 0x00
GYRO_SR_13 GyroSampleRate = 0x10
GYRO_SR_26 GyroSampleRate = 0x20
GYRO_SR_52 GyroSampleRate = 0x30
GYRO_SR_104 GyroSampleRate = 0x40
GYRO_SR_208 GyroSampleRate = 0x50
GYRO_SR_416 GyroSampleRate = 0x60
GYRO_SR_833 GyroSampleRate = 0x70
GYRO_SR_1666 GyroSampleRate = 0x80
)
+92
View File
@@ -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() {
}
+27
View File
@@ -0,0 +1,27 @@
package net
type DeviceDriver interface {
GetDNS(domain string) (string, error)
ConnectTCPSocket(addr, port string) error
ConnectSSLSocket(addr, port string) error
ConnectUDPSocket(addr, sendport, listenport string) error
DisconnectSocket() error
StartSocketSend(size int) error
Write(b []byte) (n int, err error)
ReadSocket(b []byte) (n int, err error)
IsSocketDataAvailable() bool
// FIXME: this is really specific to espat, and maybe shouldn't be part
// of the driver interface
Response(timeout int) ([]byte, error)
}
var ActiveDevice DeviceDriver
func UseDriver(driver DeviceDriver) {
// TODO: rethink and refactor this
if ActiveDevice != nil {
panic("net.ActiveDevice is already set")
}
ActiveDevice = driver
}
+148 -33
View File
@@ -1,3 +1,5 @@
// Package mqtt is intended to provide compatible interfaces with the
// Paho mqtt library.
package mqtt
import (
@@ -6,9 +8,8 @@ import (
"time"
"github.com/eclipse/paho.mqtt.golang/packets"
"tinygo.org/x/drivers/espat"
"tinygo.org/x/drivers/espat/net"
"tinygo.org/x/drivers/espat/tls"
"tinygo.org/x/drivers/net"
"tinygo.org/x/drivers/net/tls"
)
// NewClient will create an MQTT v3.1.1 client with all of the options specified
@@ -17,15 +18,21 @@ import (
// connection) are created before the application is actually ready.
func NewClient(o *ClientOptions) Client {
c := &mqttclient{opts: o, adaptor: o.Adaptor}
c.msgRouter, c.stopRouter = newRouter()
return c
}
type mqttclient struct {
adaptor *espat.Device
conn net.Conn
connected bool
opts *ClientOptions
mid uint16
adaptor net.DeviceDriver
conn net.Conn
connected bool
opts *ClientOptions
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
@@ -69,6 +76,12 @@ func (c *mqttclient) Connect() Token {
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
connectPkt := packets.NewControlPacket(packets.Connect).(*packets.ConnectPacket)
connectPkt.Qos = 0
@@ -82,7 +95,7 @@ func (c *mqttclient) Connect() Token {
connectPkt.PasswordFlag = true
}
connectPkt.ClientIdentifier = c.opts.ClientID //"tinygo-client-" + randomString(10)
connectPkt.ClientIdentifier = c.opts.ClientID
connectPkt.ProtocolVersion = byte(c.opts.ProtocolVersion)
connectPkt.ProtocolName = "MQTT"
connectPkt.Keepalive = 30
@@ -92,26 +105,25 @@ func (c *mqttclient) Connect() Token {
return &mqtttoken{err: err}
}
// TODO: handle timeout
for {
packet, _ := packets.ReadPacket(c.conn)
if packet != nil {
ack, ok := packet.(*packets.ConnackPacket)
if ok {
if ack.ReturnCode == 0 {
// success
return &mqtttoken{}
}
// otherwise something went wrong
// TODO: handle timeout as ReadPacket blocks until it gets a packet.
// CONNECT response.
packet, err := packets.ReadPacket(c.conn)
if err != nil {
return &mqtttoken{err: err}
}
if packet != nil {
ack, ok := packet.(*packets.ConnackPacket)
if ok {
if ack.ReturnCode != 0 {
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{}
}
@@ -127,6 +139,10 @@ func (c *mqttclient) Disconnect(quiesce uint) {
// to the specified topic.
// 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 {
if !c.IsConnected() {
return &mqtttoken{err: errors.New("MQTT client not connected")}
}
pub := packets.NewControlPacket(packets.Publish).(*packets.PublishPacket)
pub.Qos = qos
pub.TopicName = topic
@@ -142,12 +158,37 @@ func (c *mqttclient) Publish(topic string, qos byte, retained bool, payload inte
c.mid++
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
// a message is published on the topic provided.
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{}
}
@@ -171,18 +212,92 @@ func (c *mqttclient) OptionsReader() ClientOptionsReader {
return r
}
type mqtttoken struct {
err error
func processInbound(c *mqttclient) {
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 {
return true
// readMessages reads incoming messages off the wire.
// 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 {
return true
func (c *mqttclient) ackFunc(packet *packets.PublishPacket) func() {
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 {
return t.err
// ReadPacket tries to read the next incoming packet from the MQTT broker.
// 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 net.ActiveDevice.IsSocketDataAvailable() {
return packets.ReadPacket(c.conn)
}
return nil, nil
}
+39 -4
View File
@@ -24,7 +24,8 @@ import (
"strings"
"time"
"tinygo.org/x/drivers/espat"
"github.com/eclipse/paho.mqtt.golang/packets"
"tinygo.org/x/drivers/net"
)
const (
@@ -155,6 +156,18 @@ func (m *message) Ack() {
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.
type ClientOptionsReader struct {
options *ClientOptions
@@ -162,7 +175,7 @@ type ClientOptionsReader struct {
// ClientOptions contains configurable options for an MQTT Client.
type ClientOptions struct {
Adaptor *espat.Device
Adaptor net.DeviceDriver
//Servers []*url.URL
Servers string
@@ -196,8 +209,8 @@ type ClientOptions struct {
}
// NewClientOptions returns a new ClientOptions struct.
func NewClientOptions(adaptor *espat.Device) *ClientOptions {
return &ClientOptions{Adaptor: adaptor, ProtocolVersion: 4}
func NewClientOptions() *ClientOptions {
return &ClientOptions{Adaptor: net.ActiveDevice, ProtocolVersion: 4}
}
// AddBroker adds a broker URI to the list of brokers to be used. The format should be
@@ -243,3 +256,25 @@ func (o *ClientOptions) SetPassword(p string) *ClientOptions {
o.Password = p
return o
}
// SetWill accepts a string will message to be set. When the client connects,
// it will give this will message to the broker, which will then publish the
// provided payload (the will) to any clients that are subscribed to the provided
// topic.
func (o *ClientOptions) SetWill(topic string, payload string, qos byte, retained bool) *ClientOptions {
o.SetBinaryWill(topic, []byte(payload), qos, retained)
return o
}
// SetBinaryWill accepts a []byte will message to be set. When the client connects,
// it will give this will message to the broker, which will then publish the
// provided payload (the will) to any clients that are subscribed to the provided
// topic.
func (o *ClientOptions) SetBinaryWill(topic string, payload []byte, qos byte, retained bool) *ClientOptions {
o.WillEnabled = true
o.WillTopic = topic
o.WillPayload = payload
o.WillQos = qos
o.WillRetained = retained
return o
}
+182
View File
@@ -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
}
}
}()
}
+19
View File
@@ -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
}
+36 -17
View File
@@ -7,8 +7,6 @@ import (
"strconv"
"strings"
"time"
"tinygo.org/x/drivers/espat"
)
// DialUDP makes a UDP network connection. raadr is the port that the messages will
@@ -20,12 +18,15 @@ func DialUDP(network string, laddr, raddr *UDPAddr) (*UDPSerialConn, error) {
listenport := strconv.Itoa(laddr.Port)
// disconnect any old socket
espat.ActiveDevice.DisconnectSocket()
ActiveDevice.DisconnectSocket()
// connect new socket
espat.ActiveDevice.ConnectUDPSocket(addr, sendport, listenport)
err := 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: ActiveDevice}, laddr: laddr, raddr: raddr}, nil
}
// ListenUDP listens for UDP connections on the port listed in laddr.
@@ -35,12 +36,15 @@ func ListenUDP(network string, laddr *UDPAddr) (*UDPSerialConn, error) {
listenport := strconv.Itoa(laddr.Port)
// disconnect any old socket
espat.ActiveDevice.DisconnectSocket()
ActiveDevice.DisconnectSocket()
// connect new socket
espat.ActiveDevice.ConnectUDPSocket(addr, sendport, listenport)
err := 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: ActiveDevice}, laddr: laddr}, nil
}
// DialTCP makes a TCP network connection. raadr is the port that the messages will
@@ -50,13 +54,16 @@ func DialTCP(network string, laddr, raddr *TCPAddr) (*TCPSerialConn, error) {
addr := raddr.IP.String()
sendport := strconv.Itoa(raddr.Port)
// disconnect any old socket
espat.ActiveDevice.DisconnectSocket()
// disconnect any old socket?
//ActiveDevice.DisconnectSocket()
// connect new socket
espat.ActiveDevice.ConnectTCPSocket(addr, sendport)
err := 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: ActiveDevice}, laddr: laddr, raddr: raddr}, nil
}
// Dial connects to the address on the named network.
@@ -87,7 +94,7 @@ func Dial(network, address string) (Conn, error) {
// SerialConn is a loosely net.Conn compatible implementation
type SerialConn struct {
Adaptor *espat.Device
Adaptor DeviceDriver
}
// UDPSerialConn is a loosely net.Conn compatible intended to support
@@ -132,8 +139,20 @@ func (c *SerialConn) Read(b []byte) (n int, err error) {
func (c *SerialConn) Write(b []byte) (n int, err error) {
// specify that is a data transfer to the
// currently open socket, not commands to the ESP8266/ESP32.
c.Adaptor.StartSocketSend(len(b))
return c.Adaptor.Write(b)
err = c.Adaptor.StartSocketSend(len(b))
if err != nil {
return
}
n, err = c.Adaptor.Write(b)
if err != nil {
return n, err
}
/* TODO(bcg): this is kind of specific to espat, should maybe refactor */
_, err = c.Adaptor.Response(1000)
if err != nil {
return n, err
}
return n, err
}
// Close closes the connection.
@@ -220,7 +239,7 @@ func ResolveTCPAddr(network, address string) (*TCPAddr, error) {
// TODO: make sure network is 'tcp'
// separate domain from port, if any
r := strings.Split(address, ":")
addr, err := espat.ActiveDevice.GetDNS(r[0])
addr, err := ActiveDevice.GetDNS(r[0])
if err != nil {
return nil, err
}
@@ -243,7 +262,7 @@ func ResolveUDPAddr(network, address string) (*UDPAddr, error) {
// TODO: make sure network is 'udp'
// separate domain from port, if any
r := strings.Split(address, ":")
addr, err := espat.ActiveDevice.GetDNS(r[0])
addr, err := ActiveDevice.GetDNS(r[0])
if err != nil {
return nil, err
}
+4 -5
View File
@@ -5,8 +5,7 @@ package tls
import (
"strconv"
"tinygo.org/x/drivers/espat"
"tinygo.org/x/drivers/espat/net"
"tinygo.org/x/drivers/net"
)
// Dial makes a TLS network connection. It tries to provide a mostly compatible interface
@@ -22,15 +21,15 @@ func Dial(network, address string, config *Config) (*net.TCPSerialConn, error) {
sendport := strconv.Itoa(raddr.Port)
// disconnect any old socket
espat.ActiveDevice.DisconnectSocket()
net.ActiveDevice.DisconnectSocket()
// connect new socket
err = espat.ActiveDevice.ConnectSSLSocket(addr, sendport)
err = net.ActiveDevice.ConnectSSLSocket(addr, sendport)
if err != nil {
return nil, err
}
return net.NewTCPSerialConn(net.SerialConn{Adaptor: espat.ActiveDevice}, nil, raddr), nil
return net.NewTCPSerialConn(net.SerialConn{Adaptor: net.ActiveDevice}, nil, raddr), nil
}
// Config is a placeholder for future compatibility with
+47
View File
@@ -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(&params)))
if unwritten != 0 {
// Error: unwritten is the number of bytes not written.
return &IOError{
BytesWritten: len(data) - unwritten,
}
}
return nil
}
+20
View File
@@ -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)
}
+104
View File
@@ -0,0 +1,104 @@
// Package shifter is for 8bit shift register, most common are 74HC165 and 74165
package shifter // import "tinygo.org/x/drivers/shifter"
import (
"errors"
"machine"
)
const (
EIGHT_BITS NumberBit = 8
SIXTEEN_BITS NumberBit = 16
THIRTYTWO_BITS NumberBit = 32
)
type NumberBit int8
// Device holds the Pins.
type Device struct {
latch machine.Pin
clk machine.Pin
out machine.Pin
Pins []ShiftPin
bits NumberBit
}
// ShiftPin is the implementation of the ShiftPin interface.
type ShiftPin struct {
pin machine.Pin
d *Device
}
// New returns a new thermistor driver given an ADC pin.
func New(numBits NumberBit, latch, clk, out machine.Pin) Device {
return Device{
latch: latch,
clk: clk,
out: out,
Pins: make([]ShiftPin, int(numBits)),
bits: numBits,
}
}
// Configure here just for interface compatibility.
func (d *Device) Configure() {
d.latch.Configure(machine.PinConfig{Mode: machine.PinOutput})
d.clk.Configure(machine.PinConfig{Mode: machine.PinOutput})
d.out.Configure(machine.PinConfig{Mode: machine.PinInput})
for i := 0; i < int(d.bits); i++ {
d.Pins[i] = d.GetShiftPin(i)
}
}
// GetShiftPin returns an ShiftPin for a specific input.
func (d *Device) GetShiftPin(input int) ShiftPin {
return ShiftPin{pin: machine.Pin(input), d: d}
}
// Read8Input reads the 8 inputs and return an uint8
func (d *Device) Read8Input() (uint8, error) {
if d.bits != EIGHT_BITS {
return 0, errors.New("wrong amount of registers")
}
return uint8(d.readInput(EIGHT_BITS)), nil
}
// Read16Input reads the 16 inputs and return an uint16
func (d *Device) Read16Input() (uint16, error) {
if d.bits != SIXTEEN_BITS {
return 0, errors.New("wrong amount of registers")
}
return uint16(d.readInput(SIXTEEN_BITS)), nil
}
// Read32Input reads the 32 inputs and return an uint32
func (d *Device) Read32Input() (uint32, error) {
if d.bits != THIRTYTWO_BITS {
return 0, errors.New("wrong amount of registers")
}
return d.readInput(THIRTYTWO_BITS), nil
}
// Get the current reading for a specific ShiftPin.
func (p ShiftPin) Get() bool {
return (p.d.readInput(p.d.bits) & (1 << int(p.pin))) > 0
}
// Configure here just for interface compatibility.
func (p ShiftPin) Configure() {
}
// readInput reads howMany bits from the shift register
func (d *Device) readInput(howMany NumberBit) uint32 {
d.latch.High()
var data uint32
for i := howMany - 1; i >= 0; i-- {
d.clk.Low()
if d.out.Get() {
data |= 1 << i
}
d.clk.High()
}
d.latch.Low()
return data
}
+3 -16
View File
@@ -34,7 +34,7 @@ type Config struct {
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 {
dcPin.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
func (d *Device) Tx(data []byte, isCommand bool) {
if isCommand {
d.csPin.High()
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()
}
d.dcPin.Set(!isCommand)
d.bus.Tx(data, nil)
}
// Size returns the current size of the display.
+2 -35
View File
@@ -151,29 +151,9 @@ func (d *Device) Configure(cfg Config) {
if d.model == GREENTAB {
d.InvertColors(false)
d.Command(CASET)
d.Data(0x00)
d.Data(0x02)
d.Data(0x00)
d.Data(0x7F + 0x02)
d.Command(RASET)
d.Data(0x00)
d.Data(0x01)
d.Data(0x00)
d.Data(0x9F + 0x01)
} else if d.model == MINI80x160 {
d.isBGR = true
d.InvertColors(true)
d.Command(CASET)
d.Data(0x00)
d.Data(0x00)
d.Data(0x00)
d.Data(0x7F)
d.Command(RASET)
d.Data(0x00)
d.Data(0x00)
d.Data(0x00)
d.Data(0x9F)
}
// common color adjustment
@@ -383,21 +363,8 @@ func (d *Device) Data(data uint8) {
// Tx sends data to the display
func (d *Device) Tx(data []byte, isCommand bool) {
if isCommand {
d.csPin.High()
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()
}
d.dcPin.Set(!isCommand)
d.bus.Tx(data, nil)
}
// Size returns the current size of the display.
+7 -18
View File
@@ -100,18 +100,6 @@ func (d *Device) Configure(cfg Config) {
time.Sleep(10 * time.Millisecond)
d.SetRotation(d.rotation)
d.Command(CASET)
d.Data(0x00)
d.Data(uint8(d.columnOffset))
d.Data((240 + uint8(d.columnOffset)) >> 8)
d.Data(((240 + uint8(d.columnOffset)) >> 8) & 0xFF)
d.Command(RASET)
d.Data(0x00)
d.Data(uint8(d.rowOffset))
d.Data((240 + uint8(d.rowOffset)) >> 8)
d.Data(((240 + uint8(d.rowOffset)) >> 8) & 0xFF)
d.InvertColors(true)
d.Command(NORON)
@@ -194,12 +182,13 @@ func (d *Device) FillRectangleWithBuffer(x, y, width, height int16, buffer []col
offset := int32(0)
for k > 0 {
for i := int32(0); i < d.batchLength; i++ {
c565 := RGBATo565(buffer[offset+i])
c1 := uint8(c565 >> 8)
c2 := uint8(c565)
data[i*2] = c1
data[i*2+1] = c2
if offset+i < int32(len(buffer)) {
c565 := RGBATo565(buffer[offset+i])
c1 := uint8(c565 >> 8)
c2 := uint8(c565)
data[i*2] = c1
data[i*2+1] = c2
}
}
if k >= d.batchLength {
d.Tx(data, false)
+65
View File
@@ -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
)
+140
View File
@@ -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
}
+5
View File
@@ -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.9.0"
+3
View File
@@ -0,0 +1,3 @@
WiFiNINA protocol
=================
+213
View File
@@ -0,0 +1,213 @@
package wifinina
import (
"fmt"
"strconv"
"time"
"tinygo.org/x/drivers/net"
)
const (
ReadBufferSize = 128
)
func (d *Device) NewDriver() net.DeviceDriver {
return &Driver{dev: d, sock: NoSocketAvail}
}
type Driver struct {
dev *Device
sock uint8
readBuf readBuffer
}
type readBuffer struct {
data [ReadBufferSize]byte
head int
size int
}
func (drv *Driver) GetDNS(domain string) (string, error) {
ipAddr, err := drv.dev.GetHostByName(domain)
return ipAddr.String(), err
}
func (drv *Driver) ConnectTCPSocket(addr, portStr string) error {
return drv.connectSocket(addr, portStr, ProtoModeTCP)
}
func (drv *Driver) ConnectSSLSocket(addr, portStr string) error {
return drv.connectSocket(addr, portStr, ProtoModeTLS)
}
func (drv *Driver) connectSocket(addr, portStr string, mode uint8) error {
// convert port to uint16
p64, err := strconv.ParseUint(portStr, 10, 16)
if err != nil {
return fmt.Errorf("could not convert port to uint16: %s", err.Error())
}
port := uint16(p64)
// look up the hostname if necessary; if an IP address was specified, the
// same will be returned. Otherwise, an IPv4 for the hostname is returned.
ipAddr, err := drv.dev.GetHostByName(addr)
if err != nil {
return err
}
ip := ipAddr.AsUint32()
// check to see if socket is already set; if so, stop it
if drv.sock != NoSocketAvail {
if err := drv.stop(); err != nil {
return err
}
}
// get a socket from the device
if drv.sock, err = drv.dev.GetSocket(); err != nil {
return err
}
// attempt to start the client
if err := drv.dev.StartClient(ip, port, drv.sock, mode); err != nil {
return err
}
// FIXME: this 4 second timeout is simply mimicking the Arduino driver
for t := newTimer(4 * time.Second); !t.Expired(); {
connected, err := drv.IsConnected()
if err != nil {
return err
}
if connected {
return nil
}
wait(1 * time.Millisecond)
}
return ErrConnectionTimeout
}
func (drv *Driver) ConnectUDPSocket(addr, sport, lport string) error {
return ErrNotImplemented
}
func (drv *Driver) DisconnectSocket() error {
return drv.stop()
}
func (drv *Driver) StartSocketSend(size int) error {
// not needed for WiFiNINA???
return nil
}
func (drv *Driver) Response(timeout int) ([]byte, error) {
return nil, nil
}
func (drv *Driver) Write(b []byte) (n int, err error) {
if drv.sock == NoSocketAvail {
return 0, ErrNoSocketAvail
}
if len(b) == 0 {
return 0, ErrNoData
}
written, err := drv.dev.SendData(b, drv.sock)
if err != nil {
return 0, err
}
if written == 0 {
return 0, ErrDataNotWritten
}
if sent, _ := drv.dev.CheckDataSent(drv.sock); !sent {
return 0, ErrCheckDataError
}
return len(b), nil
}
func (drv *Driver) ReadSocket(b []byte) (n int, err error) {
avail, err := drv.available()
if err != nil {
println("ReadSocket error: " + err.Error())
return 0, err
}
if avail == 0 {
return 0, nil
}
length := len(b)
if avail < length {
length = avail
}
copy(b, drv.readBuf.data[drv.readBuf.head:drv.readBuf.head+length])
drv.readBuf.head += length
drv.readBuf.size -= length
return length, nil
}
// IsSocketDataAvailable returns of there is socket data available
func (drv *Driver) IsSocketDataAvailable() bool {
n, err := drv.available()
return err == nil && n > 0
}
func (drv *Driver) available() (int, error) {
if drv.readBuf.size == 0 {
n, err := drv.dev.GetDataBuf(drv.sock, drv.readBuf.data[:])
if n > 0 {
drv.readBuf.head = 0
drv.readBuf.size = n
}
if err != nil {
return int(n), err
}
}
return drv.readBuf.size, nil
}
func (drv *Driver) IsConnected() (bool, error) {
if drv.sock == NoSocketAvail {
return false, nil
}
s, err := drv.status()
if err != nil {
return false, err
}
isConnected := !(s == TCPStateListen || s == TCPStateClosed ||
s == TCPStateFinWait1 || s == TCPStateFinWait2 || s == TCPStateTimeWait ||
s == TCPStateSynSent || s == TCPStateSynRcvd || s == TCPStateCloseWait)
// TODO: investigate if the below is necessary (as per Arduino driver)
//if !isConnected {
// //close socket buffer?
// WiFiSocketBuffer.close(_sock);
// _sock = 255;
//}
return isConnected, nil
}
func (drv *Driver) status() (uint8, error) {
if drv.sock == NoSocketAvail {
return TCPStateClosed, nil
}
return drv.dev.GetClientState(drv.sock)
}
func (drv *Driver) stop() error {
if drv.sock == NoSocketAvail {
return nil
}
drv.dev.StopClient(drv.sock)
for t := newTimer(5 * time.Second); !t.Expired(); {
st, _ := drv.status()
if st == TCPStateClosed {
break
}
// FIXME: without the time.Sleep below this blocks until TCPStateClosed,
// however with it got goroutine stack overflows; not sure if this is still
// an issue so should investigate further
//time.Sleep(1 * time.Millisecond)
}
drv.sock = NoSocketAvail
return nil
}
+28
View File
@@ -0,0 +1,28 @@
package wifinina
import "time"
func wait(duration time.Duration) {
newTimer(duration).WaitUntilExpired()
}
type timer struct {
start int64
interval int64
}
func newTimer(interval time.Duration) timer {
return timer{
start: time.Now().UnixNano(),
interval: int64(interval),
}
}
func (t timer) Expired() bool {
return time.Now().UnixNano() > (t.start + t.interval)
}
func (t timer) WaitUntilExpired() {
for !t.Expired() {
}
}
+1032
View File
File diff suppressed because it is too large Load Diff
+16
View File
@@ -0,0 +1,16 @@
// +build !baremetal
package ws2812
// This file implements the WS2812 protocol for simulation.
import "machine"
// Send a single byte using the WS2812 protocol.
func (d Device) WriteByte(c byte) error {
writeByte(d.Pin, c)
return nil
}
//go:export __tinygo_ws2812_write_byte
func writeByte(pin machine.Pin, c byte)
+10 -8
View File
@@ -17,18 +17,20 @@ func (d Device) WriteByte(c byte) error {
// See:
// https://wp.josh.com/2014/05/13/ws2812-neopixels-are-not-so-finicky-once-you-get-to-know-them/
// T0H: 4 cycles or 250ns
// T0L: 14 cycles or 875ns -> together 18 cycles or 1125ns
// T1H: 10 cycles or 625ns
// T1H: 8 cycles or 500ns -> together 18 cycles or 1125ns
// Note: timings have been increased slightly to also support ws2811 LEDs.
// T0H: 5 cycles or 312.5ns
// T0L: 14 cycles or 875.0ns -> together 19 cycles or 1187.5ns
// T1H: 11 cycles or 687.5ns
// T1H: 8 cycles or 500.0ns -> together 19 cycles or 1187.5ns
value := uint32(c) << 24
arm.AsmFull(`
send_bit:
1: @ send_bit
str {maskSet}, {portSet} @ [2] T0H and T0L start here
nop @ [1]
lsls {value}, #1 @ [1]
bcs.n skip_store @ [1/3]
bcs.n 2f @ [1/3] skip_store
str {maskClear}, {portClear} @ [2] T0H -> T0L transition
skip_store:
2: @ skip_store
nop @ [4]
nop
nop
@@ -37,7 +39,7 @@ func (d Device) WriteByte(c byte) error {
nop @ [2]
nop
subs {i}, #1 @ [1]
bne.n send_bit @ [1/3]
bne.n 1b @ [1/3] send_bit
`, map[string]interface{}{
"value": value,
"i": 8,
+5 -5
View File
@@ -1,4 +1,4 @@
// +build circuitplay_express itsybitsy_m0
// +build atsamd21
package ws2812
@@ -23,7 +23,7 @@ func (d Device) WriteByte(c byte) error {
// T1L: 22 cycles or 458.3ns -> together 49 cycles or 1020.8ns
value := uint32(c) << 24
arm.AsmFull(`
send_bit:
1: @ send_bit
str {maskSet}, {portSet} @ [2] T0H and T0L start here
lsls {value}, #1 @ [1]
nop @ [6]
@@ -32,9 +32,9 @@ func (d Device) WriteByte(c byte) error {
nop
nop
nop
bcs.n skip_store @ [1/3]
bcs.n 2f @ [1/3] skip_store
str {maskClear}, {portClear} @ [2] T0H -> T0L transition
skip_store:
2: @ skip_store
nop @ [15]
nop
nop
@@ -68,7 +68,7 @@ func (d Device) WriteByte(c byte) error {
nop
nop
subs {i}, #1 @ [1]
bne.n send_bit @ [1/3]
bne.n 1b @ [1/3] send_bit
`, map[string]interface{}{
"value": value,
"i": 8,
+173
View File
@@ -0,0 +1,173 @@
// +build atsamd51
package ws2812
// This file implements the WS2812 protocol for 120MHz Cortex-M4
// microcontrollers.
// Note: This implementation does not work with tinygo 0.9.0 or older.
import (
"device/arm"
)
// Send a single byte using the WS2812 protocol.
func (d Device) WriteByte(c byte) error {
// For the Cortex-M4 at 120MHz
portSet, maskSet := d.Pin.PortMaskSet()
portClear, maskClear := d.Pin.PortMaskClear()
// See:
// https://wp.josh.com/2014/05/13/ws2812-neopixels-are-not-so-finicky-once-you-get-to-know-them/
// T0H: 32-34 cycles or 266.67ns - 283.33ns
// T0L: 101-103 cycles or 841.67ns - 858.33ns
// +: 133-137 cycles or 1108.33ns - 1141.67ns
// T1H: 73-75 cycles or 608.33ns - 625.00ns
// T1L: 58-60 cycles or 483.33ns - 500.00ns
// +: 131-135 cycles or 1091.67ns - 1125.00ns
// A branch is treated here as 1-3 cycles, because apparently it might get
// speculated. This is more of a guess than hard fact, because the only docs
// by ARM that state this are now considered superseded (by what?).
value := uint32(c) << 24
arm.AsmFull(`
1: @ send_bit
str {maskSet}, {portSet} @ [2] T0H and T0L start here
lsls {value}, #1 @ [1]
nop @ [28]
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
bcs.n 2f @ [1-3] skip_store
str {maskClear}, {portClear} @ [2] T0H -> T0L transition
2: @ skip_store
nop @ [41]
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
str {maskClear}, {portClear} @ [2] T1H -> T1L transition
nop @ [54]
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
subs {i}, #1 @ [1]
bne.n 1b @ [1-3] send_bit
`, map[string]interface{}{
"value": value,
"i": 8,
"maskSet": maskSet,
"portSet": portSet,
"maskClear": maskClear,
"portClear": portClear,
})
return nil
}
+5 -5
View File
@@ -28,10 +28,10 @@ func (d Device) WriteByte(c byte) error {
// by ARM that state this are now considered superseded (by what?).
value := uint32(c) << 24
arm.AsmFull(`
send_bit:
1: @ send_bit
str {maskSet}, {portSet} @ [2] T0H and T0L start here
lsls {value}, #1 @ [1]
nop @ [10]
nop @ [13]
nop
nop
nop
@@ -44,9 +44,9 @@ func (d Device) WriteByte(c byte) error {
nop
nop
nop
bcs.n skip_store @ [1-3]
bcs.n 2f @ [1-3] skip_store
str {maskClear}, {portClear} @ [2] T0H -> T0L transition
skip_store:
2: @ skip_store
nop @ [22]
nop
nop
@@ -97,7 +97,7 @@ func (d Device) WriteByte(c byte) error {
nop
nop
subs {i}, #1 @ [1]
bne.n send_bit @ [1-3]
bne.n 1b @ [1-3] send_bit
`, map[string]interface{}{
"value": value,
"i": 8,