mirror of
https://github.com/tinygo-org/drivers.git
synced 2026-07-27 19:18:41 +00:00
Compare commits
47 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 850df0a25c | |||
| 5f4806f0f9 | |||
| c8e62562b5 | |||
| 6842bdb424 | |||
| d43263f764 | |||
| 6716bb6c0a | |||
| f4bccd1fed | |||
| d5aa295b76 | |||
| c6e8af3057 | |||
| 38076352eb | |||
| 086415605e | |||
| 1d0f04af6b | |||
| dc883d913d | |||
| 3bb5b4519b | |||
| c12f78eb50 | |||
| adb0c2c261 | |||
| 18f0722728 | |||
| e80a22d0ba | |||
| cc5ecafacf | |||
| 7b56e61d52 | |||
| 514b436889 | |||
| 7233452819 | |||
| 7929aa10ea | |||
| 02084fd8a5 | |||
| f822da51fe | |||
| c62d7db35d | |||
| 93372474a2 | |||
| 7dcbfbecc6 | |||
| 2e606b090a | |||
| e0cdc931e7 | |||
| 130d9de03b | |||
| 2c2f1d3db4 | |||
| 2413eb86e0 | |||
| c7555a1469 | |||
| 3fca96e0ef | |||
| c7981f72ec | |||
| 5df157230f | |||
| bcb291992c | |||
| 7b710e3a48 | |||
| dcfd9c066d | |||
| 955b3a56e8 | |||
| 21b8d953f4 | |||
| d1b917b835 | |||
| 2cd73e3204 | |||
| 3ae5895183 | |||
| d80f619c9f | |||
| c91888a099 |
@@ -1,3 +1,82 @@
|
||||
0.10.0
|
||||
---
|
||||
- **new devices**
|
||||
- adt7410: Support for ADT7410 temperature sensor (#109)
|
||||
- ili9341: ILI9341 TFT driver (#115)
|
||||
- l293x: added support for h-bridge motor controller
|
||||
- l9110x: add support for L9110x h-bridge motor driver
|
||||
- resistive: Adding driver for four-wire resistive touchscreen (#118)
|
||||
- **enhancements**
|
||||
- st7735: added scroll functionality to st7735
|
||||
- st7735: remove default offsets
|
||||
- st7789: remove default offsets
|
||||
- ws2812: Added nrf52840 tag to ws2812
|
||||
- ws2812: work-arounds to allow Digispark to control WS2812 LEDs
|
||||
- **docs**
|
||||
- readme: update README to include list of all 44 drivers
|
||||
- wifinina: update docs and add Dockerfile to build firmware
|
||||
- wifinina: update docs and info on how to install WiFiNINA driver
|
||||
|
||||
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**
|
||||
|
||||
@@ -9,44 +9,115 @@ 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/adt7410/main.go
|
||||
@md5sum ./build/test.hex
|
||||
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=pyportal ./examples/ili9341/basic/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=pyportal ./examples/touch/resistive/fourwire/main.go
|
||||
@md5sum ./build/test.hex
|
||||
tinygo build -size short -o ./build/test.hex -target=pyportal ./examples/touch/resistive/pyportal_touchpaint/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
|
||||
@md5sum ./build/test.hex
|
||||
tinygo build -size short -o ./build/test.hex -target=digispark ./examples/ws2812
|
||||
@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
|
||||
tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/l293x/simple/main.go
|
||||
@md5sum ./build/test.hex
|
||||
tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/l293x/speed/main.go
|
||||
@md5sum ./build/test.hex
|
||||
tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/l9110x/simple/main.go
|
||||
@md5sum ./build/test.hex
|
||||
tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/l9110x/speed/main.go
|
||||
@md5sum ./build/test.hex
|
||||
|
||||
test: clean fmt-check smoke-test
|
||||
|
||||
@@ -52,8 +52,11 @@ func main() {
|
||||
|
||||
## Currently supported devices
|
||||
|
||||
The following 44 devices are supported.
|
||||
|
||||
| Device Name | Interface Type |
|
||||
|----------|-------------|
|
||||
| [ADT7410 I2C Temperature Sensor](https://www.analog.com/media/en/technical-documentation/data-sheets/ADT7410.pdf) | I2C |
|
||||
| [ADXL345 accelerometer](http://www.analog.com/media/en/technical-documentation/data-sheets/ADXL345.pdf) | I2C |
|
||||
| [APA102 RGB LED](https://cdn-shop.adafruit.com/product-files/2343/APA102C.pdf) | SPI |
|
||||
| [AT24CX 2-wire serial EEPROM](https://www.openimpulse.com/blog/wp-content/uploads/wpsc/downloadables/24C32-Datasheet.pdf) | I2C |
|
||||
@@ -65,22 +68,33 @@ 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 |
|
||||
| [HD44780 LCD controller](https://www.sparkfun.com/datasheets/LCD/HD44780.pdf) | GPIO |
|
||||
| [HUB75 RGB led matrix](https://cdn-learn.adafruit.com/downloads/pdf/32x16-32x32-rgb-led-matrix.pdf) | SPI |
|
||||
| [ILI9341 TFT color display](https://cdn-shop.adafruit.com/datasheets/ILI9341.pdf) | SPI |
|
||||
| [L293x motor driver](https://www.ti.com/lit/ds/symlink/l293d.pdf) | GPIO/PWM |
|
||||
| [L9110x motor driver](https://www.elecrow.com/download/datasheet-l9110.pdf) | GPIO/PWM |
|
||||
| [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 |
|
||||
| [Resistive Touchscreen (4-wire)](http://ww1.microchip.com/downloads/en/Appnotes/doc8091.pdf) | GPIO |
|
||||
| [Semihosting](https://wiki.segger.com/Semihosting) | Debug |
|
||||
| [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 |
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
package adt7410
|
||||
|
||||
import (
|
||||
"machine"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Error uint8
|
||||
|
||||
const (
|
||||
ErrInvalidID Error = 0x1
|
||||
)
|
||||
|
||||
func (e Error) Error() string {
|
||||
switch e {
|
||||
case ErrInvalidID:
|
||||
return "Invalid chip ID"
|
||||
default:
|
||||
return "Unknown error"
|
||||
}
|
||||
}
|
||||
|
||||
type Device struct {
|
||||
bus *machine.I2C
|
||||
buf []byte
|
||||
addr uint8
|
||||
}
|
||||
|
||||
// New returns ADT7410 device for the provided I2C bus and address. The ADT7410
|
||||
// has a default address of 0x48 (1001000). The last 2 bits of the address
|
||||
// can be set using by connecting to the A1 and A0 pins to VDD or GND (for a
|
||||
// total of up to 4 devices on a I2C bus). Also note that 10k pullups are
|
||||
// recommended for the SDA and SCL lines.
|
||||
func New(i2c *machine.I2C, addressBits uint8) *Device {
|
||||
return &Device{
|
||||
bus: i2c,
|
||||
buf: make([]byte, 2),
|
||||
addr: Address | (addressBits & 0x3),
|
||||
}
|
||||
}
|
||||
|
||||
func (dev *Device) Configure() (err error) {
|
||||
|
||||
// verify the chip ID
|
||||
// TODO: According to datasheet, the check below should work; however
|
||||
// this does not seem to be working right, but is not exactly
|
||||
// necessary, so can revisit later to see if there is a bug
|
||||
//id := dev.ReadByte(RegID) & 0xF8
|
||||
//if id != 0xC8 {
|
||||
// err = ErrInvalidID
|
||||
//}
|
||||
|
||||
// reset the chip
|
||||
dev.writeByte(RegReset, 0xFF)
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
// ReadTemperature returns the temperature in celsius milli degrees (ºC/1000)
|
||||
func (d *Device) ReadTemperature() (temperature int32, err error) {
|
||||
return (int32(d.readUint16(RegTempValueMSB)) * 1000) / 128, nil
|
||||
}
|
||||
|
||||
// ReadTempC returns the value in the temperature value register, in Celcius
|
||||
func (d *Device) ReadTempC() float32 {
|
||||
t := d.readUint16(RegTempValueMSB)
|
||||
return float32(int(t)) / 128.0
|
||||
}
|
||||
|
||||
// ReadTempF returns the value in the temperature value register, in Fahrenheit
|
||||
func (d *Device) ReadTempF() float32 {
|
||||
return d.ReadTempC()*1.8 + 32.0
|
||||
}
|
||||
|
||||
func (d *Device) writeByte(reg uint8, data byte) {
|
||||
d.buf[0] = reg
|
||||
d.buf[1] = data
|
||||
d.bus.Tx(uint16(d.addr), d.buf, nil)
|
||||
}
|
||||
|
||||
func (d *Device) readByte(reg uint8) byte {
|
||||
d.bus.ReadRegister(d.addr, reg, d.buf)
|
||||
return d.buf[0]
|
||||
}
|
||||
|
||||
func (d *Device) readUint16(reg uint8) uint16 {
|
||||
d.bus.ReadRegister(d.addr, reg, d.buf)
|
||||
return uint16(d.buf[0])<<8 | uint16(d.buf[1])
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package adt7410
|
||||
|
||||
const (
|
||||
// Default I2C address
|
||||
Address = 0x48
|
||||
|
||||
// Temperature Value MSB Register
|
||||
RegTempValueMSB = 0x0
|
||||
|
||||
// Temperature Value LSB Register
|
||||
RegTempValueLSB = 0x1
|
||||
|
||||
// Status Register
|
||||
RegStatus = 0x2
|
||||
|
||||
// Config Register
|
||||
RegConfig = 0x3
|
||||
|
||||
// ID Register
|
||||
RegID = 0x0B
|
||||
|
||||
// Software Reset Register
|
||||
RegReset = 0x2F
|
||||
)
|
||||
+15
-2
@@ -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) {
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
package apa102
|
||||
|
||||
import "machine"
|
||||
|
||||
// bbSPI is a dumb bit-bang implementation of SPI protocol that is hardcoded
|
||||
// to mode 0 and ignores trying to receive data. Just enough for the APA102.
|
||||
// Note: making this unexported for now because it is probable not suitable
|
||||
// most purposes other than the APA102 package. It might be desirable to make
|
||||
// this more generic and include it in the TinyGo "machine" package instead.
|
||||
type bbSPI struct {
|
||||
SCK machine.Pin
|
||||
MOSI machine.Pin
|
||||
Delay uint32
|
||||
}
|
||||
|
||||
// Configure sets up the SCK and MOSI pins as outputs and sets them low
|
||||
func (s *bbSPI) Configure() {
|
||||
s.SCK.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
s.MOSI.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
s.SCK.Low()
|
||||
s.MOSI.Low()
|
||||
if s.Delay == 0 {
|
||||
s.Delay = 1
|
||||
}
|
||||
}
|
||||
|
||||
// Tx matches signature of machine.SPI.Tx() and is used to send multiple bytes.
|
||||
// The r slice is ignored and no error will ever be returned.
|
||||
func (s *bbSPI) Tx(w []byte, r []byte) error {
|
||||
s.Configure()
|
||||
for _, b := range w {
|
||||
s.Transfer(b)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// delay represents a quarter of the clock cycle
|
||||
func (s *bbSPI) delay() {
|
||||
for i := uint32(0); i < s.Delay; {
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
// Transfer is used to send a single byte.
|
||||
func (s *bbSPI) Transfer(b byte) {
|
||||
for i := uint8(0); i < 8; i++ {
|
||||
|
||||
// half clock cycle high to start
|
||||
s.SCK.High()
|
||||
s.delay()
|
||||
|
||||
// write the value to MOSI (MSB first)
|
||||
if b&(1<<(7-i)) == 0 {
|
||||
s.MOSI.Low()
|
||||
} else {
|
||||
s.MOSI.High()
|
||||
}
|
||||
s.delay()
|
||||
|
||||
// half clock cycle low
|
||||
s.SCK.Low()
|
||||
s.delay()
|
||||
|
||||
// for actual SPI would try to read the MISO value here
|
||||
s.delay()
|
||||
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
// Package drivers provides a collection of hardware drivers for devices that
|
||||
// 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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"machine"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/adt7410"
|
||||
)
|
||||
|
||||
var (
|
||||
i2c = &machine.I2C0
|
||||
sensor = adt7410.New(i2c, 0)
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
i2c.Configure(machine.I2CConfig{Frequency: machine.TWI_FREQ_400KHZ})
|
||||
sensor.Configure()
|
||||
|
||||
for {
|
||||
temp := sensor.ReadTempF()
|
||||
fmt.Printf("temperature: %f\r\n", temp)
|
||||
time.Sleep(time.Second)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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")
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"image/color"
|
||||
"machine"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/ili9341"
|
||||
)
|
||||
|
||||
var (
|
||||
display = ili9341.NewParallel(
|
||||
machine.LCD_DATA0,
|
||||
machine.TFT_WR,
|
||||
machine.TFT_DC,
|
||||
machine.TFT_CS,
|
||||
machine.TFT_RESET,
|
||||
machine.TFT_RD,
|
||||
)
|
||||
|
||||
black = color.RGBA{0, 0, 0, 255}
|
||||
white = color.RGBA{255, 255, 255, 255}
|
||||
red = color.RGBA{255, 0, 0, 255}
|
||||
blue = color.RGBA{0, 0, 255, 255}
|
||||
green = color.RGBA{0, 255, 0, 255}
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
machine.TFT_BACKLIGHT.Configure(machine.PinConfig{machine.PinOutput})
|
||||
|
||||
display.Configure(ili9341.Config{})
|
||||
width, height := display.Size()
|
||||
|
||||
display.FillScreen(black)
|
||||
machine.TFT_BACKLIGHT.High()
|
||||
|
||||
display.FillRectangle(0, 0, width/2, height/2, white)
|
||||
display.FillRectangle(width/2, 0, width/2, height/2, red)
|
||||
display.FillRectangle(0, height/2, width/2, height/2, green)
|
||||
display.FillRectangle(width/2, height/2, width/2, height/2, blue)
|
||||
display.FillRectangle(width/4, height/4, width/2, height/2, black)
|
||||
for {
|
||||
time.Sleep(time.Hour)
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,234 @@
|
||||
// Port of Adafruit's "pyportal_boing" demo found here:
|
||||
// https://github.com/adafruit/Adafruit_ILI9341/blob/master/examples/pyportal_boing
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/examples/ili9341/pyportal_boing/graphics"
|
||||
"tinygo.org/x/drivers/ili9341"
|
||||
)
|
||||
|
||||
const (
|
||||
BGCOLOR = 0xAD75
|
||||
GRIDCOLOR = 0xA815
|
||||
BGSHADOW = 0x5285
|
||||
GRIDSHADOW = 0x600C
|
||||
RED = 0xF800
|
||||
WHITE = 0xFFFF
|
||||
|
||||
YBOTTOM = 123 // Ball Y coord at bottom
|
||||
YBOUNCE = -3.5 // Upward velocity on ball bounce
|
||||
|
||||
_debug = false
|
||||
)
|
||||
|
||||
var (
|
||||
display = ili9341.NewParallel(
|
||||
machine.LCD_DATA0,
|
||||
machine.TFT_WR,
|
||||
machine.TFT_DC,
|
||||
machine.TFT_CS,
|
||||
machine.TFT_RESET,
|
||||
machine.TFT_RD,
|
||||
)
|
||||
|
||||
frameBuffer = [(graphics.BALLHEIGHT + 8) * (graphics.BALLWIDTH + 8)]uint16{}
|
||||
|
||||
startTime int64
|
||||
frame int64
|
||||
|
||||
// Ball coordinates are stored floating-point because screen refresh
|
||||
// is so quick, whole-pixel movements are just too fast!
|
||||
ballx float32
|
||||
bally float32
|
||||
ballvx float32
|
||||
ballvy float32
|
||||
ballframe float32
|
||||
balloldx float32
|
||||
balloldy float32
|
||||
|
||||
// Color table for ball rotation effect
|
||||
palette [16]uint16
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
// configure backlight
|
||||
machine.TFT_BACKLIGHT.Configure(machine.PinConfig{machine.PinOutput})
|
||||
|
||||
// configure display
|
||||
display.Configure(ili9341.Config{})
|
||||
print("width, height == ")
|
||||
width, height := display.Size()
|
||||
println(width, height)
|
||||
|
||||
machine.TFT_BACKLIGHT.High()
|
||||
|
||||
display.SetRotation(ili9341.Rotation270)
|
||||
DrawBackground()
|
||||
|
||||
startTime = time.Now().UnixNano()
|
||||
frame = 0
|
||||
|
||||
ballx = 20.0
|
||||
bally = YBOTTOM // Current ball position
|
||||
ballvx = 0.8
|
||||
ballvy = YBOUNCE // Ball velocity
|
||||
ballframe = 3 // Ball animation frame #
|
||||
balloldx = ballx
|
||||
balloldy = bally // Prior ball position
|
||||
|
||||
for {
|
||||
|
||||
balloldx = ballx // Save prior position
|
||||
balloldy = bally
|
||||
ballx += ballvx // Update position
|
||||
bally += ballvy
|
||||
ballvy += 0.06 // Update Y velocity
|
||||
if (ballx <= 15) || (ballx >= graphics.SCREENWIDTH-graphics.BALLWIDTH) {
|
||||
ballvx *= -1 // Left/right bounce
|
||||
}
|
||||
if bally >= YBOTTOM { // Hit ground?
|
||||
bally = YBOTTOM // Clip and
|
||||
ballvy = YBOUNCE // bounce up
|
||||
}
|
||||
|
||||
// Determine screen area to update. This is the bounds of the ball's
|
||||
// prior and current positions, so the old ball is fully erased and new
|
||||
// ball is fully drawn.
|
||||
var minx, miny, maxx, maxy, width, height int16
|
||||
|
||||
// Determine bounds of prior and new positions
|
||||
minx = int16(ballx)
|
||||
if int16(balloldx) < minx {
|
||||
minx = int16(balloldx)
|
||||
}
|
||||
miny = int16(bally)
|
||||
if int16(balloldy) < miny {
|
||||
miny = int16(balloldy)
|
||||
}
|
||||
maxx = int16(ballx + graphics.BALLWIDTH - 1)
|
||||
if int16(balloldx+graphics.BALLWIDTH-1) > maxx {
|
||||
maxx = int16(balloldx + graphics.BALLWIDTH - 1)
|
||||
}
|
||||
maxy = int16(bally + graphics.BALLHEIGHT - 1)
|
||||
if int16(balloldy+graphics.BALLHEIGHT-1) > maxy {
|
||||
maxy = int16(balloldy + graphics.BALLHEIGHT - 1)
|
||||
}
|
||||
|
||||
width = maxx - minx + 1
|
||||
height = maxy - miny + 1
|
||||
|
||||
// Ball animation frame # is incremented opposite the ball's X velocity
|
||||
ballframe -= ballvx * 0.5
|
||||
if ballframe < 0 {
|
||||
ballframe += 14 // Constrain from 0 to 13
|
||||
} else if ballframe >= 14 {
|
||||
ballframe -= 14
|
||||
}
|
||||
|
||||
// Set 7 palette entries to white, 7 to red, based on frame number.
|
||||
// This makes the ball spin
|
||||
for i := 0; i < 14; i++ {
|
||||
if (int(ballframe)+i)%14 < 7 {
|
||||
palette[i+2] = WHITE
|
||||
} else {
|
||||
palette[i+2] = RED
|
||||
} // Palette entries 0 and 1 aren't used (clear and shadow, respectively)
|
||||
}
|
||||
|
||||
// Only the changed rectangle is drawn into the 'renderbuf' array...
|
||||
var c uint16 //, *destPtr;
|
||||
bx := minx - int16(ballx) // X relative to ball bitmap (can be negative)
|
||||
by := miny - int16(bally) // Y relative to ball bitmap (can be negative)
|
||||
bgx := minx // X relative to background bitmap (>= 0)
|
||||
bgy := miny // Y relative to background bitmap (>= 0)
|
||||
var bx1, bgx1 int16 // Loop counters and working vars
|
||||
var p uint8 // 'packed' value of 2 ball pixels
|
||||
var bufIdx int8 = 0
|
||||
|
||||
//tft.setAddrWindow(minx, miny, width, height)
|
||||
|
||||
for y := 0; y < int(height); y++ { // For each row...
|
||||
//destPtr = &renderbuf[bufIdx][0];
|
||||
bx1 = bx // Need to keep the original bx and bgx values,
|
||||
bgx1 = bgx // so copies of them are made here (and changed in loop below)
|
||||
for x := 0; x < int(width); x++ {
|
||||
var bgidx = int(bgy)*(graphics.SCREENWIDTH/8) + int(bgx1/8)
|
||||
if (bx1 >= 0) && (bx1 < graphics.BALLWIDTH) && // Is current pixel row/column
|
||||
(by >= 0) && (by < graphics.BALLHEIGHT) { // inside the ball bitmap area?
|
||||
// Yes, do ball compositing math...
|
||||
p = graphics.Ball[int(by*(graphics.BALLWIDTH/2))+int(bx1/2)] // Get packed value (2 pixels)
|
||||
if (bx1 & 1) != 0 {
|
||||
c = uint16(p & 0xF)
|
||||
} else {
|
||||
c = uint16(p >> 4)
|
||||
} // Unpack high or low nybble
|
||||
if c == 0 { // Outside ball - just draw grid
|
||||
if graphics.Background[bgidx]&(0x80>>(bgx1&7)) != 0 {
|
||||
c = GRIDCOLOR
|
||||
} else {
|
||||
c = BGCOLOR
|
||||
}
|
||||
} else if c > 1 { // In ball area...
|
||||
c = palette[c]
|
||||
} else { // In shadow area...
|
||||
if graphics.Background[bgidx]&(0x80>>(bgx1&7)) != 0 {
|
||||
c = GRIDSHADOW
|
||||
} else {
|
||||
c = BGSHADOW
|
||||
}
|
||||
}
|
||||
} else { // Outside ball bitmap, just draw background bitmap...
|
||||
if graphics.Background[bgidx]&(0x80>>(bgx1&7)) != 0 {
|
||||
c = GRIDCOLOR
|
||||
} else {
|
||||
c = BGCOLOR
|
||||
}
|
||||
}
|
||||
frameBuffer[y*int(width)+x] = c
|
||||
bx1++ // Increment bitmap position counters (X axis)
|
||||
bgx1++
|
||||
}
|
||||
//tft.dmaWait(); // Wait for prior line to complete
|
||||
//tft.writePixels(&renderbuf[bufIdx][0], width, false); // Non-blocking write
|
||||
bufIdx = 1 - bufIdx
|
||||
by++ // Increment bitmap position counters (Y axis)
|
||||
bgy++
|
||||
}
|
||||
|
||||
display.DrawRGBBitmap(minx, miny, frameBuffer[:width*height], width, height)
|
||||
|
||||
// Show approximate frame rate
|
||||
frame++
|
||||
if frame&255 == 0 { // Every 256 frames...
|
||||
elapsed := (time.Now().UnixNano() - startTime) / int64(time.Second)
|
||||
if elapsed > 0 {
|
||||
println(frame/elapsed, " fps")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func DrawBackground() {
|
||||
w, h := display.Size()
|
||||
byteWidth := (w + 7) / 8 // Bitmap scanline pad = whole byte
|
||||
var b uint8
|
||||
for j := int16(0); j < h; j++ {
|
||||
for k := int16(0); k < w; k++ {
|
||||
if k&7 > 0 {
|
||||
b <<= 1
|
||||
} else {
|
||||
b = graphics.Background[j*byteWidth+k/8]
|
||||
}
|
||||
if b&0x80 == 0 {
|
||||
frameBuffer[k] = BGCOLOR
|
||||
} else {
|
||||
frameBuffer[k] = GRIDCOLOR
|
||||
}
|
||||
}
|
||||
display.DrawRGBBitmap(0, j, frameBuffer[0:w], w, 1)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/l293x"
|
||||
)
|
||||
|
||||
func main() {
|
||||
wheel := l293x.New(machine.D10, machine.D11, machine.D12)
|
||||
wheel.Configure()
|
||||
|
||||
for i := 0; i <= 10; i++ {
|
||||
println("Forward")
|
||||
wheel.Forward()
|
||||
time.Sleep(time.Millisecond * 1000)
|
||||
|
||||
println("Stop")
|
||||
wheel.Stop()
|
||||
time.Sleep(time.Millisecond * 1000)
|
||||
|
||||
println("Backward")
|
||||
wheel.Backward()
|
||||
time.Sleep(time.Millisecond * 1000)
|
||||
|
||||
println("Stop")
|
||||
wheel.Stop()
|
||||
time.Sleep(time.Millisecond * 1000)
|
||||
}
|
||||
|
||||
println("Stop")
|
||||
wheel.Stop()
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/l293x"
|
||||
)
|
||||
|
||||
const (
|
||||
maxSpeed = 30000
|
||||
)
|
||||
|
||||
func main() {
|
||||
machine.InitPWM()
|
||||
|
||||
wheel := l293x.NewWithSpeed(machine.D10, machine.D11, machine.PWM{machine.D12})
|
||||
wheel.Configure()
|
||||
|
||||
for i := 0; i <= 10; i++ {
|
||||
println("Forward")
|
||||
var i uint16
|
||||
for i = 0; i < maxSpeed; i += 1000 {
|
||||
wheel.Forward(i)
|
||||
time.Sleep(time.Millisecond * 100)
|
||||
}
|
||||
|
||||
println("Stop")
|
||||
wheel.Stop()
|
||||
time.Sleep(time.Millisecond * 1000)
|
||||
|
||||
println("Backward")
|
||||
for i = 0; i < maxSpeed; i += 1000 {
|
||||
wheel.Backward(i)
|
||||
time.Sleep(time.Millisecond * 100)
|
||||
}
|
||||
|
||||
println("Stop")
|
||||
wheel.Stop()
|
||||
time.Sleep(time.Millisecond * 1000)
|
||||
}
|
||||
|
||||
println("Stop")
|
||||
wheel.Stop()
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/l9110x"
|
||||
)
|
||||
|
||||
func main() {
|
||||
wheel := l9110x.New(machine.D10, machine.D11)
|
||||
wheel.Configure()
|
||||
|
||||
for i := 0; i <= 10; i++ {
|
||||
println("Forward")
|
||||
wheel.Forward()
|
||||
time.Sleep(time.Millisecond * 1000)
|
||||
|
||||
println("Stop")
|
||||
wheel.Stop()
|
||||
time.Sleep(time.Millisecond * 1000)
|
||||
|
||||
println("Backward")
|
||||
wheel.Backward()
|
||||
time.Sleep(time.Millisecond * 1000)
|
||||
|
||||
println("Stop")
|
||||
wheel.Stop()
|
||||
time.Sleep(time.Millisecond * 1000)
|
||||
}
|
||||
|
||||
println("Stop")
|
||||
wheel.Stop()
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/l9110x"
|
||||
)
|
||||
|
||||
const (
|
||||
maxSpeed = 30000
|
||||
)
|
||||
|
||||
func main() {
|
||||
machine.InitPWM()
|
||||
|
||||
wheel := l9110x.NewWithSpeed(machine.PWM{machine.D11}, machine.PWM{machine.D12})
|
||||
wheel.Configure()
|
||||
|
||||
for i := 0; i <= 10; i++ {
|
||||
println("Forward")
|
||||
var i uint16
|
||||
for i = 0; i < maxSpeed; i += 1000 {
|
||||
wheel.Forward(i)
|
||||
time.Sleep(time.Millisecond * 100)
|
||||
}
|
||||
|
||||
println("Stop")
|
||||
wheel.Stop()
|
||||
time.Sleep(time.Millisecond * 1000)
|
||||
|
||||
println("Backward")
|
||||
for i = 0; i < maxSpeed; i += 1000 {
|
||||
wheel.Backward(i)
|
||||
time.Sleep(time.Millisecond * 100)
|
||||
}
|
||||
|
||||
println("Stop")
|
||||
wheel.Stop()
|
||||
time.Sleep(time.Millisecond * 1000)
|
||||
}
|
||||
|
||||
println("Stop")
|
||||
wheel.Stop()
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
// Connects to a MCP3008 ADC via SPI.
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/mcp3008"
|
||||
)
|
||||
|
||||
var (
|
||||
spi = machine.SPI0
|
||||
csPin = machine.D12
|
||||
)
|
||||
|
||||
func main() {
|
||||
spi.Configure(machine.SPIConfig{
|
||||
Frequency: 4000000,
|
||||
Mode: 3})
|
||||
|
||||
adc := mcp3008.New(spi, csPin)
|
||||
adc.Configure()
|
||||
|
||||
// get "CH0" aka "machine.ADC" interface to channel 0 from ADC.
|
||||
p := adc.CH0
|
||||
|
||||
for {
|
||||
val := p.Get()
|
||||
println(val)
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,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)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
// demo of 4-wire touchscreen as described in app note:
|
||||
// http://ww1.microchip.com/downloads/en/Appnotes/doc8091.pdf
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
"math"
|
||||
|
||||
"tinygo.org/x/drivers/touch"
|
||||
"tinygo.org/x/drivers/touch/resistive"
|
||||
)
|
||||
|
||||
var (
|
||||
resistiveTouch = new(resistive.FourWire)
|
||||
)
|
||||
|
||||
const (
|
||||
Xmin = 750
|
||||
Xmax = 325
|
||||
Ymin = 840
|
||||
Ymax = 240
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
// configure touchscreen
|
||||
machine.InitADC()
|
||||
resistiveTouch.Configure(&resistive.FourWireConfig{
|
||||
YP: machine.TOUCH_YD, // y+
|
||||
YM: machine.TOUCH_YU, // y-
|
||||
XP: machine.TOUCH_XR, // x+
|
||||
XM: machine.TOUCH_XL, // x-
|
||||
})
|
||||
|
||||
last := touch.Point{}
|
||||
|
||||
// loop and poll for touches, including performing debouncing
|
||||
debounce := 0
|
||||
for {
|
||||
|
||||
point := resistiveTouch.ReadTouchPoint()
|
||||
touch := touch.Point{}
|
||||
if point.Z>>6 > 100 {
|
||||
touch.X = mapval(point.X>>6, Xmin, Xmax, 0, 240)
|
||||
touch.Y = mapval(point.Y>>6, Ymin, Ymax, 0, 320)
|
||||
touch.Z = point.Z >> 6 / 100
|
||||
} else {
|
||||
touch.X = 0
|
||||
touch.Y = 0
|
||||
touch.Z = 0
|
||||
}
|
||||
|
||||
if last.Z != touch.Z {
|
||||
debounce = 0
|
||||
last = touch
|
||||
} else if math.Abs(float64(touch.X-last.X)) > 4 ||
|
||||
math.Abs(float64(touch.Y-last.Y)) > 4 {
|
||||
debounce = 0
|
||||
last = touch
|
||||
} else if debounce > 1 {
|
||||
debounce = 0
|
||||
HandleTouch(last)
|
||||
} else if touch.Z > 0 {
|
||||
debounce++
|
||||
} else {
|
||||
last = touch
|
||||
debounce = 0
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// based on Arduino's "map" function
|
||||
func mapval(x int, inMin int, inMax int, outMin int, outMax int) int {
|
||||
return (x-inMin)*(outMax-outMin)/(inMax-inMin) + outMin
|
||||
}
|
||||
|
||||
func HandleTouch(touch touch.Point) {
|
||||
println("touch point:", touch.X, touch.Y, touch.Z)
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"image/color"
|
||||
"machine"
|
||||
"math"
|
||||
|
||||
"tinygo.org/x/drivers/ili9341"
|
||||
"tinygo.org/x/drivers/touch"
|
||||
"tinygo.org/x/drivers/touch/resistive"
|
||||
)
|
||||
|
||||
var (
|
||||
resistiveTouch = &resistive.FourWire{}
|
||||
|
||||
display = ili9341.NewParallel(
|
||||
machine.LCD_DATA0,
|
||||
machine.TFT_WR,
|
||||
machine.TFT_DC,
|
||||
machine.TFT_CS,
|
||||
machine.TFT_RESET,
|
||||
machine.TFT_RD,
|
||||
)
|
||||
|
||||
white = color.RGBA{255, 255, 255, 255}
|
||||
black = color.RGBA{0, 0, 0, 255}
|
||||
red = color.RGBA{255, 0, 0, 255}
|
||||
green = color.RGBA{0, 255, 0, 255}
|
||||
blue = color.RGBA{0, 0, 255, 255}
|
||||
magenta = color.RGBA{255, 0, 255, 255}
|
||||
yellow = color.RGBA{255, 255, 0, 255}
|
||||
cyan = color.RGBA{0, 255, 255, 255}
|
||||
|
||||
oldColor color.RGBA
|
||||
currentColor color.RGBA
|
||||
)
|
||||
|
||||
const (
|
||||
penRadius = 3
|
||||
boxSize = 30
|
||||
|
||||
Xmin = 750
|
||||
Xmax = 325
|
||||
Ymin = 840
|
||||
Ymax = 240
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
// configure backlight
|
||||
machine.TFT_BACKLIGHT.Configure(machine.PinConfig{machine.PinOutput})
|
||||
|
||||
// configure touchscreen
|
||||
machine.InitADC()
|
||||
resistiveTouch.Configure(&resistive.FourWireConfig{
|
||||
YP: machine.TOUCH_YD,
|
||||
YM: machine.TOUCH_YU,
|
||||
XP: machine.TOUCH_XR,
|
||||
XM: machine.TOUCH_XL,
|
||||
})
|
||||
|
||||
// configure display
|
||||
display.Configure(ili9341.Config{})
|
||||
|
||||
// fill the background and activate the backlight
|
||||
width, height := display.Size()
|
||||
display.FillRectangle(0, 0, width, height, black)
|
||||
machine.TFT_BACKLIGHT.High()
|
||||
|
||||
// make color selection boxes
|
||||
display.FillRectangle(0, 0, boxSize, boxSize, red)
|
||||
display.FillRectangle(boxSize, 0, boxSize, boxSize, yellow)
|
||||
display.FillRectangle(boxSize*2, 0, boxSize, boxSize, green)
|
||||
display.FillRectangle(boxSize*3, 0, boxSize, boxSize, cyan)
|
||||
display.FillRectangle(boxSize*4, 0, boxSize, boxSize, blue)
|
||||
display.FillRectangle(boxSize*5, 0, boxSize, boxSize, magenta)
|
||||
display.FillRectangle(boxSize*6, 0, boxSize, boxSize, black)
|
||||
display.FillRectangle(boxSize*7, 0, boxSize, boxSize, white)
|
||||
|
||||
// set the initial color to red and draw a box to highlight it
|
||||
oldColor = red
|
||||
currentColor = red
|
||||
display.DrawRectangle(0, 0, boxSize, boxSize, white)
|
||||
|
||||
last := touch.Point{}
|
||||
|
||||
// loop and poll for touches, including performing debouncing
|
||||
debounce := 0
|
||||
for {
|
||||
|
||||
point := resistiveTouch.ReadTouchPoint()
|
||||
touch := touch.Point{}
|
||||
if point.Z>>6 > 100 {
|
||||
rawX := mapval(point.X>>6, Xmin, Xmax, 0, 240)
|
||||
rawY := mapval(point.Y>>6, Ymin, Ymax, 0, 320)
|
||||
touch.X = rawX
|
||||
touch.Y = rawY
|
||||
touch.Z = 1
|
||||
} else {
|
||||
touch.X = 0
|
||||
touch.Y = 0
|
||||
touch.Z = 0
|
||||
}
|
||||
|
||||
if last.Z != touch.Z {
|
||||
debounce = 0
|
||||
last = touch
|
||||
} else if math.Abs(float64(touch.X-last.X)) > 4 ||
|
||||
math.Abs(float64(touch.Y-last.Y)) > 4 {
|
||||
debounce = 0
|
||||
last = touch
|
||||
} else if debounce > 1 {
|
||||
debounce = 0
|
||||
HandleTouch(last)
|
||||
} else if touch.Z > 0 {
|
||||
debounce++
|
||||
} else {
|
||||
last = touch
|
||||
debounce = 0
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// based on Arduino's "map" function
|
||||
func mapval(x int, inMin int, inMax int, outMin int, outMax int) int {
|
||||
return (x-inMin)*(outMax-outMin)/(inMax-inMin) + outMin
|
||||
}
|
||||
|
||||
func HandleTouch(touch touch.Point) {
|
||||
|
||||
if int16(touch.Y) < boxSize {
|
||||
oldColor = currentColor
|
||||
x := int16(touch.X)
|
||||
switch {
|
||||
case x < boxSize:
|
||||
currentColor = red
|
||||
case x < boxSize*2:
|
||||
currentColor = yellow
|
||||
case x < boxSize*3:
|
||||
currentColor = green
|
||||
case x < boxSize*4:
|
||||
currentColor = cyan
|
||||
case x < boxSize*5:
|
||||
currentColor = blue
|
||||
case x < boxSize*6:
|
||||
currentColor = magenta
|
||||
case x < boxSize*7:
|
||||
currentColor = black
|
||||
case x < boxSize*8:
|
||||
currentColor = white
|
||||
}
|
||||
|
||||
if oldColor == currentColor {
|
||||
return
|
||||
}
|
||||
|
||||
display.DrawRectangle((x/boxSize)*boxSize, 0, boxSize, boxSize, white)
|
||||
switch oldColor {
|
||||
case red:
|
||||
x = 0
|
||||
case yellow:
|
||||
x = boxSize
|
||||
case green:
|
||||
x = boxSize * 2
|
||||
case cyan:
|
||||
x = boxSize * 3
|
||||
case blue:
|
||||
x = boxSize * 4
|
||||
case magenta:
|
||||
x = boxSize * 5
|
||||
case black:
|
||||
x = boxSize * 6
|
||||
case white:
|
||||
x = boxSize * 7
|
||||
}
|
||||
display.FillRectangle(int16(x), 0, boxSize, boxSize, oldColor)
|
||||
|
||||
}
|
||||
|
||||
if (int16(touch.Y) - penRadius) > boxSize {
|
||||
display.FillRectangle(
|
||||
int16(touch.X), int16(touch.Y), penRadius*2, penRadius*2, currentColor)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"machine"
|
||||
|
||||
"tinygo.org/x/drivers/veml6070"
|
||||
)
|
||||
|
||||
func main() {
|
||||
machine.I2C0.Configure(machine.I2CConfig{})
|
||||
sensor := veml6070.New(machine.I2C0)
|
||||
|
||||
if !sensor.Configure() {
|
||||
println("VEML6070 could not be configured")
|
||||
return
|
||||
}
|
||||
|
||||
println("VEML6070 configured")
|
||||
|
||||
for {
|
||||
intensity, _ := sensor.ReadUVALightIntensity()
|
||||
println("UVA light intensity:", float32(intensity)/1000.0, "W/(m*m)")
|
||||
|
||||
switch sensor.GetEstimatedRiskLevel(intensity) {
|
||||
case veml6070.UVI_RISK_LOW:
|
||||
println("UV risk level: low")
|
||||
case veml6070.UVI_RISK_MODERATE:
|
||||
println("UV risk level: moderate")
|
||||
case veml6070.UVI_RISK_HIGH:
|
||||
println("UV risk level: high")
|
||||
case veml6070.UVI_RISK_VERY_HIGH:
|
||||
println("UV risk level: very high")
|
||||
case veml6070.UVI_RISK_EXTREME:
|
||||
println("UV risk level: extreme")
|
||||
}
|
||||
|
||||
time.Sleep(2 * time.Second)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,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)
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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")
|
||||
}
|
||||
@@ -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")
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
// +build digispark
|
||||
|
||||
package main
|
||||
|
||||
import "machine"
|
||||
|
||||
// This is the pin assignment for the Digispark only.
|
||||
// Replace neo in the code below to match the pin
|
||||
// that you are using if different.
|
||||
var neo machine.Pin = 0
|
||||
+10
-7
@@ -1,8 +1,7 @@
|
||||
// Connects to an WS2812 RGB LED strip with 10 LEDS, such as
|
||||
// on an Adafruit Circuit Playground Express board.
|
||||
// Connects to an WS2812 RGB LED strip with 10 LEDS.
|
||||
//
|
||||
// Replace machine.NEOPIXELS in the code below to match the pin
|
||||
// that you are using, if you have a different board.
|
||||
// See either the others.go or digispark.go files in this directory
|
||||
// for the neopixels pin assignments.
|
||||
package main
|
||||
|
||||
import (
|
||||
@@ -13,12 +12,15 @@ import (
|
||||
"tinygo.org/x/drivers/ws2812"
|
||||
)
|
||||
|
||||
var leds [10]color.RGBA
|
||||
|
||||
func main() {
|
||||
neo := machine.NEOPIXELS
|
||||
led := machine.LED
|
||||
led.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
|
||||
neo.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
|
||||
ws := ws2812.New(neo)
|
||||
leds := make([]color.RGBA, 10)
|
||||
rg := false
|
||||
|
||||
for {
|
||||
@@ -33,7 +35,8 @@ func main() {
|
||||
}
|
||||
}
|
||||
|
||||
ws.WriteColors(leds)
|
||||
ws.WriteColors(leds[:])
|
||||
led.Set(rg)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
// +build !digispark
|
||||
|
||||
package main
|
||||
|
||||
import "machine"
|
||||
|
||||
// Replace neo in the code below to match the pin
|
||||
// that you are using if different.
|
||||
var neo machine.Pin = machine.NEOPIXELS
|
||||
@@ -0,0 +1,19 @@
|
||||
TinyGo driver for TFT displays using ILI9341 driver chips.
|
||||
|
||||
These displays support 8-bit parallel, 16-bit parallel, or SPI interfaces.
|
||||
|
||||
Examples of such displays include:
|
||||
|
||||
* [Adafruit PyPortal
|
||||
](https://www.adafruit.com/product/4116)
|
||||
* [Adafruit 2.8" Touch Shield V2 (SPI)](http://www.adafruit.com/products/1651)
|
||||
* [Adafruit 2.4" TFT LCD with Touchscreen Breakout w/MicroSD Socket](https://www.adafruit.com/product/2478)
|
||||
* [2.8" TFT LCD with Touchscreen Breakout Board w/MicroSD Socket](https://www.adafruit.com/product/1770)
|
||||
* [2.2" 18-bit color TFT LCD display with microSD card breakout](https://www.adafruit.com/product/1770)
|
||||
* [TFT FeatherWing - 2.4" 320x240 Touchscreen For All Feathers](https://www.adafruit.com/product/3315)
|
||||
|
||||
Currently this driver only supports an 8-bit parallel interface using ATSAMD51
|
||||
(this is the default configuration on PyPortal). It should be relatively
|
||||
straightforward to implement a more generic SPI-based interface as well.
|
||||
Please see `parallel_atsamd51.go` for an example of what needs to be
|
||||
implemented if you are interested in contributing.
|
||||
@@ -0,0 +1,290 @@
|
||||
package ili9341
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"image/color"
|
||||
"machine"
|
||||
"time"
|
||||
)
|
||||
|
||||
const _debug = false
|
||||
|
||||
type Config struct {
|
||||
Width int16
|
||||
Height int16
|
||||
Rotation Rotation
|
||||
}
|
||||
|
||||
type Device struct {
|
||||
width int16
|
||||
height int16
|
||||
rotation Rotation
|
||||
driver driver
|
||||
|
||||
dc machine.Pin
|
||||
cs machine.Pin
|
||||
rst machine.Pin
|
||||
rd machine.Pin
|
||||
}
|
||||
|
||||
func (d *Device) Configure(config Config) {
|
||||
|
||||
if config.Width == 0 {
|
||||
config.Width = TFTWIDTH
|
||||
}
|
||||
if config.Height == 0 {
|
||||
config.Height = TFTHEIGHT
|
||||
}
|
||||
d.width = config.Width
|
||||
d.height = config.Height
|
||||
|
||||
output := machine.PinConfig{machine.PinOutput}
|
||||
|
||||
// configure chip select if there is one
|
||||
if d.cs != machine.NoPin {
|
||||
d.cs.Configure(output)
|
||||
d.cs.High() // deselect
|
||||
}
|
||||
|
||||
d.dc.Configure(output)
|
||||
d.dc.High() // data mode
|
||||
|
||||
// driver-specific configuration
|
||||
d.driver.configure(&config)
|
||||
|
||||
if d.rd != machine.NoPin {
|
||||
d.rd.Configure(output)
|
||||
d.rd.High()
|
||||
}
|
||||
|
||||
// reset the display
|
||||
if d.rst != machine.NoPin {
|
||||
// configure hardware reset if there is one
|
||||
d.rst.Configure(output)
|
||||
d.rst.High()
|
||||
delay(100)
|
||||
d.rst.Low()
|
||||
delay(100)
|
||||
d.rst.High()
|
||||
delay(200)
|
||||
} else {
|
||||
// if no hardware reset, send software reset
|
||||
d.sendCommand(SWRESET, nil)
|
||||
delay(150)
|
||||
}
|
||||
|
||||
initCmd := []byte{
|
||||
0xEF, 3, 0x03, 0x80, 0x02,
|
||||
0xCF, 3, 0x00, 0xC1, 0x30,
|
||||
0xED, 4, 0x64, 0x03, 0x12, 0x81,
|
||||
0xE8, 3, 0x85, 0x00, 0x78,
|
||||
0xCB, 5, 0x39, 0x2C, 0x00, 0x34, 0x02,
|
||||
0xF7, 1, 0x20,
|
||||
0xEA, 2, 0x00, 0x00,
|
||||
PWCTR1, 1, 0x23, // Power control VRH[5:0]
|
||||
PWCTR2, 1, 0x10, // Power control SAP[2:0];BT[3:0]
|
||||
VMCTR1, 2, 0x3e, 0x28, // VCM control
|
||||
VMCTR2, 1, 0x86, // VCM control2
|
||||
MADCTL, 1, 0x48, // Memory Access Control
|
||||
VSCRSADD, 1, 0x00, // Vertical scroll zero
|
||||
PIXFMT, 1, 0x55,
|
||||
FRMCTR1, 2, 0x00, 0x18,
|
||||
DFUNCTR, 3, 0x08, 0x82, 0x27, // Display Function Control
|
||||
0xF2, 1, 0x00, // 3Gamma Function Disable
|
||||
GAMMASET, 1, 0x01, // Gamma curve selected
|
||||
GMCTRP1, 15, 0x0F, 0x31, 0x2B, 0x0C, 0x0E, 0x08, // Set Gamma
|
||||
0x4E, 0xF1, 0x37, 0x07, 0x10, 0x03, 0x0E, 0x09, 0x00,
|
||||
GMCTRN1, 15, 0x00, 0x0E, 0x14, 0x03, 0x11, 0x07, // Set Gamma
|
||||
0x31, 0xC1, 0x48, 0x08, 0x0F, 0x0C, 0x31, 0x36, 0x0F,
|
||||
SLPOUT, 0x80, // Exit Sleep
|
||||
DISPON, 0x80, // Display on
|
||||
0x00, // End of list
|
||||
}
|
||||
for i, c := 0, len(initCmd); i < c; {
|
||||
cmd := initCmd[i]
|
||||
if cmd == 0x00 {
|
||||
break
|
||||
}
|
||||
x := initCmd[i+1]
|
||||
numArgs := int(x & 0x7F)
|
||||
d.sendCommand(cmd, initCmd[i+2:i+2+numArgs])
|
||||
if x&0x80 > 0 {
|
||||
delay(150)
|
||||
}
|
||||
i += numArgs + 2
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Size returns the current size of the display.
|
||||
func (d *Device) Size() (x, y int16) {
|
||||
if d.rotation == 1 || d.rotation == 3 {
|
||||
return d.height, d.width
|
||||
}
|
||||
return d.width, d.height
|
||||
}
|
||||
|
||||
// SetPixel modifies the internal buffer.
|
||||
func (d *Device) SetPixel(x, y int16, c color.RGBA) {
|
||||
d.setWindow(x, y, 1, 1)
|
||||
c565 := RGBATo565(c)
|
||||
d.startWrite()
|
||||
d.driver.write16(c565)
|
||||
d.endWrite()
|
||||
}
|
||||
|
||||
// Display sends the buffer (if any) to the screen.
|
||||
func (d *Device) Display() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Device) DrawRGBBitmap(x, y int16, data []uint16, w, h int16) error {
|
||||
k, i := d.Size()
|
||||
if x < 0 || y < 0 || w <= 0 || h <= 0 ||
|
||||
x >= k || (x+w) > k || y >= i || (y+h) > i {
|
||||
return errors.New("rectangle coordinates outside display area")
|
||||
}
|
||||
d.setWindow(x, y, w, h)
|
||||
d.startWrite()
|
||||
d.driver.write16sl(data)
|
||||
d.endWrite()
|
||||
return nil
|
||||
}
|
||||
|
||||
// FillRectangle fills a rectangle at a given coordinates with a color
|
||||
func (d *Device) FillRectangle(x, y, width, height int16, c color.RGBA) error {
|
||||
k, i := d.Size()
|
||||
if x < 0 || y < 0 || width <= 0 || height <= 0 ||
|
||||
x >= k || (x+width) > k || y >= i || (y+height) > i {
|
||||
return errors.New("rectangle coordinates outside display area")
|
||||
}
|
||||
d.setWindow(x, y, width, height)
|
||||
c565 := RGBATo565(c)
|
||||
d.startWrite()
|
||||
d.driver.write16n(c565, int(width)*int(height))
|
||||
d.endWrite()
|
||||
return nil
|
||||
}
|
||||
|
||||
// DrawRectangle fills a rectangle at a given coordinates with a color
|
||||
func (d *Device) DrawRectangle(x, y, w, h int16, c color.RGBA) error {
|
||||
if err := d.DrawFastHLine(x, x+w-1, y, c); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := d.DrawFastHLine(x, x+w-1, y+h-1, c); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := d.DrawFastVLine(x, y, y+h-1, c); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := d.DrawFastVLine(x+w-1, y, y+h-1, c); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DrawFastVLine draws a vertical line faster than using SetPixel
|
||||
func (d *Device) DrawFastVLine(x, y0, y1 int16, c color.RGBA) error {
|
||||
if y0 > y1 {
|
||||
y0, y1 = y1, y0
|
||||
}
|
||||
return d.FillRectangle(x, y0, 1, y1-y0+1, c)
|
||||
}
|
||||
|
||||
// DrawFastHLine draws a horizontal line faster than using SetPixel
|
||||
func (d *Device) DrawFastHLine(x0, x1, y int16, c color.RGBA) error {
|
||||
if x0 > x1 {
|
||||
x0, x1 = x1, x0
|
||||
}
|
||||
return d.FillRectangle(x0, y, x1-x0+1, 1, c)
|
||||
}
|
||||
|
||||
// FillScreen fills the screen with a given color
|
||||
func (d *Device) FillScreen(c color.RGBA) {
|
||||
if d.rotation == Rotation0 || d.rotation == Rotation180 {
|
||||
d.FillRectangle(0, 0, d.width, d.height, c)
|
||||
} else {
|
||||
d.FillRectangle(0, 0, d.height, d.width, c)
|
||||
}
|
||||
}
|
||||
|
||||
func (d *Device) GetRotation() Rotation {
|
||||
return d.rotation
|
||||
}
|
||||
|
||||
// SetRotation changes the rotation of the device (clock-wise)
|
||||
func (d *Device) SetRotation(rotation Rotation) {
|
||||
madctl := uint8(0)
|
||||
switch rotation % 4 {
|
||||
case 0:
|
||||
madctl = MADCTL_MX | MADCTL_BGR
|
||||
case 1:
|
||||
madctl = MADCTL_MV | MADCTL_BGR
|
||||
case 2:
|
||||
madctl = MADCTL_MY | MADCTL_BGR
|
||||
case 3:
|
||||
madctl = MADCTL_MX | MADCTL_MY | MADCTL_MV | MADCTL_BGR
|
||||
}
|
||||
d.sendCommand(MADCTL, []uint8{madctl})
|
||||
d.rotation = rotation
|
||||
}
|
||||
|
||||
// setWindow prepares the screen to be modified at a given rectangle
|
||||
func (d *Device) setWindow(x, y, w, h int16) {
|
||||
//x += d.columnOffset
|
||||
//y += d.rowOffset
|
||||
d.sendCommand(CASET, []uint8{
|
||||
uint8(x << 8), uint8(x), uint8((x + w - 1) >> 8), uint8(x + w - 1),
|
||||
})
|
||||
d.sendCommand(PASET, []uint8{
|
||||
uint8(y >> 8), uint8(y), uint8((y + h - 1) >> 8), uint8(y + h - 1),
|
||||
})
|
||||
d.sendCommand(RAMWR, nil)
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func (d *Device) startWrite() {
|
||||
if d.cs != machine.NoPin {
|
||||
d.cs.Low()
|
||||
}
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func (d *Device) endWrite() {
|
||||
if d.cs != machine.NoPin {
|
||||
d.cs.High()
|
||||
}
|
||||
}
|
||||
|
||||
func (d *Device) sendCommand(cmd byte, data []byte) {
|
||||
d.startWrite()
|
||||
d.dc.Low()
|
||||
d.driver.write8(cmd)
|
||||
d.dc.High()
|
||||
for _, b := range data {
|
||||
d.driver.write8(b)
|
||||
}
|
||||
d.endWrite()
|
||||
}
|
||||
|
||||
type driver interface {
|
||||
configure(config *Config)
|
||||
write8(b byte)
|
||||
write16(data uint16)
|
||||
write16n(data uint16, n int)
|
||||
write16sl(data []uint16)
|
||||
}
|
||||
|
||||
func delay(m int) {
|
||||
t := time.Now().UnixNano() + int64(time.Duration(m*1000)*time.Microsecond)
|
||||
for time.Now().UnixNano() < t {
|
||||
}
|
||||
}
|
||||
|
||||
// RGBATo565 converts a color.RGBA to uint16 used in the display
|
||||
func RGBATo565(c color.RGBA) uint16 {
|
||||
r, g, b, _ := c.RGBA()
|
||||
return uint16((r & 0xF800) +
|
||||
((g & 0xFC00) >> 5) +
|
||||
((b & 0xF800) >> 11))
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
// +build atsamd51
|
||||
|
||||
package ili9341
|
||||
|
||||
import (
|
||||
"machine"
|
||||
"runtime/volatile"
|
||||
)
|
||||
|
||||
type parallelDriver struct {
|
||||
d0 machine.Pin
|
||||
wr machine.Pin
|
||||
|
||||
setPort *uint32
|
||||
setMask uint32
|
||||
|
||||
clrPort *uint32
|
||||
clrMask uint32
|
||||
|
||||
wrPortSet *uint32
|
||||
wrMaskSet uint32
|
||||
|
||||
wrPortClr *uint32
|
||||
wrMaskClr uint32
|
||||
}
|
||||
|
||||
func NewParallel(d0, wr, dc, cs, rst, rd machine.Pin) *Device {
|
||||
return &Device{
|
||||
dc: dc,
|
||||
cs: cs,
|
||||
rd: rd,
|
||||
rst: rst,
|
||||
driver: ¶llelDriver{
|
||||
d0: d0,
|
||||
wr: wr,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (pd *parallelDriver) configure(config *Config) {
|
||||
output := machine.PinConfig{machine.PinOutput}
|
||||
for pin := pd.d0; pin < pd.d0+8; pin++ {
|
||||
pin.Configure(output)
|
||||
pin.Low()
|
||||
}
|
||||
pd.wr.Configure(output)
|
||||
pd.wr.High()
|
||||
|
||||
pd.setPort, _ = pd.d0.PortMaskSet()
|
||||
pd.setMask = uint32(pd.d0) & 0x1f
|
||||
|
||||
pd.clrPort, _ = (pd.d0).PortMaskClear()
|
||||
pd.clrMask = 0xFF << uint32(pd.d0)
|
||||
|
||||
pd.wrPortSet, pd.wrMaskSet = pd.wr.PortMaskSet()
|
||||
pd.wrPortClr, pd.wrMaskClr = pd.wr.PortMaskClear()
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func (pd *parallelDriver) write8(b byte) {
|
||||
volatile.StoreUint32(pd.clrPort, pd.clrMask)
|
||||
volatile.StoreUint32(pd.setPort, uint32(b)<<pd.setMask)
|
||||
volatile.StoreUint32(pd.wrPortClr, pd.wrMaskClr)
|
||||
volatile.StoreUint32(pd.wrPortSet, pd.wrMaskSet)
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func (pd *parallelDriver) write16(data uint16) {
|
||||
pd.write8(byte(data >> 8))
|
||||
pd.write8(byte(data))
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func (pd *parallelDriver) write16n(data uint16, n int) {
|
||||
for i := 0; i < n; i++ {
|
||||
pd.write8(byte(data >> 8))
|
||||
pd.write8(byte(data))
|
||||
}
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func (pd *parallelDriver) write16sl(data []uint16) {
|
||||
for i, c := 0, len(data); i < c; i++ {
|
||||
pd.write8(byte(data[i] >> 8))
|
||||
pd.write8(byte(data[i]))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
package ili9341
|
||||
|
||||
type Rotation uint8
|
||||
|
||||
const (
|
||||
|
||||
// register constants based on source:
|
||||
// https://github.com/adafruit/Adafruit_ILI9341/blob/master/Adafruit_ILI9341.h
|
||||
|
||||
TFTWIDTH = 240 ///< ILI9341 max TFT width
|
||||
TFTHEIGHT = 320 ///< ILI9341 max TFT height
|
||||
|
||||
NOP = 0x00 ///< No-op register
|
||||
SWRESET = 0x01 ///< Software reset register
|
||||
RDDID = 0x04 ///< Read display identification information
|
||||
RDDST = 0x09 ///< Read Display Status
|
||||
|
||||
SLPIN = 0x10 ///< Enter Sleep Mode
|
||||
SLPOUT = 0x11 ///< Sleep Out
|
||||
PTLON = 0x12 ///< Partial Mode ON
|
||||
NORON = 0x13 ///< Normal Display Mode ON
|
||||
|
||||
RDMODE = 0x0A ///< Read Display Power Mode
|
||||
RDMADCTL = 0x0B ///< Read Display MADCTL
|
||||
RDPIXFMT = 0x0C ///< Read Display Pixel Format
|
||||
RDIMGFMT = 0x0D ///< Read Display Image Format
|
||||
RDSELFDIAG = 0x0F ///< Read Display Self-Diagnostic Result
|
||||
|
||||
INVOFF = 0x20 ///< Display Inversion OFF
|
||||
INVON = 0x21 ///< Display Inversion ON
|
||||
GAMMASET = 0x26 ///< Gamma Set
|
||||
DISPOFF = 0x28 ///< Display OFF
|
||||
DISPON = 0x29 ///< Display ON
|
||||
|
||||
CASET = 0x2A ///< Column Address Set
|
||||
PASET = 0x2B ///< Page Address Set
|
||||
RAMWR = 0x2C ///< Memory Write
|
||||
RAMRD = 0x2E ///< Memory Read
|
||||
|
||||
PTLAR = 0x30 ///< Partial Area
|
||||
VSCRDEF = 0x33 ///< Vertical Scrolling Definition
|
||||
MADCTL = 0x36 ///< Memory Access Control
|
||||
VSCRSADD = 0x37 ///< Vertical Scrolling Start Address
|
||||
PIXFMT = 0x3A ///< COLMOD: Pixel Format Set
|
||||
|
||||
FRMCTR1 = 0xB1 ///< Frame Rate Control (In Normal Mode/Full Colors)
|
||||
FRMCTR2 = 0xB2 ///< Frame Rate Control (In Idle Mode/8 colors)
|
||||
FRMCTR3 = 0xB3 ///< Frame Rate control (In Partial Mode/Full Colors)
|
||||
INVCTR = 0xB4 ///< Display Inversion Control
|
||||
DFUNCTR = 0xB6 ///< Display Function Control
|
||||
|
||||
PWCTR1 = 0xC0 ///< Power Control 1
|
||||
PWCTR2 = 0xC1 ///< Power Control 2
|
||||
PWCTR3 = 0xC2 ///< Power Control 3
|
||||
PWCTR4 = 0xC3 ///< Power Control 4
|
||||
PWCTR5 = 0xC4 ///< Power Control 5
|
||||
VMCTR1 = 0xC5 ///< VCOM Control 1
|
||||
VMCTR2 = 0xC7 ///< VCOM Control 2
|
||||
|
||||
RDID1 = 0xDA ///< Read ID 1
|
||||
RDID2 = 0xDB ///< Read ID 2
|
||||
RDID3 = 0xDC ///< Read ID 3
|
||||
RDID4 = 0xDD ///< Read ID 4
|
||||
|
||||
GMCTRP1 = 0xE0 ///< Positive Gamma Correction
|
||||
GMCTRN1 = 0xE1 ///< Negative Gamma Correction
|
||||
//PWCTR6 0xFC
|
||||
|
||||
MADCTL_MY = 0x80 ///< Bottom to top
|
||||
MADCTL_MX = 0x40 ///< Right to left
|
||||
MADCTL_MV = 0x20 ///< Reverse Mode
|
||||
MADCTL_ML = 0x10 ///< LCD refresh Bottom to top
|
||||
MADCTL_RGB = 0x00 ///< Red-Green-Blue pixel order
|
||||
MADCTL_BGR = 0x08 ///< Blue-Green-Red pixel order
|
||||
MADCTL_MH = 0x04 ///< LCD refresh right to left
|
||||
|
||||
)
|
||||
|
||||
const (
|
||||
Rotation0 Rotation = 0
|
||||
Rotation90 Rotation = 1 // 90 degrees clock-wise rotation
|
||||
Rotation180 Rotation = 2
|
||||
Rotation270 Rotation = 3
|
||||
)
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
// Package l293x provides a driver to the L293/L293D H-bridge chip
|
||||
// typically used to control DC motors.
|
||||
//
|
||||
// Datasheet: https://www.ti.com/lit/ds/symlink/l293d.pdf
|
||||
//
|
||||
package l293x // import "tinygo.org/x/drivers/l293x"
|
||||
|
||||
import (
|
||||
"machine"
|
||||
)
|
||||
|
||||
// Device is a motor without speed control.
|
||||
// a1 and a2 are the directional pins.
|
||||
// en is the pin turns the motor on/off.
|
||||
type Device struct {
|
||||
a1, a2 machine.Pin
|
||||
en machine.Pin
|
||||
}
|
||||
|
||||
// New returns a new Motor driver for GPIO-only operation.
|
||||
func New(direction1, direction2, enablePin machine.Pin) Device {
|
||||
return Device{
|
||||
a1: direction1,
|
||||
a2: direction2,
|
||||
en: enablePin,
|
||||
}
|
||||
}
|
||||
|
||||
// Configure configures the Device.
|
||||
func (d *Device) Configure() {
|
||||
d.a1.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
d.a2.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
d.en.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
|
||||
d.Stop()
|
||||
}
|
||||
|
||||
// Forward turns motor on in forward direction.
|
||||
func (d *Device) Forward() {
|
||||
d.a1.High()
|
||||
d.a2.Low()
|
||||
d.en.High()
|
||||
}
|
||||
|
||||
// Backward turns motor on in backward direction.
|
||||
func (d *Device) Backward() {
|
||||
d.a1.Low()
|
||||
d.a2.High()
|
||||
d.en.High()
|
||||
}
|
||||
|
||||
// Stop turns motor off.
|
||||
func (d *Device) Stop() {
|
||||
d.a1.Low()
|
||||
d.a2.Low()
|
||||
d.en.Low()
|
||||
}
|
||||
|
||||
// PWMDevice is a motor with speed control.
|
||||
// a1 and a2 are the directional GPIO pins.
|
||||
// en is the PWM pin that controls the motor speed.
|
||||
type PWMDevice struct {
|
||||
a1, a2 machine.Pin
|
||||
en machine.PWM
|
||||
}
|
||||
|
||||
// NewWithSpeed returns a new PWMMotor driver that uses a PWM pin to control speed.
|
||||
func NewWithSpeed(direction1, direction2 machine.Pin, speedPin machine.PWM) PWMDevice {
|
||||
return PWMDevice{
|
||||
a1: direction1,
|
||||
a2: direction2,
|
||||
en: speedPin,
|
||||
}
|
||||
}
|
||||
|
||||
// Configure configures the PWMDevice.
|
||||
func (d *PWMDevice) Configure() {
|
||||
d.a1.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
d.a2.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
d.en.Configure()
|
||||
|
||||
d.Stop()
|
||||
}
|
||||
|
||||
// Forward turns motor on in forward direction at specific speed.
|
||||
func (d *PWMDevice) Forward(speed uint16) {
|
||||
d.a1.High()
|
||||
d.a2.Low()
|
||||
d.en.Set(speed)
|
||||
}
|
||||
|
||||
// Backward turns motor on in backward direction at specific speed.
|
||||
func (d *PWMDevice) Backward(speed uint16) {
|
||||
d.a1.Low()
|
||||
d.a2.High()
|
||||
d.en.Set(speed)
|
||||
}
|
||||
|
||||
// Stop turns motor off.
|
||||
func (d *PWMDevice) Stop() {
|
||||
d.a1.Low()
|
||||
d.a2.Low()
|
||||
d.en.Set(0)
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
// Package l9110x provides a driver to the L9110/L9110S H-bridge chip
|
||||
// typically used to control DC motors.
|
||||
//
|
||||
// Datasheet: https://www.elecrow.com/download/datasheet-l9110.pdf
|
||||
//
|
||||
package l9110x // import "tinygo.org/x/drivers/l9110x"
|
||||
|
||||
import (
|
||||
"machine"
|
||||
)
|
||||
|
||||
// Device is a motor without speed control.
|
||||
// ia and ib are the directional pins.
|
||||
type Device struct {
|
||||
ia, ib machine.Pin
|
||||
}
|
||||
|
||||
// New returns a new Motor driver for GPIO-only operation.
|
||||
func New(direction1, direction2 machine.Pin) Device {
|
||||
return Device{
|
||||
ia: direction1,
|
||||
ib: direction2,
|
||||
}
|
||||
}
|
||||
|
||||
// Configure configures the Device.
|
||||
func (d *Device) Configure() {
|
||||
d.ia.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
d.ib.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
|
||||
d.Stop()
|
||||
}
|
||||
|
||||
// Forward turns motor on in forward direction.
|
||||
func (d *Device) Forward() {
|
||||
d.ia.High()
|
||||
d.ib.Low()
|
||||
}
|
||||
|
||||
// Backward turns motor on in backward direction.
|
||||
func (d *Device) Backward() {
|
||||
d.ia.Low()
|
||||
d.ib.High()
|
||||
}
|
||||
|
||||
// Stop turns motor off.
|
||||
func (d *Device) Stop() {
|
||||
d.ia.Low()
|
||||
d.ib.Low()
|
||||
}
|
||||
|
||||
// PWMDevice is a motor with speed control.
|
||||
// ia and ib are the directional/speed PWM pins.
|
||||
type PWMDevice struct {
|
||||
ia, ib machine.PWM
|
||||
}
|
||||
|
||||
// NewWithSpeed returns a new PWMMotor driver that uses 2 PWM pins to control both direction and speed.
|
||||
func NewWithSpeed(direction1, direction2 machine.PWM) PWMDevice {
|
||||
return PWMDevice{
|
||||
ia: direction1,
|
||||
ib: direction2,
|
||||
}
|
||||
}
|
||||
|
||||
// Configure configures the PWMDevice.
|
||||
func (d *PWMDevice) Configure() {
|
||||
d.ia.Configure()
|
||||
d.ib.Configure()
|
||||
|
||||
d.Stop()
|
||||
}
|
||||
|
||||
// Forward turns motor on in forward direction at specific speed.
|
||||
func (d *PWMDevice) Forward(speed uint16) {
|
||||
d.ia.Set(speed)
|
||||
d.ib.Set(0)
|
||||
}
|
||||
|
||||
// Backward turns motor on in backward direction at specific speed.
|
||||
func (d *PWMDevice) Backward(speed uint16) {
|
||||
d.ia.Set(0)
|
||||
d.ib.Set(speed)
|
||||
}
|
||||
|
||||
// Stop turns motor off.
|
||||
func (d *PWMDevice) Stop() {
|
||||
d.ia.Set(0)
|
||||
d.ib.Set(0)
|
||||
}
|
||||
@@ -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])))
|
||||
}
|
||||
@@ -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
|
||||
)
|
||||
@@ -0,0 +1,92 @@
|
||||
// Package mcp3008 implements a driver for the MCP3008 Analog to Digital Converter.
|
||||
//
|
||||
// Datasheet: http://ww1.microchip.com/downloads/en/DeviceDoc/21295d.pdf
|
||||
//
|
||||
package mcp3008 // import "tinygo.org/x/drivers/mcp3008"
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"machine"
|
||||
)
|
||||
|
||||
// Device wraps MCP3008 SPI ADC.
|
||||
type Device struct {
|
||||
bus machine.SPI
|
||||
cs machine.Pin
|
||||
tx []byte
|
||||
rx []byte
|
||||
CH0 ADCPin
|
||||
CH1 ADCPin
|
||||
CH2 ADCPin
|
||||
CH3 ADCPin
|
||||
CH4 ADCPin
|
||||
CH5 ADCPin
|
||||
CH6 ADCPin
|
||||
CH7 ADCPin
|
||||
}
|
||||
|
||||
// ADCPin is the implementation of the ADConverter interface.
|
||||
type ADCPin struct {
|
||||
machine.Pin
|
||||
d *Device
|
||||
}
|
||||
|
||||
// New returns a new MCP3008 driver. Pass in a fully configured SPI bus.
|
||||
func New(b machine.SPI, csPin machine.Pin) *Device {
|
||||
d := &Device{bus: b,
|
||||
cs: csPin,
|
||||
tx: make([]byte, 3),
|
||||
rx: make([]byte, 3),
|
||||
}
|
||||
|
||||
// setup all channels
|
||||
d.CH0 = d.GetADC(0)
|
||||
d.CH1 = d.GetADC(1)
|
||||
d.CH2 = d.GetADC(2)
|
||||
d.CH3 = d.GetADC(3)
|
||||
d.CH4 = d.GetADC(4)
|
||||
d.CH5 = d.GetADC(5)
|
||||
d.CH6 = d.GetADC(6)
|
||||
d.CH7 = d.GetADC(7)
|
||||
|
||||
return d
|
||||
}
|
||||
|
||||
// Configure sets up the device for communication
|
||||
func (d *Device) Configure() {
|
||||
d.cs.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
}
|
||||
|
||||
// Read analog data from channel
|
||||
func (d *Device) Read(ch int) (uint16, error) {
|
||||
if ch < 0 || ch > 7 {
|
||||
return 0, errors.New("invalid channel for MCP3008 Read")
|
||||
}
|
||||
|
||||
return d.GetADC(ch).Get(), nil
|
||||
}
|
||||
|
||||
// GetADC returns an ADC for a specific channel.
|
||||
func (d *Device) GetADC(ch int) ADCPin {
|
||||
return ADCPin{machine.Pin(ch), d}
|
||||
}
|
||||
|
||||
// Get the current reading for a specific ADCPin.
|
||||
func (p ADCPin) Get() uint16 {
|
||||
p.d.tx[0] = 0x01
|
||||
p.d.tx[1] = byte(8+p.Pin) << 4
|
||||
p.d.tx[2] = 0x00
|
||||
|
||||
p.d.cs.Low()
|
||||
p.d.bus.Tx(p.d.tx, p.d.rx)
|
||||
|
||||
// scale result to 16bit value like other ADCs
|
||||
result := uint16((p.d.rx[1]&0x3))<<8 + uint16(p.d.rx[2])<<6
|
||||
p.d.cs.High()
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Configure here just for interface compatibility.
|
||||
func (p ADCPin) Configure() {
|
||||
}
|
||||
@@ -0,0 +1,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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
// The following code is a slightly modified version of code taken from the Paho MQTT library.
|
||||
// It is here until TinyGo can compile the "net" package from the standard library, at which time
|
||||
// it can be removed.
|
||||
|
||||
/*
|
||||
* Copyright (c) 2013 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Seth Hoenig
|
||||
* Allan Stockdill-Mander
|
||||
* Mike Robertson
|
||||
*/
|
||||
|
||||
package mqtt
|
||||
|
||||
import (
|
||||
"container/list"
|
||||
"strings"
|
||||
|
||||
"github.com/eclipse/paho.mqtt.golang/packets"
|
||||
)
|
||||
|
||||
// route is a type which associates MQTT Topic strings with a
|
||||
// callback to be executed upon the arrival of a message associated
|
||||
// with a subscription to that topic.
|
||||
type route struct {
|
||||
topic string
|
||||
callback MessageHandler
|
||||
}
|
||||
|
||||
// match takes a slice of strings which represent the route being tested having been split on '/'
|
||||
// separators, and a slice of strings representing the topic string in the published message, similarly
|
||||
// split.
|
||||
// The function determines if the topic string matches the route according to the MQTT topic rules
|
||||
// and returns a boolean of the outcome
|
||||
func match(route []string, topic []string) bool {
|
||||
if len(route) == 0 {
|
||||
if len(topic) == 0 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
if len(topic) == 0 {
|
||||
if route[0] == "#" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
if route[0] == "#" {
|
||||
return true
|
||||
}
|
||||
|
||||
if (route[0] == "+") || (route[0] == topic[0]) {
|
||||
return match(route[1:], topic[1:])
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func routeIncludesTopic(route, topic string) bool {
|
||||
return match(routeSplit(route), strings.Split(topic, "/"))
|
||||
}
|
||||
|
||||
// removes $share and sharename when splitting the route to allow
|
||||
// shared subscription routes to correctly match the topic
|
||||
func routeSplit(route string) []string {
|
||||
var result []string
|
||||
if strings.HasPrefix(route, "$share") {
|
||||
result = strings.Split(route, "/")[2:]
|
||||
} else {
|
||||
result = strings.Split(route, "/")
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// match takes the topic string of the published message and does a basic compare to the
|
||||
// string of the current Route, if they match it returns true
|
||||
func (r *route) match(topic string) bool {
|
||||
return r.topic == topic || routeIncludesTopic(r.topic, topic)
|
||||
}
|
||||
|
||||
type router struct {
|
||||
//sync.RWMutex
|
||||
routes *list.List
|
||||
defaultHandler MessageHandler
|
||||
messages chan *packets.PublishPacket
|
||||
stop chan bool
|
||||
}
|
||||
|
||||
// newRouter returns a new instance of a Router and channel which can be used to tell the Router
|
||||
// to stop
|
||||
func newRouter() (*router, chan bool) {
|
||||
router := &router{routes: list.New(), messages: make(chan *packets.PublishPacket), stop: make(chan bool)}
|
||||
stop := router.stop
|
||||
return router, stop
|
||||
}
|
||||
|
||||
// addRoute takes a topic string and MessageHandler callback. It looks in the current list of
|
||||
// routes to see if there is already a matching Route. If there is it replaces the current
|
||||
// callback with the new one. If not it add a new entry to the list of Routes.
|
||||
func (r *router) addRoute(topic string, callback MessageHandler) {
|
||||
for e := r.routes.Front(); e != nil; e = e.Next() {
|
||||
if e.Value.(*route).match(topic) {
|
||||
r := e.Value.(*route)
|
||||
r.callback = callback
|
||||
return
|
||||
}
|
||||
}
|
||||
r.routes.PushBack(&route{topic: topic, callback: callback})
|
||||
}
|
||||
|
||||
// deleteRoute takes a route string, looks for a matching Route in the list of Routes. If
|
||||
// found it removes the Route from the list.
|
||||
func (r *router) deleteRoute(topic string) {
|
||||
for e := r.routes.Front(); e != nil; e = e.Next() {
|
||||
if e.Value.(*route).match(topic) {
|
||||
r.routes.Remove(e)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// setDefaultHandler assigns a default callback that will be called if no matching Route
|
||||
// is found for an incoming Publish.
|
||||
func (r *router) setDefaultHandler(handler MessageHandler) {
|
||||
r.defaultHandler = handler
|
||||
}
|
||||
|
||||
// matchAndDispatch takes a channel of Message pointers as input and starts a go routine that
|
||||
// takes messages off the channel, matches them against the internal route list and calls the
|
||||
// associated callback (or the defaultHandler, if one exists and no other route matched). If
|
||||
// anything is sent down the stop channel the function will end.
|
||||
func (r *router) matchAndDispatch(messages <-chan *packets.PublishPacket, order bool, client *mqttclient) {
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case message := <-messages:
|
||||
sent := false
|
||||
m := messageFromPublish(message, client.ackFunc(message))
|
||||
handlers := []MessageHandler{}
|
||||
for e := r.routes.Front(); e != nil; e = e.Next() {
|
||||
if e.Value.(*route).match(message.TopicName) {
|
||||
if order {
|
||||
handlers = append(handlers, e.Value.(*route).callback)
|
||||
} else {
|
||||
hd := e.Value.(*route).callback
|
||||
go func() {
|
||||
hd(client, m)
|
||||
//TODO: m.Ack()
|
||||
}()
|
||||
}
|
||||
sent = true
|
||||
}
|
||||
}
|
||||
if !sent && r.defaultHandler != nil {
|
||||
if order {
|
||||
handlers = append(handlers, r.defaultHandler)
|
||||
} else {
|
||||
go func() {
|
||||
r.defaultHandler(client, m)
|
||||
//TODO: m.Ack()
|
||||
}()
|
||||
}
|
||||
}
|
||||
for _, handler := range handlers {
|
||||
func() {
|
||||
handler(client, m)
|
||||
//TODO: m.Ack()
|
||||
}()
|
||||
}
|
||||
case <-r.stop:
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package mqtt
|
||||
|
||||
import "time"
|
||||
|
||||
type mqtttoken struct {
|
||||
err error
|
||||
}
|
||||
|
||||
func (t *mqtttoken) Wait() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (t *mqtttoken) WaitTimeout(time.Duration) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (t *mqtttoken) Error() error {
|
||||
return t.err
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
@@ -0,0 +1,47 @@
|
||||
// Package semihosting implements parts of the ARM semihosting specification,
|
||||
// for communicating over a debug connection.
|
||||
//
|
||||
// If you want to use it in OpenOCD, you have to enable it first with the
|
||||
// following command:
|
||||
//
|
||||
// arm semihosting enable
|
||||
package semihosting
|
||||
|
||||
import (
|
||||
"device/arm"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// IOError is returned by I/O operations when they fail.
|
||||
type IOError struct {
|
||||
BytesWritten int
|
||||
}
|
||||
|
||||
func (e *IOError) Error() string {
|
||||
return "semihosting: I/O error"
|
||||
}
|
||||
|
||||
// Write writes the given data to the given file descriptor. It returns an
|
||||
// *IOError if the write was not successful.
|
||||
func Write(fd uintptr, data []byte) error {
|
||||
if len(data) == 0 {
|
||||
return nil
|
||||
}
|
||||
params := struct {
|
||||
fd uintptr
|
||||
data unsafe.Pointer
|
||||
len int
|
||||
}{
|
||||
fd: fd,
|
||||
data: unsafe.Pointer(&data[0]),
|
||||
len: len(data),
|
||||
}
|
||||
unwritten := arm.SemihostingCall(arm.SemihostingWrite, uintptr(unsafe.Pointer(¶ms)))
|
||||
if unwritten != 0 {
|
||||
// Error: unwritten is the number of bytes not written.
|
||||
return &IOError{
|
||||
BytesWritten: len(data) - unwritten,
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package semihosting
|
||||
|
||||
// These three file descriptors are connected to the host stdin/stdout/stderr,
|
||||
// and can be used for logging.
|
||||
var (
|
||||
Stdin = File{fd: 0}
|
||||
Stdout = File{fd: 1}
|
||||
Stderr = File{fd: 2}
|
||||
)
|
||||
|
||||
// File represents a semihosting file descriptor.
|
||||
type File struct {
|
||||
fd uintptr
|
||||
}
|
||||
|
||||
// Write writes the given data buffer to the file descriptor, returning an error
|
||||
// if the write could not complete successfully.
|
||||
func (f *File) Write(buf []byte) error {
|
||||
return Write(f.fd, buf)
|
||||
}
|
||||
@@ -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
@@ -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.
|
||||
|
||||
@@ -46,6 +46,8 @@ const (
|
||||
PWCTR6 = 0xFC
|
||||
GMCTRP1 = 0xE0
|
||||
GMCTRN1 = 0xE1
|
||||
VSCRDEF = 0x33
|
||||
VSCRSADD = 0x37
|
||||
|
||||
GREENTAB Model = 0
|
||||
MINI80x160 Model = 1
|
||||
|
||||
+25
-50
@@ -76,21 +76,8 @@ func (d *Device) Configure(cfg Config) {
|
||||
d.height = 160
|
||||
}
|
||||
d.rotation = cfg.Rotation
|
||||
|
||||
if cfg.RowOffset != 0 {
|
||||
d.rowOffset = cfg.RowOffset
|
||||
} else {
|
||||
d.rowOffset = 1
|
||||
}
|
||||
if cfg.ColumnOffset != 0 {
|
||||
d.columnOffset = cfg.ColumnOffset
|
||||
} else {
|
||||
if d.model == MINI80x160 {
|
||||
d.columnOffset = 26
|
||||
} else {
|
||||
d.columnOffset = 2
|
||||
}
|
||||
}
|
||||
d.rowOffset = cfg.RowOffset
|
||||
d.columnOffset = cfg.ColumnOffset
|
||||
|
||||
d.batchLength = d.width
|
||||
if d.height > d.width {
|
||||
@@ -151,29 +138,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
|
||||
@@ -257,6 +224,27 @@ func (d *Device) setWindow(x, y, w, h int16) {
|
||||
d.Command(RAMWR)
|
||||
}
|
||||
|
||||
// SetScrollWindow sets an area to scroll with fixed top and bottom parts of the display
|
||||
func (d *Device) SetScrollArea(topFixedArea, bottomFixedArea int16) {
|
||||
d.Command(VSCRDEF)
|
||||
d.Tx([]uint8{
|
||||
uint8(topFixedArea >> 8), uint8(topFixedArea),
|
||||
uint8(d.height - topFixedArea - bottomFixedArea>>8), uint8(d.height - topFixedArea - bottomFixedArea),
|
||||
uint8(bottomFixedArea >> 8), uint8(bottomFixedArea)},
|
||||
false)
|
||||
}
|
||||
|
||||
// SetScroll sets the vertical scroll address of the display.
|
||||
func (d *Device) SetScroll(line int16) {
|
||||
d.Command(VSCRSADD)
|
||||
d.Tx([]uint8{uint8(line >> 8), uint8(line)}, false)
|
||||
}
|
||||
|
||||
// SpotScroll returns the display to its normal state
|
||||
func (d *Device) StopScroll() {
|
||||
d.Command(NORON)
|
||||
}
|
||||
|
||||
// FillRectangle fills a rectangle at a given coordinates with a color
|
||||
func (d *Device) FillRectangle(x, y, width, height int16, c color.RGBA) error {
|
||||
k, i := d.Size()
|
||||
@@ -383,21 +371,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.
|
||||
|
||||
+9
-27
@@ -66,15 +66,8 @@ func (d *Device) Configure(cfg Config) {
|
||||
d.height = 240
|
||||
}
|
||||
d.rotation = cfg.Rotation
|
||||
|
||||
if cfg.RowOffset != 0 {
|
||||
d.rowOffsetCfg = cfg.RowOffset
|
||||
} else {
|
||||
d.rowOffsetCfg = 80
|
||||
}
|
||||
if cfg.ColumnOffset != 0 {
|
||||
d.columnOffsetCfg = cfg.ColumnOffset
|
||||
}
|
||||
d.rowOffsetCfg = cfg.RowOffset
|
||||
d.columnOffsetCfg = cfg.ColumnOffset
|
||||
|
||||
d.batchLength = int32(d.width)
|
||||
if d.height > d.width {
|
||||
@@ -100,18 +93,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 +175,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)
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
package touch
|
||||
|
||||
// Pointer is a device that is capable of reading a single touch point
|
||||
type Pointer interface {
|
||||
ReadTouchPoint() Point
|
||||
}
|
||||
|
||||
// Point represents the result of reading a single touch point from a screen.
|
||||
// X and Y are the horizontal and vertical coordinates of the touch, while Z
|
||||
// represents the touch pressure. In general, client code will want to inspect
|
||||
// the value of Z to see if it is above some threshold to determine if a touch
|
||||
// is detected at all.
|
||||
type Point struct {
|
||||
X int
|
||||
Y int
|
||||
Z int
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
package resistive
|
||||
|
||||
import (
|
||||
"machine"
|
||||
|
||||
"tinygo.org/x/drivers/touch"
|
||||
)
|
||||
|
||||
// FourWire represents a resistive touchscreen with a four-wire interface as
|
||||
// described in http://ww1.microchip.com/downloads/en/Appnotes/doc8091.pdf
|
||||
type FourWire struct {
|
||||
yp machine.ADC
|
||||
ym machine.ADC
|
||||
xp machine.ADC
|
||||
xm machine.ADC
|
||||
|
||||
readSamples int
|
||||
}
|
||||
|
||||
// FourWireConfig is passed to the Configure method. All of the pins must be
|
||||
// specified for this to be a valid configuration. ReadSamples is optional, and
|
||||
// if not set with default to 2.
|
||||
type FourWireConfig struct {
|
||||
|
||||
// Y+ pin, must be capable of analog reads
|
||||
YP machine.Pin
|
||||
|
||||
// Y- pin, must be capable of analog reads
|
||||
YM machine.Pin
|
||||
|
||||
// X+ pin, must be capable of analog reads
|
||||
XP machine.Pin
|
||||
|
||||
// X- pin, must be capable of analog reads
|
||||
XM machine.Pin
|
||||
|
||||
// If set, each call to ReadTouchPoint() will sample the X, Y, and Z values
|
||||
// and average them. This can help smooth out spurious readings, for example
|
||||
// ones that result from the capacitance of a TFT under the touchscreen
|
||||
ReadSamples int
|
||||
}
|
||||
|
||||
// Configure should be called once before starting to read the device
|
||||
func (res *FourWire) Configure(config *FourWireConfig) error {
|
||||
|
||||
res.yp = machine.ADC{Pin: config.YP}
|
||||
res.ym = machine.ADC{Pin: config.YM}
|
||||
res.xp = machine.ADC{Pin: config.XP}
|
||||
res.xm = machine.ADC{Pin: config.XM}
|
||||
|
||||
if config.ReadSamples < 1 {
|
||||
res.readSamples = 2
|
||||
} else {
|
||||
res.readSamples = config.ReadSamples
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReadTouchPoint reads a single touch.Point from the device. If the device
|
||||
// was configured with ReadSamples > 1, each value will be sampled that many
|
||||
// times and averaged to smooth over spurious results of the analog reads.
|
||||
func (res *FourWire) ReadTouchPoint() (p touch.Point) {
|
||||
p.X = int(sample(res.ReadX, res.readSamples))
|
||||
p.Y = int(sample(res.ReadY, res.readSamples))
|
||||
p.Z = int(sample(res.ReadZ, res.readSamples))
|
||||
return
|
||||
}
|
||||
|
||||
// sample the results of the provided function and average the results
|
||||
func sample(fn func() uint16, numSamples int) (v uint16) {
|
||||
sum := 0
|
||||
for n := 0; n < numSamples; n++ {
|
||||
sum += int(fn())
|
||||
}
|
||||
return uint16(sum / numSamples)
|
||||
}
|
||||
|
||||
// ReadX reads the "raw" X-value on a 16-bit scale without multiple sampling
|
||||
func (res *FourWire) ReadX() uint16 {
|
||||
res.ym.Pin.Configure(machine.PinConfig{Mode: machine.PinInputPulldown})
|
||||
|
||||
res.xp.Pin.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
res.xp.Pin.High()
|
||||
|
||||
res.xm.Pin.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
res.xm.Pin.Low()
|
||||
|
||||
res.yp.Configure()
|
||||
|
||||
return 0xFFFF - res.yp.Get()
|
||||
}
|
||||
|
||||
// ReadY reads the "raw" Y-value on a 16-bit scale without multiple sampling
|
||||
func (res *FourWire) ReadY() uint16 {
|
||||
res.xm.Pin.Configure(machine.PinConfig{Mode: machine.PinInputPulldown})
|
||||
|
||||
res.yp.Pin.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
res.yp.Pin.High()
|
||||
|
||||
res.ym.Pin.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
res.ym.Pin.Low()
|
||||
|
||||
res.xp.Configure()
|
||||
|
||||
return 0xFFFF - res.xp.Get()
|
||||
}
|
||||
|
||||
// ReadZ reads the "raw" Z-value on a 16-bit scale without multiple sampling
|
||||
func (res *FourWire) ReadZ() uint16 {
|
||||
res.xp.Pin.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
res.xp.Pin.Low()
|
||||
|
||||
res.ym.Pin.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
res.ym.Pin.High()
|
||||
|
||||
res.xm.Configure()
|
||||
res.yp.Configure()
|
||||
|
||||
z1 := res.xm.Get()
|
||||
z2 := res.yp.Get()
|
||||
|
||||
return 0xFFFF - (z2 - z1)
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package veml6070
|
||||
|
||||
// I2C addresses and other constants
|
||||
|
||||
const (
|
||||
ADDR_L = 0x38 // 7bit address of the VEML6070 (write, read)
|
||||
ADDR_H = 0x39 // 7bit address of the VEML6070 (read)
|
||||
)
|
||||
|
||||
// Some possible values for resistance value (in ohm) of VEML6070 calibration resistor
|
||||
const (
|
||||
RSET_240K = 240000
|
||||
RSET_270K = 270000
|
||||
RSET_300K = 300000
|
||||
RSET_600K = 600000
|
||||
)
|
||||
|
||||
// Possible values for integration time of VEML6070
|
||||
// (internally represents the config register bit mask)
|
||||
const (
|
||||
IT_HALF = 0x00
|
||||
IT_1 = 0x04
|
||||
IT_2 = 0x08
|
||||
IT_4 = 0x0C
|
||||
)
|
||||
|
||||
// Possible values for UVI (UV index) risk level estimations - the VEML6070 can
|
||||
// only estimate UVI risk levels since it can only sense UVA rays but the vendor
|
||||
// tried to come up with some coarse thresholds, from application notes
|
||||
const (
|
||||
UVI_RISK_LOW = iota
|
||||
UVI_RISK_MODERATE
|
||||
UVI_RISK_HIGH
|
||||
UVI_RISK_VERY_HIGH
|
||||
UVI_RISK_EXTREME
|
||||
)
|
||||
|
||||
// Scale factor in milliseconds / ohm to determine refresh time
|
||||
// (aka sampling time) without IT_FACTOR for any given RSET, from datasheet.
|
||||
// Note: 100.0 milliseconds are applicable for RSET=240 kOhm and IT_FACTOR=1
|
||||
const RSET_TO_REFRESHTIME_SCALE = 100.0 / RSET_240K
|
||||
|
||||
// The refresh time in milliseconds for which NORMALIZED_UVA_SENSITIVITY
|
||||
// is applicable to a step count
|
||||
const NORMALIZED_REFRESHTIME = 100.0
|
||||
|
||||
// The UVA sensitivity in mW/(m*m)/step which is applicable to a step count
|
||||
// normalized to the NORMALIZED_REFRESHTIME, from datasheet for RSET=240 kOhm
|
||||
// and IT_FACTOR=1
|
||||
const NORMALIZED_UVA_SENSITIVITY = 50.0
|
||||
|
||||
// Config register
|
||||
|
||||
// Possible values for shutdown
|
||||
const (
|
||||
CONFIG_SD_DISABLE = 0x00
|
||||
CONFIG_SD_ENABLE = 0x01
|
||||
)
|
||||
|
||||
// Enable / disable
|
||||
const (
|
||||
CONFIG_DEFAULTS = 0x02
|
||||
CONFIG_ENABLE = CONFIG_SD_DISABLE | CONFIG_DEFAULTS
|
||||
CONFIG_DISABLE = CONFIG_SD_ENABLE | CONFIG_DEFAULTS
|
||||
)
|
||||
@@ -0,0 +1,140 @@
|
||||
// Package veml6070 provides a driver for the VEML6070 digital UV light sensor
|
||||
// by Vishay.
|
||||
//
|
||||
// Datasheet:
|
||||
// https://www.vishay.com/docs/84277/veml6070.pdf
|
||||
// Application Notes:
|
||||
// https://www.vishay.com/docs/84310/designingveml6070.pdf
|
||||
//
|
||||
package veml6070 // import "tinygo.org/x/drivers/veml6070"
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"machine"
|
||||
)
|
||||
|
||||
// Device wraps an I2C connection to a VEML6070 device.
|
||||
type Device struct {
|
||||
bus machine.I2C
|
||||
AddressLow uint16
|
||||
AddressHigh uint16
|
||||
RSET uint32
|
||||
IT uint8
|
||||
}
|
||||
|
||||
// New creates a new VEML6070 connection. The I2C bus must already be
|
||||
// configured.
|
||||
//
|
||||
// This function only creates the Device object, it does not initialize the device.
|
||||
// You must call Configure() first in order to use the device itself.
|
||||
func New(bus machine.I2C) Device {
|
||||
return Device{
|
||||
bus: bus,
|
||||
AddressLow: ADDR_L,
|
||||
AddressHigh: ADDR_H,
|
||||
RSET: RSET_240K,
|
||||
// Note: default to maximum to get as much precision as possible since
|
||||
// raw data values larger than 16 bit can hardly occur with RSET below
|
||||
// 300 kOhm in real world applications. Power saving due to shorter
|
||||
// sampling time might be a reason to reduce this.
|
||||
IT: IT_4,
|
||||
}
|
||||
}
|
||||
|
||||
// Configure sets up the device for communication
|
||||
func (d *Device) Configure() bool {
|
||||
// save power by shutdown as early as possible, also serves as presence test
|
||||
if err := d.disable(); err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// ReadUVALightIntensity returns the UVA light intensity (irradiance)
|
||||
// in milli Watt per square meter (mW/(m*m))
|
||||
func (d *Device) ReadUVALightIntensity() (uint32, error) {
|
||||
var err2 error
|
||||
|
||||
if err := d.enable(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// wait two times the refresh time to allow completion of a previous cycle
|
||||
// with old settings (worst case)
|
||||
time.Sleep(time.Duration(d.getRefreshTime()) * 2 * time.Millisecond)
|
||||
|
||||
msb, err2 := d.readData(d.AddressHigh)
|
||||
if err2 != nil {
|
||||
return 0, err2
|
||||
}
|
||||
|
||||
lsb, err2 := d.readData(d.AddressLow)
|
||||
if err2 != nil {
|
||||
return 0, err2
|
||||
}
|
||||
|
||||
if err := d.disable(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
rawData := (uint32(msb) << 8) | uint32(lsb)
|
||||
|
||||
// normalize raw data (step count sampled in d.getRefreshTime()) into the
|
||||
// linearly scaled normalized data (step count sampled in 100ms) for which
|
||||
// we know the UVA sensitivity
|
||||
normalizedData := float32(rawData) * NORMALIZED_REFRESHTIME / d.getRefreshTime()
|
||||
|
||||
// now we can calculate the absolute UVA power detected combining normalized
|
||||
// data with known UVA sensitivity for this data, from datasheet
|
||||
intensity := normalizedData * NORMALIZED_UVA_SENSITIVITY // mW/(m*m)
|
||||
|
||||
return uint32(intensity + 0.5), nil
|
||||
}
|
||||
|
||||
// GetEstimatedRiskLevel returns estimated risk level from comparing UVA light
|
||||
// intensity values in mW/(m*m) with thresholds calculated from application notes
|
||||
func (d *Device) GetEstimatedRiskLevel(intensity uint32) uint8 {
|
||||
if intensity <= 24888 {
|
||||
return UVI_RISK_LOW
|
||||
} else if intensity <= 49800 {
|
||||
return UVI_RISK_MODERATE
|
||||
} else if intensity <= 66400 {
|
||||
return UVI_RISK_HIGH
|
||||
} else if intensity <= 91288 {
|
||||
return UVI_RISK_VERY_HIGH
|
||||
} else {
|
||||
return UVI_RISK_EXTREME
|
||||
}
|
||||
}
|
||||
|
||||
func (d *Device) disable() error {
|
||||
return d.bus.Tx(uint16(d.AddressLow), []byte{CONFIG_DISABLE}, nil)
|
||||
}
|
||||
|
||||
func (d *Device) enable() error {
|
||||
return d.bus.Tx(uint16(d.AddressLow), []byte{CONFIG_ENABLE | d.IT}, nil)
|
||||
}
|
||||
|
||||
func (d *Device) readData(address uint16) (byte, error) {
|
||||
data := []byte{0}
|
||||
err := machine.I2C0.Tx(address, []byte{}, data)
|
||||
return data[0], err
|
||||
}
|
||||
|
||||
// getRefreshTime returns the refresh time (aka sample time) in milliseconds
|
||||
func (d *Device) getRefreshTime() float32 {
|
||||
var it float32
|
||||
switch d.IT {
|
||||
case IT_HALF:
|
||||
it = 0.5
|
||||
case IT_1:
|
||||
it = 1
|
||||
case IT_2:
|
||||
it = 2
|
||||
case IT_4:
|
||||
it = 4
|
||||
}
|
||||
return float32(d.RSET) * RSET_TO_REFRESHTIME_SCALE * it
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package drivers
|
||||
|
||||
// Version returns a user-readable string showing the version of the drivers package for support purposes.
|
||||
// Update this value before release of new version of software.
|
||||
const Version = "0.10.0"
|
||||
@@ -0,0 +1,28 @@
|
||||
# docker build -t wifinina .
|
||||
# docker run wifinina -v "../build/wifinina:/src/build"
|
||||
|
||||
FROM debian:stable-slim AS esp
|
||||
WORKDIR /src
|
||||
|
||||
RUN apt-get clean && apt-get update && \
|
||||
apt-get install -y sudo wget gcc git wget libncurses-dev flex bison gperf build-essential \
|
||||
python python-pip python-setuptools python-serial python-cryptography python-future python-pyparsing make
|
||||
|
||||
RUN mkdir /src/wifinina && \
|
||||
cd /src/wifinina && \
|
||||
wget https://dl.espressif.com/dl/xtensa-esp32-elf-linux64-1.22.0-80-g6c4433a-5.2.0.tar.gz && \
|
||||
mkdir -p /src/esp && \
|
||||
cd /src/esp && \
|
||||
tar -xzf /src/wifinina/xtensa-esp32-elf-linux64-1.22.0-80-g6c4433a-5.2.0.tar.gz
|
||||
|
||||
RUN cd /src/esp && \
|
||||
git clone --branch v3.3.1 --recursive https://github.com/espressif/esp-idf.git
|
||||
|
||||
FROM esp AS nina
|
||||
|
||||
RUN cd /src/esp && \
|
||||
git clone https://github.com/arduino/nina-fw.git
|
||||
|
||||
COPY ./firmware.sh /src
|
||||
RUN chmod +x /src/firmware.sh
|
||||
ENTRYPOINT ["/src/firmware.sh"]
|
||||
@@ -0,0 +1,58 @@
|
||||
# WifiNINA Driver
|
||||
|
||||
This package provides a driver to use a separate connected WiFi processor ESP32 for TCP/UDP communication.
|
||||
|
||||
The way this driver works is by using the SPI interface of your microcontroller to communicate with the WiFi chip using the Arduino SPI command set.
|
||||
|
||||
## Using the WiFiNINA Driver
|
||||
|
||||
For information on how to use this driver, please take a look at the examples located in the [examples/wifinina](../examples/wifinina) directory.
|
||||
|
||||
## WiFiNINA Firmware Installation
|
||||
|
||||
**PLEASE NOTE: New Arduino Nano33 IoT boards already have the WiFiNINA firmware pre-installed, so you should not need to install the firmware yourself.**
|
||||
|
||||
In order to use this driver, you must have the WiFiNINA firmware installed on the ESP32 chip. If it is already installed, you can just use it. You do not need to build and flash the firmware again.
|
||||
|
||||
### Building the WifiNINA firmware
|
||||
|
||||
We have provided a Dockerfile that can build the needed firmware.
|
||||
|
||||
```shell
|
||||
docker build -t wifinina ./wifinina/
|
||||
docker run -v "$(pwd)/build:/src/build" wifinina
|
||||
```
|
||||
|
||||
This will put the firmware files into the `build` directory. Now you can flash them to the ESP32 chip.
|
||||
|
||||
### Installing esptool to flash WifiNINA firmware
|
||||
|
||||
In order to flash the firmware, you need to use Python to install the `esptool` package.
|
||||
|
||||
```shell
|
||||
pip install esptool
|
||||
```
|
||||
|
||||
Once you have installed `esptool` you can follow the correct procedure for flashing your board.
|
||||
|
||||
### Installing on Arduino Nano33 IoT
|
||||
|
||||
The Arduino Nano33 IoT board has the WiFiNINA firmware flashed onto the onboard NINA-W102 chip out of the box.
|
||||
|
||||
Flashing the firmware is only necessary on the Arduino Nano33 IoT in order to upgrade or if other firmware was installed previously.
|
||||
|
||||
If you do want to install the firmware on the Arduino Nano33 IoT board's built-in NINA-W102 chip, you will need to first build the firmware as described above.
|
||||
|
||||
To flash this firmware on the Arduino Nano33 IoT you will need to follow the following procedure using the Arduino IDE software:
|
||||
|
||||
- Install _Arduino SAMD Boards_ from the Boards Manager.
|
||||
- Install _WiFiNINA_ from the Library Manager.
|
||||
- Using the normal Arduino software, load the `SerialNINAPassthrough` sketch on to the board (in File -> Examples -> WiFiNINA-> Tools).
|
||||
|
||||
Now you can flash the WifiNINA firmware using the `esptool` script:
|
||||
|
||||
```shell
|
||||
python esptool.py --chip esp32 --port /dev/ttyACM0 --baud 115200 --before no_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq 40m --flash_size detect 0x1000 build/bootloader.bin 0xf000 build/phy_init_data.bin 0x30000 build/nina-fw.bin 0x8000 build/partitions.bin
|
||||
```
|
||||
|
||||
You only need to do this one time, and then the correct WiFiNINA firmware will be on the NINA chip, and you can just flash the Arduino Nano33 IoT board using TinyGo. We should be able to remove some of these steps in a future release of this software.
|
||||
@@ -0,0 +1,10 @@
|
||||
#!/bin/bash
|
||||
cd /src/esp/nina-fw
|
||||
export PATH=/src/esp/xtensa-esp32-elf/bin:$PATH
|
||||
export IDF_PATH=/src/esp/esp-idf
|
||||
make firmware
|
||||
cp /src/esp/nina-fw/build/bootloader/bootloader.bin /src/build/
|
||||
cp /src/esp/nina-fw/build/phy_init_data.bin /src/build/
|
||||
cp /src/esp/nina-fw/build/nina-fw.bin /src/build/
|
||||
cp /src/esp/nina-fw/build/partitions.bin /src/build/
|
||||
cd -
|
||||
@@ -0,0 +1,3 @@
|
||||
WiFiNINA protocol
|
||||
=================
|
||||
|
||||
+213
@@ -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
|
||||
}
|
||||
@@ -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() {
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,50 @@
|
||||
// +build digispark
|
||||
|
||||
package ws2812
|
||||
|
||||
// This file implements the WS2812 protocol for 16.5MHz Digispark AVR microcontrollers.
|
||||
// This is a slightly different implementation than the one for the atmega to work around a compiler bug.
|
||||
|
||||
import (
|
||||
"device/avr"
|
||||
)
|
||||
|
||||
// Send a single byte using the WS2812 protocol.
|
||||
func (d Device) WriteByte(c byte) error {
|
||||
// For the AVR at 16MHz
|
||||
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: 4 cycles or 250ns
|
||||
// T0L: 14 cycles or 875ns -> together 18 cycles or 1125ns
|
||||
// T1H: 9 cycles or 562ns
|
||||
// T1L: 8 cycles or 500ns -> together 17 cycles or 1062ns
|
||||
avr.AsmFull(`
|
||||
send_bit:
|
||||
st {portSet}, {maskSet} ; [2] set output high
|
||||
lsl {value} ; [1] shift off the next bit, store it in C
|
||||
brcs skip_store ; [1/2] branch if this bit is high (long pulse)
|
||||
st {portClear}, {maskClear} ; [2] set output low (short pulse)
|
||||
skip_store:
|
||||
nop ; [4] wait before changing the output again
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
st {portClear}, {maskClear} ; [2] set output low (end of pulse)
|
||||
nop ; [3]
|
||||
nop
|
||||
nop
|
||||
subi {i}, 1 ; [1] subtract one (for the loop)
|
||||
brne send_bit ; [1/2] send the next bit, if not at the end of the loop
|
||||
`, map[string]interface{}{
|
||||
"value": c,
|
||||
"i": byte(8),
|
||||
"maskSet": maskSet,
|
||||
"portSet": portSet,
|
||||
"maskClear": maskClear,
|
||||
"portClear": portClear,
|
||||
})
|
||||
return nil
|
||||
}
|
||||
@@ -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
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// +build nrf52
|
||||
// +build nrf52 nrf52840
|
||||
|
||||
package ws2812
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user