mirror of
https://github.com/tinygo-org/drivers.git
synced 2026-07-29 12:08:40 +00:00
Compare commits
52 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9f23761c5e | |||
| 2ea620026b | |||
| 04be2320b7 | |||
| b1529dcf7a | |||
| 1987f424ad | |||
| 6f213e97c3 | |||
| ebceed6014 | |||
| 08cc84ce48 | |||
| 0c7b6c0d3c | |||
| 8b11387d30 | |||
| 41c6e3be0b | |||
| 583e80026a | |||
| c7cbd7c6cd | |||
| 12ac4c2c06 | |||
| 6380ad5ed5 | |||
| d61fe99ef1 | |||
| 8c7eed8d38 | |||
| fbe36b62fd | |||
| 0519540d0c | |||
| 2034f92d11 | |||
| 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 |
@@ -1,3 +1,81 @@
|
||||
0.12.0
|
||||
---
|
||||
- **new devices**
|
||||
- hcsr04: Added HC-SR04 ultrasonic distance sensor. (#143)
|
||||
- spi/qspi: Low-level IO driver for serial flash memory via SPI and QSPI (#124)
|
||||
- tmp102: TMP102 low-power digital temperature sensor (#141)
|
||||
- amg88xx: AMG88xx thermal camera module
|
||||
- **bugfixes**
|
||||
- mqtt: reduce use of goroutines in router to not start a new goroutine for each invocation of each callback
|
||||
|
||||
0.11.0
|
||||
---
|
||||
- **new devices**
|
||||
- shiftregister: Support for various shift register chips (#135)
|
||||
- **enhancements**
|
||||
- shifter: simplify API surface for PyBadge (#137)
|
||||
- shifter: new API for shifter driver
|
||||
- mqtt: use buffered channels for incoming messages to handle bursts
|
||||
- ili9341: Adding scroll functionality (#121)
|
||||
- **bugfixes**
|
||||
- wifinina: fix typo on StartScanNetworks
|
||||
- ili9341: various bugfixes for display
|
||||
- **examples**
|
||||
- semihosting: add example
|
||||
- **docs**
|
||||
- readme: Use degree sign instead of ordinal
|
||||
- all: fix celsius symbol in all code comments
|
||||
|
||||
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**
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2018-2019 The TinyGo Authors. All rights reserved.
|
||||
Copyright (c) 2018-2020 The TinyGo Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
|
||||
@@ -9,47 +9,127 @@ 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=itsybitsy-m0 ./examples/apa102/itsybitsy-m0/main.go
|
||||
tinygo build -size short -o ./build/test.elf -target=microbit ./examples/at24cx/main.go
|
||||
tinygo build -size short -o ./build/test.elf -target=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=arduino-nano33 ./examples/espat/espconsole/main.go
|
||||
tinygo build -size short -o ./build/test.elf -target=arduino-nano33 ./examples/espat/esphub/main.go
|
||||
tinygo build -size short -o ./build/test.elf -target=arduino-nano33 ./examples/espat/espstation/main.go
|
||||
tinygo build -size short -o ./build/test.elf -target=feather-m0 ./examples/gps/i2c/main.go
|
||||
tinygo build -size short -o ./build/test.elf -target=feather-m0 ./examples/gps/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=arduino-nano33 ./examples/lsm6ds3/main.go
|
||||
tinygo build -size short -o ./build/test.elf -target=itsybitsy-m0 ./examples/mag3110/main.go
|
||||
tinygo build -size short -o ./build/test.elf -target=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.elf -target=trinket-m0 ./examples/veml6070/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=pybadge ./examples/amg88xx
|
||||
@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=itsybitsy-m0 ./examples/flash/console/spi
|
||||
@md5sum ./build/test.hex
|
||||
tinygo build -size short -o ./build/test.hex -target=pyportal ./examples/flash/console/qspi
|
||||
@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=itsybitsy-m0 ./examples/hcsr04/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=pyportal ./examples/ili9341/scroll/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
|
||||
tinygo build -size short -o ./build/test.hex -target=nucleo-f103rb ./examples/shiftregister/main.go
|
||||
@md5sum ./build/test.hex
|
||||
|
||||
test: clean fmt-check smoke-test
|
||||
|
||||
@@ -40,7 +40,7 @@ func main() {
|
||||
|
||||
for {
|
||||
temp, _ := sensor.ReadTemperature()
|
||||
println("Temperature:", float32(temp)/1000, "ºC")
|
||||
println("Temperature:", float32(temp)/1000, "°C")
|
||||
|
||||
pressure, _ := sensor.ReadPressure()
|
||||
println("Pressure", float32(pressure)/100000, "hPa")
|
||||
@@ -52,11 +52,16 @@ func main() {
|
||||
|
||||
## Currently supported devices
|
||||
|
||||
The following 48 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 |
|
||||
| [AMG88xx 8x8 Thermal camera sensor](https://cdn-learn.adafruit.com/assets/assets/000/043/261/original/Grid-EYE_SPECIFICATIONS%28Reference%29.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 |
|
||||
| [BBC micro:bit LED matrix](https://github.com/bbcmicrobit/hardware/blob/master/SCH_BBC-Microbit_V1.3B.pdf) | GPIO |
|
||||
| [BH1750 ambient light sensor](https://www.mouser.com/ds/2/348/bh1750fvi-e-186247.pdf) | I2C |
|
||||
| [BlinkM RGB LED](http://thingm.com/fileadmin/thingm/downloads/BlinkM_datasheet.pdf) | I2C |
|
||||
| [BME280 humidity/pressure sensor](https://cdn-shop.adafruit.com/datasheets/BST-BME280_DS001-10.pdf) | I2C |
|
||||
@@ -64,28 +69,40 @@ func main() {
|
||||
| [Buzzer](https://en.wikipedia.org/wiki/Buzzer#Piezoelectric) | GPIO |
|
||||
| [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 |
|
||||
| [HC-SR04 Ultrasonic distance sensor](https://cdn.sparkfun.com/datasheets/Sensors/Proximity/HCSR04.pdf) | GPIO |
|
||||
| [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 |
|
||||
| [BBC micro:bit LED matrix](https://github.com/bbcmicrobit/hardware/blob/master/SCH_BBC-Microbit_V1.3B.pdf) | GPIO |
|
||||
| [MCP3008 analog to digital converter (ADC)](http://ww1.microchip.com/downloads/en/DeviceDoc/21295d.pdf) | SPI |
|
||||
| [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 (PISO)](https://en.wikipedia.org/wiki/Shift_register#Parallel-in_serial-out_\(PISO\)) | GPIO |
|
||||
| [Shift registers (SIPO)](https://en.wikipedia.org/wiki/Shift_register#Serial-in_parallel-out_(SIPO)) | 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 |
|
||||
| [SPI NOR Flash Memory](https://en.wikipedia.org/wiki/Flash_memory#NOR_flash) | SPI/QSPI |
|
||||
| [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 |
|
||||
| [Stepper motor "Easystepper" controller](https://en.wikipedia.org/wiki/Stepper_motor) | GPIO |
|
||||
| [Thermistor](https://www.farnell.com/datasheets/33552.pdf) | ADC |
|
||||
| [TMP102 I2C Temperature Sensor](https://download.mikroe.com/documents/datasheets/tmp102-data-sheet.pdf) | I2C |
|
||||
| [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 |
|
||||
| [Waveshare 2.13" e-paper display](https://www.waveshare.com/w/upload/e/e6/2.13inch_e-Paper_Datasheet.pdf) | SPI |
|
||||
| [WS2812 RGB LED](https://cdn-shop.adafruit.com/datasheets/WS2812.pdf) | GPIO |
|
||||
|
||||
## Contributing
|
||||
|
||||
@@ -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
|
||||
)
|
||||
@@ -0,0 +1,158 @@
|
||||
// Package amg88xx provides a driver for the AMG88XX Thermal Camera
|
||||
//
|
||||
// Datasheet:
|
||||
// https://cdn-learn.adafruit.com/assets/assets/000/043/261/original/Grid-EYE_SPECIFICATIONS%28Reference%29.pdf
|
||||
package amg88xx // import "tinygo.org/x/drivers/amg88xx"
|
||||
|
||||
import (
|
||||
"machine"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Device wraps an I2C connection to a AMG88xx device.
|
||||
type Device struct {
|
||||
bus machine.I2C
|
||||
Address uint16
|
||||
data []uint8
|
||||
interruptMode InterruptMode
|
||||
interruptEnable uint8
|
||||
}
|
||||
|
||||
type InterruptMode uint8
|
||||
|
||||
type Config struct {
|
||||
}
|
||||
|
||||
// New creates a new AMG88xx 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: AddressHigh,
|
||||
}
|
||||
}
|
||||
|
||||
// Configure sets up the device for communication
|
||||
func (d *Device) Configure(cfg Config) {
|
||||
d.data = make([]uint8, 128)
|
||||
|
||||
d.SetPCTL(NORMAL_MODE)
|
||||
d.SetReset(INITIAL_RESET)
|
||||
d.SetFrameRate(FPS_10)
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
}
|
||||
|
||||
// ReadPixels returns the 64 values (8x8 grid) of the sensor converted to millicelsius
|
||||
func (d *Device) ReadPixels(buffer *[64]int16) {
|
||||
d.bus.ReadRegister(uint8(d.Address), PIXEL_OFFSET, d.data)
|
||||
for i := 0; i < 64; i++ {
|
||||
buffer[i] = int16((uint16(d.data[2*i+1]) << 8) | uint16(d.data[2*i]))
|
||||
if (buffer[i] & (1 << 11)) > 0 { // temperature negative
|
||||
buffer[i] &= ^(1 << 11)
|
||||
buffer[i] = -buffer[i]
|
||||
}
|
||||
buffer[i] *= PIXEL_TEMP_CONVERSION
|
||||
}
|
||||
}
|
||||
|
||||
// SetPCTL sets the PCTL
|
||||
func (d *Device) SetPCTL(pctl uint8) {
|
||||
d.bus.WriteRegister(uint8(d.Address), PCTL, []byte{pctl})
|
||||
}
|
||||
|
||||
// SetReset sets the reset value
|
||||
func (d *Device) SetReset(rst uint8) {
|
||||
d.bus.WriteRegister(uint8(d.Address), RST, []byte{rst})
|
||||
}
|
||||
|
||||
// SetFrameRate configures the frame rate
|
||||
func (d *Device) SetFrameRate(framerate uint8) {
|
||||
d.bus.WriteRegister(uint8(d.Address), FPSC, []byte{framerate & 0x01})
|
||||
}
|
||||
|
||||
// SetMovingAverageMode sets the moving average mode
|
||||
func (d *Device) SetMovingAverageMode(mode bool) {
|
||||
var value uint8
|
||||
if mode {
|
||||
value = 1
|
||||
}
|
||||
d.bus.WriteRegister(uint8(d.Address), AVE, []byte{value << 5})
|
||||
}
|
||||
|
||||
// SetInterruptLevels sets the interrupt levels
|
||||
func (d *Device) SetInterruptLevels(high int16, low int16) {
|
||||
d.SetInterruptLevelsHysteresis(high, low, (high*95)/100)
|
||||
}
|
||||
|
||||
// SetInterruptLevelsHysteresis sets the interrupt levels with hysteresis
|
||||
func (d *Device) SetInterruptLevelsHysteresis(high int16, low int16, hysteresis int16) {
|
||||
high = high / PIXEL_TEMP_CONVERSION
|
||||
if high < -4095 {
|
||||
high = -4095
|
||||
}
|
||||
if high > 4095 {
|
||||
high = 4095
|
||||
}
|
||||
d.bus.WriteRegister(uint8(d.Address), INTHL, []byte{uint8(high & 0xFF)})
|
||||
d.bus.WriteRegister(uint8(d.Address), INTHL, []byte{uint8((high & 0xFF) >> 4)})
|
||||
|
||||
low = low / PIXEL_TEMP_CONVERSION
|
||||
if low < -4095 {
|
||||
low = -4095
|
||||
}
|
||||
if low > 4095 {
|
||||
low = 4095
|
||||
}
|
||||
d.bus.WriteRegister(uint8(d.Address), INTHL, []byte{uint8(low & 0xFF)})
|
||||
d.bus.WriteRegister(uint8(d.Address), INTHL, []byte{uint8((low & 0xFF) >> 4)})
|
||||
|
||||
hysteresis = hysteresis / PIXEL_TEMP_CONVERSION
|
||||
if hysteresis < -4095 {
|
||||
hysteresis = -4095
|
||||
}
|
||||
if hysteresis > 4095 {
|
||||
hysteresis = 4095
|
||||
}
|
||||
d.bus.WriteRegister(uint8(d.Address), INTHL, []byte{uint8(hysteresis & 0xFF)})
|
||||
d.bus.WriteRegister(uint8(d.Address), INTHL, []byte{uint8((hysteresis & 0xFF) >> 4)})
|
||||
}
|
||||
|
||||
// EnableInterrupt enables the interrupt pin on the device
|
||||
func (d *Device) EnableInterrupt() {
|
||||
d.interruptEnable = 1
|
||||
d.bus.WriteRegister(uint8(d.Address), INTC, []byte{((uint8(d.interruptMode) << 1) | d.interruptEnable) & 0x03})
|
||||
}
|
||||
|
||||
// DisableInterrupt disables the interrupt pin on the device
|
||||
func (d *Device) DisableInterrupt() {
|
||||
d.interruptEnable = 0
|
||||
d.bus.WriteRegister(uint8(d.Address), INTC, []byte{((uint8(d.interruptMode) << 1) | d.interruptEnable) & 0x03})
|
||||
}
|
||||
|
||||
// SetInterruptMode sets the interrupt mode
|
||||
func (d *Device) SetInterruptMode(mode InterruptMode) {
|
||||
d.interruptMode = mode
|
||||
d.bus.WriteRegister(uint8(d.Address), INTC, []byte{((uint8(d.interruptMode) << 1) | d.interruptEnable) & 0x03})
|
||||
}
|
||||
|
||||
// GetInterrupt reads the state of the triggered interrupts
|
||||
func (d *Device) GetInterrupt() []uint8 {
|
||||
data := make([]uint8, 8)
|
||||
d.bus.ReadRegister(uint8(d.Address), INT_OFFSET, data)
|
||||
return data
|
||||
}
|
||||
|
||||
// ClearInterrupt clears any triggered interrupts
|
||||
func (d *Device) ClearInterrupt() {
|
||||
d.SetReset(FLAG_RESET)
|
||||
}
|
||||
|
||||
// ReadThermistor reads the onboard thermistor
|
||||
func (d *Device) ReadThermistor() int16 {
|
||||
data := make([]uint8, 2)
|
||||
d.bus.ReadRegister(uint8(d.Address), TTHL, data)
|
||||
return (int16((uint16(data[1])<<8)|uint16(data[0])) * THERMISTOR_CONVERSION) / 10
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package amg88xx
|
||||
|
||||
// The I2C address which this device listens to.
|
||||
const AddressHigh = 0x69
|
||||
const AddressLow = 0x68
|
||||
|
||||
const (
|
||||
PCTL = 0x00
|
||||
RST = 0x01
|
||||
FPSC = 0x02
|
||||
INTC = 0x03
|
||||
STAT = 0x04
|
||||
SCLR = 0x05
|
||||
AVE = 0x07
|
||||
INTHL = 0x08
|
||||
INTHH = 0x09
|
||||
INTLL = 0x0A
|
||||
INTLH = 0x0B
|
||||
IHYSL = 0x0C
|
||||
IHYSH = 0x0D
|
||||
TTHL = 0x0E
|
||||
TTHH = 0x0F
|
||||
INT_OFFSET = 0x010
|
||||
PIXEL_OFFSET = 0x80
|
||||
|
||||
// power modes
|
||||
NORMAL_MODE = 0x00
|
||||
SLEEP_MODE = 0x01
|
||||
STAND_BY_60 = 0x20
|
||||
STAND_BY_10 = 0x21
|
||||
|
||||
// resets
|
||||
FLAG_RESET = 0x30
|
||||
INITIAL_RESET = 0x3F
|
||||
|
||||
// frame rates
|
||||
FPS_10 = 0x00
|
||||
FPS_1 = 0x01
|
||||
|
||||
// interrupt modes
|
||||
DIFFERENCE InterruptMode = 0x00
|
||||
ABSOLUTE_VALUE InterruptMode = 0x01
|
||||
|
||||
PIXEL_TEMP_CONVERSION = 250
|
||||
THERMISTOR_CONVERSION = 625
|
||||
)
|
||||
+1
-1
@@ -112,7 +112,7 @@ func (d *Device) Reset() {
|
||||
d.bus.WriteRegister(uint8(d.Address), CMD_RESET, []byte{0xB6})
|
||||
}
|
||||
|
||||
// ReadTemperature returns the temperature in celsius milli degrees (ºC/1000)
|
||||
// ReadTemperature returns the temperature in celsius milli degrees (°C/1000)
|
||||
func (d *Device) ReadTemperature() (int32, error) {
|
||||
data, err := d.readData()
|
||||
if err != nil {
|
||||
|
||||
+1
-1
@@ -80,7 +80,7 @@ func (d *Device) Configure() {
|
||||
d.calibrationCoefficients.md = readInt(data[20], data[21])
|
||||
}
|
||||
|
||||
// ReadTemperature returns the temperature in celsius milli degrees (ºC/1000).
|
||||
// ReadTemperature returns the temperature in celsius milli degrees (°C/1000).
|
||||
func (d *Device) ReadTemperature() (temperature int32, err error) {
|
||||
rawTemp, err := d.rawTemp()
|
||||
if err != nil {
|
||||
|
||||
+1
-1
@@ -26,7 +26,7 @@
|
||||
//
|
||||
// for {
|
||||
// temp, _ := sensor.ReadTemperature()
|
||||
// println("Temperature:", float32(temp)/1000, "ºC")
|
||||
// println("Temperature:", float32(temp)/1000, "°C")
|
||||
//
|
||||
// pressure, _ := sensor.ReadPressure()
|
||||
// println("Pressure", float32(pressure)/100000, "hPa")
|
||||
|
||||
+100
-20
@@ -10,47 +10,115 @@ import (
|
||||
type Device struct {
|
||||
pins [4]machine.Pin
|
||||
stepDelay int32
|
||||
stepNumber int32
|
||||
stepNumber uint8
|
||||
}
|
||||
|
||||
// New returns a new easystepper driver given 4 pins numbers (not pin object),
|
||||
// number of steps and rpm
|
||||
// DualDevice holds information for controlling 2 motors
|
||||
type DualDevice struct {
|
||||
devices [2]Device
|
||||
}
|
||||
|
||||
// New returns a new easystepper driver given 4 pins, 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, 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 +145,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)
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,56 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"image/color"
|
||||
"machine"
|
||||
|
||||
"tinygo.org/x/drivers/st7735"
|
||||
|
||||
"tinygo.org/x/drivers/amg88xx"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
machine.SPI1.Configure(machine.SPIConfig{
|
||||
SCK: machine.SPI1_SCK_PIN,
|
||||
MOSI: machine.SPI1_MOSI_PIN,
|
||||
MISO: machine.SPI1_MISO_PIN,
|
||||
Frequency: 8000000,
|
||||
})
|
||||
machine.I2C0.Configure(machine.I2CConfig{SCL: machine.SCL_PIN, SDA: machine.SDA_PIN})
|
||||
|
||||
display := st7735.New(machine.SPI1, machine.TFT_RST, machine.TFT_DC, machine.TFT_CS, machine.TFT_LITE)
|
||||
display.Configure(st7735.Config{
|
||||
Rotation: st7735.ROTATION_90,
|
||||
})
|
||||
display.FillScreen(color.RGBA{0, 0, 0, 255})
|
||||
|
||||
camera := amg88xx.New(machine.I2C0)
|
||||
camera.Configure(amg88xx.Config{})
|
||||
|
||||
var data [64]int16
|
||||
var value int16
|
||||
for {
|
||||
// get the values of the sensor in millicelsius
|
||||
camera.ReadPixels(&data)
|
||||
|
||||
for j := int16(0); j < 8; j++ {
|
||||
for i := int16(0); i < 8; i++ {
|
||||
value = data[63-(i+j*8)]
|
||||
// treat anything below 18°C as 18°C
|
||||
if value < 18000 {
|
||||
value = 0
|
||||
} else {
|
||||
value = (value - 18000) / 36
|
||||
// our color array only have 433 values, avoid getting a value that doesn't exist
|
||||
if value > 432 {
|
||||
value = 432
|
||||
}
|
||||
}
|
||||
// show the image on the PyBadge's display
|
||||
display.FillRectangle(16+i*16, j*16, 16, 16, colors[value])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -22,7 +22,7 @@ func main() {
|
||||
|
||||
for {
|
||||
temp, _ := sensor.ReadTemperature()
|
||||
println("Temperature:", strconv.FormatFloat(float64(temp)/1000, 'f', 2, 64), "ºC")
|
||||
println("Temperature:", strconv.FormatFloat(float64(temp)/1000, 'f', 2, 64), "°C")
|
||||
press, _ := sensor.ReadPressure()
|
||||
println("Pressure:", strconv.FormatFloat(float64(press)/100000, 'f', 2, 64), "hPa")
|
||||
hum, _ := sensor.ReadHumidity()
|
||||
|
||||
@@ -22,7 +22,7 @@ func main() {
|
||||
|
||||
for {
|
||||
temp, _ := sensor.ReadTemperature()
|
||||
println("Temperature:", float32(temp)/1000, "ºC")
|
||||
println("Temperature:", float32(temp)/1000, "°C")
|
||||
|
||||
pressure, _ := sensor.ReadPressure()
|
||||
println("Pressure", float32(pressure)/100000, "hPa")
|
||||
|
||||
@@ -38,7 +38,7 @@ func main() {
|
||||
fmt.Printf("Date: %d/%s/%02d %02d:%02d:%02d \r\n", dt.Year(), dt.Month(), dt.Day(), dt.Hour(), dt.Minute(), dt.Second())
|
||||
}
|
||||
temp, _ := rtc.ReadTemperature()
|
||||
fmt.Printf("Temperature: %.2f ºC \r\n", float32(temp)/1000)
|
||||
fmt.Printf("Temperature: %.2f °C \r\n", float32(temp)/1000)
|
||||
|
||||
time.Sleep(time.Second * 1)
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -26,7 +26,7 @@ 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.UART2
|
||||
uart = machine.UART1
|
||||
tx = machine.PA22
|
||||
rx = machine.PA23
|
||||
|
||||
@@ -43,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 {
|
||||
@@ -82,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()
|
||||
@@ -101,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.
|
||||
@@ -24,7 +24,7 @@ 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.UART2
|
||||
uart = machine.UART1
|
||||
tx = machine.PA22
|
||||
rx = machine.PA23
|
||||
|
||||
@@ -43,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
|
||||
}
|
||||
|
||||
@@ -86,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
|
||||
@@ -24,7 +24,7 @@ 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.UART2
|
||||
uart = machine.UART1
|
||||
tx = machine.PA22
|
||||
rx = machine.PA23
|
||||
|
||||
@@ -39,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
|
||||
}
|
||||
|
||||
@@ -71,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,8 +25,9 @@ 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
|
||||
@@ -63,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())
|
||||
@@ -105,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
|
||||
@@ -24,7 +24,7 @@ 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.UART2
|
||||
uart = machine.UART1
|
||||
tx = machine.PA22
|
||||
rx = machine.PA23
|
||||
|
||||
@@ -39,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
|
||||
}
|
||||
|
||||
@@ -55,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
|
||||
@@ -71,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,259 @@
|
||||
package console_example
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"machine"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"tinygo.org/x/drivers/flash"
|
||||
)
|
||||
|
||||
const consoleBufLen = 64
|
||||
const storageBufLen = 512
|
||||
|
||||
var (
|
||||
debug = false
|
||||
|
||||
input [consoleBufLen]byte
|
||||
store [storageBufLen]byte
|
||||
|
||||
console = machine.UART0
|
||||
|
||||
dev *flash.Device
|
||||
|
||||
commands map[string]cmdfunc = map[string]cmdfunc{
|
||||
"": cmdfunc(noop),
|
||||
"erase": cmdfunc(erase),
|
||||
"lsblk": cmdfunc(lsblk),
|
||||
"write": cmdfunc(write),
|
||||
"xxd": cmdfunc(xxd),
|
||||
}
|
||||
)
|
||||
|
||||
type cmdfunc func(argv []string)
|
||||
|
||||
const (
|
||||
StateInput = iota
|
||||
StateEscape
|
||||
StateEscBrc
|
||||
StateCSI
|
||||
)
|
||||
|
||||
func RunFor(device *flash.Device) {
|
||||
|
||||
dev = device
|
||||
dev.Configure(&flash.DeviceConfig{
|
||||
Identifier: flash.DefaultDeviceIdentifier,
|
||||
})
|
||||
|
||||
prompt()
|
||||
|
||||
var state = StateInput
|
||||
|
||||
for i := 0; ; {
|
||||
if console.Buffered() > 0 {
|
||||
data, _ := console.ReadByte()
|
||||
if debug {
|
||||
fmt.Printf("\rdata: %x\r\n\r", data)
|
||||
prompt()
|
||||
console.Write(input[:i])
|
||||
}
|
||||
switch state {
|
||||
case StateInput:
|
||||
switch data {
|
||||
case 0x8:
|
||||
fallthrough
|
||||
case 0x7f: // this is probably wrong... works on my machine tho :)
|
||||
// backspace
|
||||
if i > 0 {
|
||||
i -= 1
|
||||
console.Write([]byte{0x8, 0x20, 0x8})
|
||||
}
|
||||
case 13:
|
||||
// return key
|
||||
console.Write([]byte("\r\n"))
|
||||
runCommand(string(input[:i]))
|
||||
prompt()
|
||||
|
||||
i = 0
|
||||
continue
|
||||
case 27:
|
||||
// escape
|
||||
state = StateEscape
|
||||
default:
|
||||
// anything else, just echo the character if it is printable
|
||||
if strconv.IsPrint(rune(data)) {
|
||||
if i < (consoleBufLen - 1) {
|
||||
console.WriteByte(data)
|
||||
input[i] = data
|
||||
i++
|
||||
}
|
||||
}
|
||||
}
|
||||
case StateEscape:
|
||||
switch data {
|
||||
case 0x5b:
|
||||
state = StateEscBrc
|
||||
default:
|
||||
state = StateInput
|
||||
}
|
||||
default:
|
||||
// TODO: handle escape sequences
|
||||
state = StateInput
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func runCommand(line string) {
|
||||
argv := strings.SplitN(strings.TrimSpace(line), " ", -1)
|
||||
cmd := argv[0]
|
||||
cmdfn, ok := commands[cmd]
|
||||
if !ok {
|
||||
println("unknown command: " + line)
|
||||
return
|
||||
}
|
||||
cmdfn(argv)
|
||||
}
|
||||
|
||||
func noop(argv []string) {}
|
||||
|
||||
func lsblk(argv []string) {
|
||||
attrs := dev.Attrs()
|
||||
status1, _ := dev.ReadStatus()
|
||||
status2, _ := dev.ReadStatus2()
|
||||
serialNumber1, _ := dev.ReadSerialNumber()
|
||||
fmt.Printf(
|
||||
"\n-------------------------------------\r\n"+
|
||||
" Device Information: \r\n"+
|
||||
"-------------------------------------\r\n"+
|
||||
" JEDEC ID: %v\r\n"+
|
||||
" Serial: %v\r\n"+
|
||||
" Status 1: %02x\r\n"+
|
||||
" Status 2: %02x\r\n"+
|
||||
" \r\n"+
|
||||
" Max clock speed (MHz): %d\r\n"+
|
||||
" Has Sector Protection: %t\r\n"+
|
||||
" Supports Fast Reads: %t\r\n"+
|
||||
" Supports QSPI Reads: %t\r\n"+
|
||||
" Supports QSPI Write: %t\r\n"+
|
||||
" Write Status Split: %t\r\n"+
|
||||
" Single Status Byte: %t\r\n"+
|
||||
"-------------------------------------\r\n\r\n",
|
||||
attrs.JedecID,
|
||||
serialNumber1,
|
||||
status1,
|
||||
status2,
|
||||
attrs.MaxClockSpeedMHz,
|
||||
attrs.HasSectorProtection,
|
||||
attrs.SupportsFastRead,
|
||||
attrs.SupportsQSPI,
|
||||
attrs.SupportsQSPIWrites,
|
||||
attrs.WriteStatusSplit,
|
||||
attrs.SingleStatusByte,
|
||||
)
|
||||
}
|
||||
|
||||
func erase(argv []string) {
|
||||
if len(argv) < 3 {
|
||||
println("usage: erase <chip|block|sector> <bytes>")
|
||||
return
|
||||
}
|
||||
var err error
|
||||
var addr uint64 = 0x0
|
||||
if addr, err = strconv.ParseUint(argv[2], 16, 32); err != nil {
|
||||
println("Invalid address: " + err.Error() + "\r\n")
|
||||
return
|
||||
}
|
||||
if argv[1] == "block" {
|
||||
if err = dev.EraseBlock(uint32(addr)); err != nil {
|
||||
println("Block erase error: " + err.Error() + "\r\n")
|
||||
}
|
||||
} else if argv[1] == "sector" {
|
||||
if err = dev.EraseSector(uint32(addr)); err != nil {
|
||||
println("Sector erase error: " + err.Error() + "\r\n")
|
||||
}
|
||||
} else if argv[1] == "chip" {
|
||||
if err = dev.EraseAll(); err != nil {
|
||||
println("Chip erase error: " + err.Error() + "\r\n")
|
||||
}
|
||||
} else {
|
||||
println("usage: erase <chip|block|sector> <bytes>")
|
||||
}
|
||||
}
|
||||
|
||||
func write(argv []string) {
|
||||
if len(argv) < 3 {
|
||||
println("usage: write <hex offset> <bytes>")
|
||||
}
|
||||
var err error
|
||||
var addr uint64 = 0x0
|
||||
if addr, err = strconv.ParseUint(argv[1], 16, 32); err != nil {
|
||||
println("Invalid address: " + err.Error() + "\r\n")
|
||||
return
|
||||
}
|
||||
buf := []byte(argv[2])
|
||||
if _, err = dev.WriteAt(buf, int64(addr)); err != nil {
|
||||
println("Write error: " + err.Error() + "\r\n")
|
||||
}
|
||||
}
|
||||
|
||||
func xxd(argv []string) {
|
||||
var err error
|
||||
var addr uint64 = 0x0
|
||||
var size int = 64
|
||||
switch len(argv) {
|
||||
case 3:
|
||||
if size, err = strconv.Atoi(argv[2]); err != nil {
|
||||
println("Invalid size argument: " + err.Error() + "\r\n")
|
||||
return
|
||||
}
|
||||
if size > storageBufLen || size < 1 {
|
||||
fmt.Printf("Size of hexdump must be greater than 0 and less than %d\r\n", storageBufLen)
|
||||
return
|
||||
}
|
||||
fallthrough
|
||||
case 2:
|
||||
if addr, err = strconv.ParseUint(argv[1], 16, 32); err != nil {
|
||||
println("Invalid address: " + err.Error() + "\r\n")
|
||||
return
|
||||
}
|
||||
fallthrough
|
||||
case 1:
|
||||
// no args supplied, so nothing to do here, just use the defaults
|
||||
default:
|
||||
println("usage: xxd <hex address, ex: 0xA0> <size of hexdump in bytes>\r\n")
|
||||
return
|
||||
}
|
||||
buf := store[0:size]
|
||||
dev.ReadAt(buf, int64(addr))
|
||||
xxdfprint(os.Stdout, uint32(addr), buf)
|
||||
}
|
||||
|
||||
func xxdfprint(w io.Writer, offset uint32, b []byte) {
|
||||
var l int
|
||||
var buf16 = make([]byte, 16)
|
||||
for i, c := 0, len(b); i < c; i += 16 {
|
||||
l = i + 16
|
||||
if l >= c {
|
||||
l = c
|
||||
}
|
||||
fmt.Fprintf(w, "%08x: % x ", offset+uint32(i), b[i:l])
|
||||
for j, n := 0, l-i; j < 16; j++ {
|
||||
if j >= n || !strconv.IsPrint(rune(b[i+j])) {
|
||||
buf16[j] = '.'
|
||||
} else {
|
||||
buf16[j] = b[i+j]
|
||||
}
|
||||
}
|
||||
console.Write(buf16)
|
||||
println()
|
||||
}
|
||||
}
|
||||
|
||||
func prompt() {
|
||||
print("==> ")
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
|
||||
"tinygo.org/x/drivers/examples/flash/console"
|
||||
"tinygo.org/x/drivers/flash"
|
||||
)
|
||||
|
||||
func main() {
|
||||
console_example.RunFor(
|
||||
flash.NewQSPI(
|
||||
machine.QSPI_CS,
|
||||
machine.QSPI_SCK,
|
||||
machine.QSPI_DATA0,
|
||||
machine.QSPI_DATA1,
|
||||
machine.QSPI_DATA2,
|
||||
machine.QSPI_DATA3,
|
||||
),
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
|
||||
"tinygo.org/x/drivers/examples/flash/console"
|
||||
"tinygo.org/x/drivers/flash"
|
||||
)
|
||||
|
||||
func main() {
|
||||
console_example.RunFor(
|
||||
flash.NewSPI(
|
||||
&machine.SPI1,
|
||||
machine.SPI1_MOSI_PIN,
|
||||
machine.SPI1_MISO_PIN,
|
||||
machine.SPI1_SCK_PIN,
|
||||
machine.SPI1_CS_PIN,
|
||||
),
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package hcsr04
|
||||
|
||||
import (
|
||||
"machine"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/hcsr04"
|
||||
)
|
||||
|
||||
func main() {
|
||||
sensor := hcsr04.New(machine.D10, machine.D9)
|
||||
sensor.Configure()
|
||||
|
||||
println("Ultrasonic starts")
|
||||
for {
|
||||
println("Distance:", sensor.ReadDistance(), "mm")
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
@@ -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,49 @@
|
||||
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,
|
||||
)
|
||||
|
||||
red = color.RGBA{255, 0, 0, 255}
|
||||
blue = color.RGBA{0, 0, 255, 255}
|
||||
green = color.RGBA{0, 255, 0, 255}
|
||||
black = color.RGBA{0, 0, 0, 255}
|
||||
white = color.RGBA{255, 255, 255, 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 scroll := int16(0); ; scroll = (scroll + 1) % 320 {
|
||||
time.Sleep(7500 * time.Microsecond)
|
||||
display.SetScroll(scroll)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
@@ -19,7 +19,7 @@ func main() {
|
||||
println("Magnetic readings:", x, y, z)
|
||||
|
||||
c, _ := mag.ReadTemperature()
|
||||
println("Temperature:", float32(c)/1000, "ºC")
|
||||
println("Temperature:", float32(c)/1000, "°C")
|
||||
|
||||
time.Sleep(time.Millisecond * 100)
|
||||
}
|
||||
|
||||
@@ -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,23 @@
|
||||
package main
|
||||
|
||||
// A small example that demonstrates how SemiHosting can be used.
|
||||
// You could use it with a board that supports GDB, such as the BBC micro:bit:
|
||||
// 1. Compile and debug it:
|
||||
// tinygo gdb -target=microbit -ocd-output tinygo.org/x/drivers/examples/semihosting
|
||||
// 2. Enable semihosting in the GDB shell:
|
||||
// monitor arm semihosting enable
|
||||
// 3. Start the program:
|
||||
// continue
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/semihosting"
|
||||
)
|
||||
|
||||
func main() {
|
||||
for {
|
||||
semihosting.Stdout.Write([]byte("hello world!\n"))
|
||||
time.Sleep(time.Second)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
// This example is designed to implement the button shifter for a PyBadge.
|
||||
package main
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/shifter"
|
||||
)
|
||||
|
||||
func main() {
|
||||
buttons := shifter.NewButtons()
|
||||
buttons.Configure()
|
||||
|
||||
for {
|
||||
// Update the pins state, to later be returned by .Get()
|
||||
buttons.ReadInput()
|
||||
|
||||
if buttons.Pins[shifter.BUTTON_LEFT].Get() {
|
||||
println("Button LEFT pressed")
|
||||
}
|
||||
if buttons.Pins[shifter.BUTTON_UP].Get() {
|
||||
println("Button UP pressed")
|
||||
}
|
||||
if buttons.Pins[shifter.BUTTON_DOWN].Get() {
|
||||
println("Button DOWN pressed")
|
||||
}
|
||||
if buttons.Pins[shifter.BUTTON_RIGHT].Get() {
|
||||
println("Button RIGHT pressed")
|
||||
}
|
||||
if buttons.Pins[shifter.BUTTON_SELECT].Get() {
|
||||
println("Button SELECT pressed")
|
||||
}
|
||||
if buttons.Pins[shifter.BUTTON_START].Get() {
|
||||
println("Button START pressed")
|
||||
}
|
||||
if buttons.Pins[shifter.BUTTON_A].Get() {
|
||||
println("Button A pressed")
|
||||
}
|
||||
if buttons.Pins[shifter.BUTTON_B].Get() {
|
||||
println("Button B pressed")
|
||||
}
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/shiftregister"
|
||||
)
|
||||
|
||||
func main() {
|
||||
d := shiftregister.New(
|
||||
shiftregister.EIGHT_BITS,
|
||||
machine.PA6, // D12 Pin latch connected to ST_CP of 74HC595 (12)
|
||||
machine.PA7, // D11 Pin clock connected to SH_CP of 74HC595 (11)
|
||||
machine.PB6, // D10 Pin data connected to DS of 74HC595 (14)
|
||||
)
|
||||
d.Configure()
|
||||
|
||||
for {
|
||||
|
||||
// Examples using masks. This method writes all pins state at once.
|
||||
|
||||
// All pins High
|
||||
d.WriteMask(0xFF)
|
||||
delay()
|
||||
|
||||
// All pins Low
|
||||
d.WriteMask(0x00)
|
||||
delay()
|
||||
|
||||
// Some fun with masks
|
||||
for _, pattern := range patterns {
|
||||
d.WriteMask(pattern)
|
||||
shortDelay()
|
||||
}
|
||||
delay()
|
||||
d.WriteMask(0x00)
|
||||
|
||||
// Examples using individually addressable pin API. This method is slower than using mask
|
||||
// because all register's pins state are send for is p.Set() call.
|
||||
|
||||
// Set register's pin #4
|
||||
d.GetShiftPin(4).High()
|
||||
delay()
|
||||
d.GetShiftPin(4).Low()
|
||||
delay()
|
||||
|
||||
// Get an individual pin and use it
|
||||
pin := d.GetShiftPin(7)
|
||||
pin.High()
|
||||
delay()
|
||||
pin.Low()
|
||||
delay()
|
||||
|
||||
// Prepare an array of pin attached to the register
|
||||
pins := [8]*shiftregister.ShiftPin{}
|
||||
for p := 0; p < 8; p++ {
|
||||
pins[p] = d.GetShiftPin(p)
|
||||
}
|
||||
|
||||
for p := 7; p >= 0; p-- {
|
||||
pins[p].Low()
|
||||
shortDelay()
|
||||
pins[p].High()
|
||||
}
|
||||
|
||||
for p := 7; p >= 0; p-- {
|
||||
pins[p].High()
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
pins[p].Low()
|
||||
}
|
||||
delay()
|
||||
}
|
||||
}
|
||||
|
||||
func delay() {
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
}
|
||||
func shortDelay() {
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
}
|
||||
|
||||
var patterns = []uint32{
|
||||
0b00000001,
|
||||
0b00000010,
|
||||
0b00000100,
|
||||
0b00001000,
|
||||
0b00010000,
|
||||
0b00100000,
|
||||
0b01000000,
|
||||
0b10000000,
|
||||
0b10000001,
|
||||
0b10000010,
|
||||
0b10000100,
|
||||
0b10001000,
|
||||
0b10010000,
|
||||
0b10100000,
|
||||
0b11000000,
|
||||
0b11000001,
|
||||
0b11000010,
|
||||
0b11000100,
|
||||
0b11001000,
|
||||
0b11010000,
|
||||
0b11100000,
|
||||
0b11100001,
|
||||
0b11100010,
|
||||
0b11100100,
|
||||
0b11101000,
|
||||
0b11110000,
|
||||
0b11110001,
|
||||
0b11110010,
|
||||
0b11110100,
|
||||
0b11111000,
|
||||
0b11111001,
|
||||
0b11111010,
|
||||
0b11111100,
|
||||
0b11111101,
|
||||
0b11111110,
|
||||
0b11111111,
|
||||
0b00000000,
|
||||
0b11111111,
|
||||
0b00000000,
|
||||
0b11111111,
|
||||
}
|
||||
@@ -16,7 +16,7 @@ func main() {
|
||||
temp, humidity, _ := sensor.ReadTemperatureHumidity()
|
||||
t := fmt.Sprintf("%.2f", float32(temp)/1000)
|
||||
h := fmt.Sprintf("%.2f", float32(humidity)/100)
|
||||
println("Temperature:", t, "ºC")
|
||||
println("Temperature:", t, "°C")
|
||||
println("Humidity", h, "%")
|
||||
time.Sleep(2 * time.Second)
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ func main() {
|
||||
|
||||
for {
|
||||
temp, _ := sensor.ReadTemperature()
|
||||
println("Temperature:", temp/1000, "ºC")
|
||||
println("Temperature:", temp/1000, "°C")
|
||||
|
||||
time.Sleep(2 * time.Second)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"machine"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/tmp102"
|
||||
)
|
||||
|
||||
func main() {
|
||||
machine.I2C0.Configure(machine.I2CConfig{
|
||||
Frequency: machine.TWI_FREQ_400KHZ,
|
||||
})
|
||||
|
||||
thermo := tmp102.New(machine.I2C0)
|
||||
thermo.Configure(tmp102.Config{})
|
||||
|
||||
for {
|
||||
|
||||
temp, _ := thermo.ReadTemperature()
|
||||
|
||||
print(fmt.Sprintf("%.2f°C\r\n", float32(temp)/1000.0))
|
||||
|
||||
time.Sleep(time.Millisecond * 1000)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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,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,448 @@
|
||||
package flash
|
||||
|
||||
import "time"
|
||||
|
||||
// A DeviceIdentifier can be passed to the Configure() method of a flash Device
|
||||
// in order provide a means of discovery of device-specific attributes based on
|
||||
// the JEDEC ID read from the device.
|
||||
type DeviceIdentifier interface {
|
||||
// Identify returns an Attrs struct based on the provided JEDEC ID
|
||||
Identify(id JedecID) Attrs
|
||||
}
|
||||
|
||||
// DeviceIdentifierFunc is a functional Identifier implementation
|
||||
type DeviceIdentifierFunc func(id JedecID) Attrs
|
||||
|
||||
// Identify implements the Identifier interface
|
||||
func (fn DeviceIdentifierFunc) Identify(id JedecID) Attrs {
|
||||
return fn(id)
|
||||
}
|
||||
|
||||
// DefaultDeviceIndentifier is a DeviceIdentifier that is capable of recognizing
|
||||
// JEDEC IDs for all of the known memory devices in this package. If you are
|
||||
// have no way to be sure about the type of memory device that might be on a
|
||||
// board you are targeting, this can be a good starting point to use. The
|
||||
// downside of using this function is that it will prevent the compiler from
|
||||
// being able to mark any of the functions for the various devices as unused,
|
||||
// resulting in larger code size. If code size is a concern, and if you know
|
||||
// ahead of time you are only dealing with a limited set of memory devices, it
|
||||
// might be worthwhile to use your own implementation of a DeviceIdentifier
|
||||
// that only references those devices, so that more methods are marked unused.
|
||||
var DefaultDeviceIdentifier = DeviceIdentifierFunc(func(id JedecID) Attrs {
|
||||
switch id.Uint32() {
|
||||
case 0x010617:
|
||||
return S25FL064L()
|
||||
case 0x014015:
|
||||
return S25FL216K()
|
||||
case 0x1F4501:
|
||||
return AT25DF081A()
|
||||
case 0xC22015:
|
||||
return MX25L1606()
|
||||
case 0xC22016:
|
||||
return MX25L3233F()
|
||||
case 0xC22817:
|
||||
return MX25R6435F()
|
||||
case 0xC84015:
|
||||
return GD25Q16C()
|
||||
case 0xC84017:
|
||||
return GD25Q64C()
|
||||
case 0xEF4015:
|
||||
return W25Q16JVIQ()
|
||||
case 0xEF4016:
|
||||
return W25Q32FV()
|
||||
case 0xEF4017:
|
||||
return W25Q64JVIQ()
|
||||
case 0xEF4018:
|
||||
return W25Q128JVSQ()
|
||||
case 0xEF6014:
|
||||
return W25Q80DL()
|
||||
case 0xEF6015:
|
||||
return W25Q16FW()
|
||||
case 0xEF6016:
|
||||
return W25Q32BV()
|
||||
case 0xEF7015:
|
||||
return W25Q16JVIM()
|
||||
case 0xEF7016:
|
||||
return W25Q32JVIM()
|
||||
case 0xEF7017:
|
||||
return W25Q64JVIM()
|
||||
case 0xEF7018:
|
||||
return W25Q128JVPM()
|
||||
default:
|
||||
return Attrs{JedecID: id}
|
||||
}
|
||||
})
|
||||
|
||||
// Settings for the Cypress (was Spansion) S25FL064L 8MiB SPI flash.
|
||||
// Datasheet: http://www.cypress.com/file/316661/download
|
||||
func S25FL064L() Attrs {
|
||||
return Attrs{
|
||||
TotalSize: 1 << 23, // 8 MiB
|
||||
StartUp: 300 * time.Microsecond,
|
||||
JedecID: JedecID{0x01, 0x60, 0x17},
|
||||
MaxClockSpeedMHz: 108,
|
||||
QuadEnableBitMask: 0x02,
|
||||
HasSectorProtection: false,
|
||||
SupportsFastRead: true,
|
||||
SupportsQSPI: true,
|
||||
SupportsQSPIWrites: true,
|
||||
WriteStatusSplit: false,
|
||||
SingleStatusByte: false,
|
||||
}
|
||||
}
|
||||
|
||||
// Settings for the Cypress (was Spansion) S25FL116K 2MiB SPI flash.
|
||||
// Datasheet: http://www.cypress.com/file/196886/download
|
||||
func S25FL116K() Attrs {
|
||||
return Attrs{
|
||||
TotalSize: 1 << 21, // 2 MiB
|
||||
StartUp: 10000 * time.Microsecond,
|
||||
JedecID: JedecID{0x01, 0x40, 0x15},
|
||||
MaxClockSpeedMHz: 108,
|
||||
QuadEnableBitMask: 0x02,
|
||||
HasSectorProtection: false,
|
||||
SupportsFastRead: true,
|
||||
SupportsQSPI: true,
|
||||
SupportsQSPIWrites: false,
|
||||
WriteStatusSplit: false,
|
||||
SingleStatusByte: false,
|
||||
}
|
||||
}
|
||||
|
||||
// Settings for the Cypress (was Spansion) S25FL216K 2MiB SPI flash.
|
||||
// Datasheet: http://www.cypress.com/file/197346/download
|
||||
func S25FL216K() Attrs {
|
||||
return Attrs{
|
||||
TotalSize: 1 << 21, // 2 MiB
|
||||
StartUp: 10000 * time.Microsecond,
|
||||
JedecID: JedecID{0x01, 0x40, 0x15},
|
||||
MaxClockSpeedMHz: 65,
|
||||
QuadEnableBitMask: 0x02,
|
||||
HasSectorProtection: false,
|
||||
SupportsFastRead: true,
|
||||
SupportsQSPI: true,
|
||||
SupportsQSPIWrites: false,
|
||||
WriteStatusSplit: false,
|
||||
SingleStatusByte: false,
|
||||
}
|
||||
}
|
||||
|
||||
// Settings for the Adesto Tech AT25DF081A 1MiB SPI flash. Its on the SAMD21
|
||||
// Xplained board.
|
||||
// Datasheet: https://www.adestotech.com/wp-content/uploads/doc8715.pdf
|
||||
func AT25DF081A() Attrs {
|
||||
return Attrs{
|
||||
TotalSize: 1 << 20, // 1 MiB
|
||||
StartUp: 10000 * time.Microsecond,
|
||||
JedecID: JedecID{0x1F, 0x45, 0x01},
|
||||
MaxClockSpeedMHz: 85,
|
||||
QuadEnableBitMask: 0x00,
|
||||
HasSectorProtection: true,
|
||||
SupportsFastRead: true,
|
||||
SupportsQSPI: false,
|
||||
SupportsQSPIWrites: false,
|
||||
WriteStatusSplit: false,
|
||||
SingleStatusByte: false,
|
||||
}
|
||||
}
|
||||
|
||||
// Settings for the Macronix MX25L1606 2MiB SPI flash.
|
||||
// Datasheet:
|
||||
func MX25L1606() Attrs {
|
||||
return Attrs{
|
||||
TotalSize: 1 << 21, // 2 MiB,
|
||||
StartUp: 5000 * time.Microsecond,
|
||||
JedecID: JedecID{0xC2, 0x20, 0x15},
|
||||
MaxClockSpeedMHz: 8,
|
||||
QuadEnableBitMask: 0x40,
|
||||
HasSectorProtection: false,
|
||||
SupportsFastRead: true,
|
||||
SupportsQSPI: true,
|
||||
SupportsQSPIWrites: true,
|
||||
WriteStatusSplit: false,
|
||||
SingleStatusByte: true,
|
||||
}
|
||||
}
|
||||
|
||||
// Settings for the Macronix MX25L3233F 4MiB SPI flash.
|
||||
// Datasheet:
|
||||
// http://www.macronix.com/Lists/Datasheet/Attachments/7426/MX25L3233F,%203V,%2032Mb,%20v1.6.pdf
|
||||
func MX25L3233F() Attrs {
|
||||
return Attrs{
|
||||
TotalSize: 1 << 22, // 4 MiB
|
||||
StartUp: 5000 * time.Microsecond,
|
||||
JedecID: JedecID{0xC2, 0x20, 0x16},
|
||||
MaxClockSpeedMHz: 133,
|
||||
QuadEnableBitMask: 0x40,
|
||||
HasSectorProtection: false,
|
||||
SupportsFastRead: true,
|
||||
SupportsQSPI: true,
|
||||
SupportsQSPIWrites: true,
|
||||
WriteStatusSplit: false,
|
||||
SingleStatusByte: false,
|
||||
}
|
||||
}
|
||||
|
||||
// Settings for the Macronix MX25R6435F 8MiB SPI flash.
|
||||
// Datasheet:
|
||||
// http://www.macronix.com/Lists/Datasheet/Attachments/7428/MX25R6435F,%20Wide%20Range,%2064Mb,%20v1.4.pdf
|
||||
// By default its in lower power mode which can only do 8mhz. In high power mode
|
||||
// it can do 80mhz.
|
||||
func MX25R6435F() Attrs {
|
||||
return Attrs{
|
||||
TotalSize: 1 << 23, // 8 MiB
|
||||
StartUp: 5000 * time.Microsecond,
|
||||
JedecID: JedecID{0xC2, 0x28, 0x17},
|
||||
MaxClockSpeedMHz: 8,
|
||||
QuadEnableBitMask: 0x40,
|
||||
HasSectorProtection: false,
|
||||
SupportsFastRead: true,
|
||||
SupportsQSPI: true,
|
||||
SupportsQSPIWrites: true,
|
||||
WriteStatusSplit: false,
|
||||
SingleStatusByte: true,
|
||||
}
|
||||
}
|
||||
|
||||
// Settings for the Gigadevice GD25Q16C 2MiB SPI flash.
|
||||
// Datasheet: http://www.gigadevice.com/datasheet/gd25q16c/
|
||||
func GD25Q16C() Attrs {
|
||||
return Attrs{
|
||||
TotalSize: 1 << 21, // 2 MiB
|
||||
StartUp: 5000 * time.Microsecond,
|
||||
JedecID: JedecID{0xC8, 0x40, 0x15},
|
||||
MaxClockSpeedMHz: 104,
|
||||
QuadEnableBitMask: 0x02,
|
||||
SupportsFastRead: true,
|
||||
SupportsQSPI: true,
|
||||
SupportsQSPIWrites: true,
|
||||
WriteStatusSplit: false,
|
||||
SingleStatusByte: false,
|
||||
}
|
||||
}
|
||||
|
||||
// Settings for the Gigadevice GD25Q64C 8MiB SPI flash.
|
||||
// Datasheet: http://www.elm-tech.com/en/products/spi-flash-memory/gd25q64/gd25q64.pdf
|
||||
func GD25Q64C() Attrs {
|
||||
return Attrs{
|
||||
TotalSize: 1 << 23, // 8 MiB
|
||||
StartUp: 5000 * time.Microsecond,
|
||||
JedecID: JedecID{0xC8, 0x40, 0x17},
|
||||
MaxClockSpeedMHz: 104,
|
||||
QuadEnableBitMask: 0x02,
|
||||
HasSectorProtection: false,
|
||||
SupportsFastRead: true,
|
||||
SupportsQSPI: true,
|
||||
SupportsQSPIWrites: true,
|
||||
WriteStatusSplit: true,
|
||||
SingleStatusByte: false,
|
||||
}
|
||||
}
|
||||
|
||||
// Settings for the Winbond W25Q16JV-IQ 2MiB SPI flash. Note that JV-IM has a
|
||||
// different .memory_type (0x70) Datasheet:
|
||||
// https://www.winbond.com/resource-files/w25q16jv%20spi%20revf%2005092017.pdf
|
||||
func W25Q16JVIQ() Attrs {
|
||||
return Attrs{
|
||||
TotalSize: 1 << 21, // 2 MiB
|
||||
StartUp: 5000 * time.Microsecond,
|
||||
JedecID: JedecID{0xEF, 0x40, 0x15},
|
||||
MaxClockSpeedMHz: 133,
|
||||
QuadEnableBitMask: 0x02,
|
||||
HasSectorProtection: false,
|
||||
SupportsFastRead: true,
|
||||
SupportsQSPI: true,
|
||||
SupportsQSPIWrites: true,
|
||||
WriteStatusSplit: false,
|
||||
SingleStatusByte: false,
|
||||
}
|
||||
}
|
||||
|
||||
// Settings for the Winbond W25Q16FW 2MiB SPI flash.
|
||||
// Datasheet:
|
||||
// https://www.winbond.com/resource-files/w25q16fw%20revj%2005182017%20sfdp.pdf
|
||||
func W25Q16FW() Attrs {
|
||||
return Attrs{
|
||||
TotalSize: 1 << 21, // 2 MiB
|
||||
StartUp: 5000 * time.Microsecond,
|
||||
JedecID: JedecID{0xEF, 0x60, 0x15},
|
||||
MaxClockSpeedMHz: 133,
|
||||
QuadEnableBitMask: 0x02,
|
||||
HasSectorProtection: false,
|
||||
SupportsFastRead: true,
|
||||
SupportsQSPI: true,
|
||||
SupportsQSPIWrites: true,
|
||||
WriteStatusSplit: false,
|
||||
SingleStatusByte: false,
|
||||
}
|
||||
}
|
||||
|
||||
// Settings for the Winbond W25Q16JV-IM 2MiB SPI flash. Note that JV-IQ has a
|
||||
// different .memory_type (0x40) Datasheet:
|
||||
// https://www.winbond.com/resource-files/w25q16jv%20spi%20revf%2005092017.pdf
|
||||
func W25Q16JVIM() Attrs {
|
||||
return Attrs{
|
||||
TotalSize: 1 << 21, // 2 MiB
|
||||
StartUp: 5000 * time.Microsecond,
|
||||
JedecID: JedecID{0xEF, 0x70, 0x15},
|
||||
MaxClockSpeedMHz: 133,
|
||||
QuadEnableBitMask: 0x02,
|
||||
HasSectorProtection: false,
|
||||
SupportsFastRead: true,
|
||||
SupportsQSPI: true,
|
||||
SupportsQSPIWrites: true,
|
||||
WriteStatusSplit: false,
|
||||
SingleStatusByte: false,
|
||||
}
|
||||
}
|
||||
|
||||
// Settings for the Winbond W25Q32BV 4MiB SPI flash.
|
||||
// Datasheet:
|
||||
// https://www.winbond.com/resource-files/w25q32bv_revi_100413_wo_automotive.pdf
|
||||
func W25Q32BV() Attrs {
|
||||
return Attrs{
|
||||
TotalSize: 1 << 22, // 4 MiB
|
||||
StartUp: 10000 * time.Microsecond,
|
||||
JedecID: JedecID{0xEF, 0x60, 0x16},
|
||||
MaxClockSpeedMHz: 104,
|
||||
QuadEnableBitMask: 0x02,
|
||||
HasSectorProtection: false,
|
||||
SupportsFastRead: true,
|
||||
SupportsQSPI: true,
|
||||
SupportsQSPIWrites: false,
|
||||
WriteStatusSplit: false,
|
||||
SingleStatusByte: false,
|
||||
}
|
||||
}
|
||||
|
||||
// Settings for the Winbond W25Q32JV-IM 4MiB SPI flash.
|
||||
// Datasheet:
|
||||
// https://www.winbond.com/resource-files/w25q32jv%20revg%2003272018%20plus.pdf
|
||||
func W25Q32JVIM() Attrs {
|
||||
return Attrs{
|
||||
TotalSize: 1 << 22, // 4 MiB
|
||||
StartUp: 5000 * time.Microsecond,
|
||||
JedecID: JedecID{0xEF, 0x70, 0x16},
|
||||
MaxClockSpeedMHz: 133,
|
||||
QuadEnableBitMask: 0x02,
|
||||
HasSectorProtection: false,
|
||||
SupportsFastRead: true,
|
||||
SupportsQSPI: true,
|
||||
SupportsQSPIWrites: true,
|
||||
WriteStatusSplit: false,
|
||||
SingleStatusByte: false,
|
||||
}
|
||||
}
|
||||
|
||||
// Settings for the Winbond W25Q64JV-IM 8MiB SPI flash. Note that JV-IQ has a
|
||||
// different .memory_type (0x40) Datasheet:
|
||||
// http://www.winbond.com/resource-files/w25q64jv%20revj%2003272018%20plus.pdf
|
||||
func W25Q64JVIM() Attrs {
|
||||
return Attrs{
|
||||
TotalSize: 1 << 23, // 8 MiB
|
||||
StartUp: 5000 * time.Microsecond,
|
||||
JedecID: JedecID{0xEF, 0x70, 0x17},
|
||||
MaxClockSpeedMHz: 133,
|
||||
QuadEnableBitMask: 0x02,
|
||||
HasSectorProtection: false,
|
||||
SupportsFastRead: true,
|
||||
SupportsQSPI: true,
|
||||
SupportsQSPIWrites: true,
|
||||
WriteStatusSplit: false,
|
||||
SingleStatusByte: false,
|
||||
}
|
||||
}
|
||||
|
||||
// Settings for the Winbond W25Q64JV-IQ 8MiB SPI flash. Note that JV-IM has a
|
||||
// different .memory_type (0x70) Datasheet:
|
||||
// http://www.winbond.com/resource-files/w25q64jv%20revj%2003272018%20plus.pdf
|
||||
func W25Q64JVIQ() Attrs {
|
||||
return Attrs{
|
||||
TotalSize: 1 << 23, // 8 MiB
|
||||
StartUp: 5000 * time.Microsecond,
|
||||
JedecID: JedecID{0xEF, 0x40, 0x17},
|
||||
MaxClockSpeedMHz: 133,
|
||||
QuadEnableBitMask: 0x02,
|
||||
HasSectorProtection: false,
|
||||
SupportsFastRead: true,
|
||||
SupportsQSPI: true,
|
||||
SupportsQSPIWrites: true,
|
||||
WriteStatusSplit: false,
|
||||
SingleStatusByte: false,
|
||||
}
|
||||
}
|
||||
|
||||
// Settings for the Winbond W25Q80DL 1MiB SPI flash.
|
||||
// Datasheet:
|
||||
// https://www.winbond.com/resource-files/w25q80dv%20dl_revh_10022015.pdf
|
||||
func W25Q80DL() Attrs {
|
||||
return Attrs{
|
||||
TotalSize: 1 << 20, // 1 MiB
|
||||
StartUp: 5000 * time.Microsecond,
|
||||
JedecID: JedecID{0xEF, 0x60, 0x14},
|
||||
MaxClockSpeedMHz: 104,
|
||||
QuadEnableBitMask: 0x02,
|
||||
HasSectorProtection: false,
|
||||
SupportsFastRead: true,
|
||||
SupportsQSPI: true,
|
||||
SupportsQSPIWrites: false,
|
||||
WriteStatusSplit: false,
|
||||
SingleStatusByte: false,
|
||||
}
|
||||
}
|
||||
|
||||
// Settings for the Winbond W25Q128JV-SQ 16MiB SPI flash. Note that JV-IM has a
|
||||
// different .memory_type (0x70) Datasheet:
|
||||
// https://www.winbond.com/resource-files/w25q128jv%20revf%2003272018%20plus.pdf
|
||||
func W25Q128JVSQ() Attrs {
|
||||
return Attrs{
|
||||
TotalSize: 1 << 24, // 16 MiB
|
||||
StartUp: 5000 * time.Microsecond,
|
||||
JedecID: JedecID{0xEF, 0x40, 0x18},
|
||||
MaxClockSpeedMHz: 133,
|
||||
QuadEnableBitMask: 0x02,
|
||||
HasSectorProtection: false,
|
||||
SupportsFastRead: true,
|
||||
SupportsQSPI: true,
|
||||
SupportsQSPIWrites: true,
|
||||
WriteStatusSplit: false,
|
||||
SingleStatusByte: false,
|
||||
}
|
||||
}
|
||||
|
||||
// Settings for the Winbond W25Q128JV-PM 16MiB SPI flash. Note that JV-IM has a
|
||||
// different .memory_type (0x70) Datasheet:
|
||||
// https://www.winbond.com/resource-files/w25q128jv%20revf%2003272018%20plus.pdf
|
||||
func W25Q128JVPM() Attrs {
|
||||
return Attrs{
|
||||
TotalSize: 1 << 24, // 16 MiB
|
||||
StartUp: 5000 * time.Microsecond,
|
||||
JedecID: JedecID{0xEF, 0x70, 0x18},
|
||||
MaxClockSpeedMHz: 133,
|
||||
QuadEnableBitMask: 0x02,
|
||||
HasSectorProtection: false,
|
||||
SupportsFastRead: true,
|
||||
SupportsQSPI: true,
|
||||
SupportsQSPIWrites: true,
|
||||
WriteStatusSplit: false,
|
||||
SingleStatusByte: false,
|
||||
}
|
||||
}
|
||||
|
||||
// Settings for the Winbond W25Q32FV 4MiB SPI flash.
|
||||
// Datasheet:http://www.winbond.com/resource-files/w25q32fv%20revj%2006032016.pdf?__locale=en
|
||||
func W25Q32FV() Attrs {
|
||||
return Attrs{
|
||||
TotalSize: 1 << 22, // 4 MiB
|
||||
StartUp: 5000 * time.Microsecond,
|
||||
JedecID: JedecID{0xEF, 0x40, 0x16},
|
||||
MaxClockSpeedMHz: 104,
|
||||
QuadEnableBitMask: 0x00,
|
||||
HasSectorProtection: false,
|
||||
SupportsFastRead: true,
|
||||
SupportsQSPI: false,
|
||||
SupportsQSPIWrites: false,
|
||||
WriteStatusSplit: false,
|
||||
SingleStatusByte: false,
|
||||
}
|
||||
}
|
||||
+405
@@ -0,0 +1,405 @@
|
||||
package flash
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
// BlockSize is the number of bytes in a block for most/all NOR flash memory
|
||||
BlockSize = 64 * 1024
|
||||
|
||||
// SectorSize is the number of bytes in a sector for most/all NOR flash memory
|
||||
SectorSize = 4 * 1024
|
||||
|
||||
// PageSize is the number of bytes in a page for most/all NOR flash memory
|
||||
PageSize = 256
|
||||
)
|
||||
|
||||
// Device represents a NOR flash memory device accessible using SPI
|
||||
type Device struct {
|
||||
trans transport
|
||||
attrs Attrs
|
||||
}
|
||||
|
||||
// DeviceConfig contains the parameters that can be set when configuring a
|
||||
// flash memory device.
|
||||
type DeviceConfig struct {
|
||||
Identifier DeviceIdentifier
|
||||
}
|
||||
|
||||
// JedecID encapsules the ID values that unique identify a flash memory device.
|
||||
type JedecID struct {
|
||||
ManufID uint8
|
||||
MemType uint8
|
||||
Capacity uint8
|
||||
}
|
||||
|
||||
// Uint32 returns the JEDEC ID packed into a uint32
|
||||
func (id JedecID) Uint32() uint32 {
|
||||
return uint32(id.ManufID)<<16 | uint32(id.MemType)<<8 | uint32(id.Capacity)
|
||||
}
|
||||
|
||||
// SerialNumber represents a serial number read from a flash memory device
|
||||
type SerialNumber uint64
|
||||
|
||||
// Attrs represent the differences in hardware characteristics and capabilities
|
||||
// of various SPI flash memory devices.
|
||||
type Attrs struct {
|
||||
|
||||
// TotalSize is the number of bytes that the flash device can store
|
||||
TotalSize uint32
|
||||
|
||||
// StartUp is the duration of time between when the device is reset and when
|
||||
// it is ready to operation
|
||||
StartUp time.Duration
|
||||
|
||||
// Three response bytes to 0x9f JEDEC ID command.
|
||||
JedecID
|
||||
|
||||
// Max clock speed for all operations and the fastest read mode.
|
||||
MaxClockSpeedMHz uint8
|
||||
|
||||
// Bitmask for Quad Enable bit if present. 0x00 otherwise. This is for the
|
||||
// highest byte in the status register.
|
||||
QuadEnableBitMask uint8
|
||||
|
||||
HasSectorProtection bool
|
||||
|
||||
// Supports the 0x0b fast read command with 8 dummy cycles.
|
||||
SupportsFastRead bool
|
||||
|
||||
// Supports the fast read, quad output command 0x6b with 8 dummy cycles.
|
||||
SupportsQSPI bool
|
||||
|
||||
// Supports the quad input page program command 0x32. This is known as 1-1-4
|
||||
// because it only uses all four lines for data.
|
||||
SupportsQSPIWrites bool
|
||||
|
||||
// Requires a separate command 0x31 to write to the second byte of the status
|
||||
// register. Otherwise two byte are written via 0x01.
|
||||
WriteStatusSplit bool
|
||||
|
||||
// True when the status register is a single byte. This implies the Quad
|
||||
// Enable bit is in the first byte and the Read Status Register 2 command
|
||||
// (0x35) is unsupported.
|
||||
SingleStatusByte bool
|
||||
}
|
||||
|
||||
// Configure sets up the device and the underlying transport mechanism. The
|
||||
// DeviceConfig argument allows the caller to specify an instance of the
|
||||
// DeviceIdentifier interface that, if provided, will be used to retrieve the
|
||||
// attributes of the device based on the JEDEC ID.
|
||||
func (dev *Device) Configure(config *DeviceConfig) (err error) {
|
||||
|
||||
dev.trans.configure(config)
|
||||
|
||||
var id JedecID
|
||||
if id, err = dev.ReadJEDEC(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// try to ascertain the vendor-specific attributes of the chip using the
|
||||
// provided Identifier
|
||||
if config.Identifier != nil {
|
||||
dev.attrs = config.Identifier.Identify(id)
|
||||
} else {
|
||||
dev.attrs = Attrs{JedecID: id}
|
||||
}
|
||||
|
||||
// We don't know what state the flash is in so wait for any remaining
|
||||
// writes and then reset.
|
||||
|
||||
// The write in progress bit should be low.
|
||||
for s, err := dev.ReadStatus(); (s & 0x01) > 0; s, err = dev.ReadStatus() {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// The suspended write/erase bit should be low.
|
||||
for s, err := dev.ReadStatus2(); (s & 0x80) > 0; s, err = dev.ReadStatus2() {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// perform device reset
|
||||
if err := dev.trans.runCommand(cmdEnableReset); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := dev.trans.runCommand(cmdReset); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Wait for the reset - 30us by default
|
||||
time.Sleep(30 * time.Microsecond)
|
||||
|
||||
// Speed up to max device frequency
|
||||
if dev.attrs.MaxClockSpeedMHz > 0 {
|
||||
err := dev.trans.setClockSpeed(uint32(dev.attrs.MaxClockSpeedMHz) * 1e6)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Enable Quad Mode if available
|
||||
if dev.trans.supportQuadMode() && dev.attrs.QuadEnableBitMask > 0 {
|
||||
// Verify that QSPI mode is enabled.
|
||||
var status byte
|
||||
if dev.attrs.SingleStatusByte {
|
||||
status, err = dev.ReadStatus()
|
||||
} else {
|
||||
status, err = dev.ReadStatus2()
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Check and set the quad enable bit.
|
||||
if status&dev.attrs.QuadEnableBitMask == 0 {
|
||||
if err := dev.WriteEnable(); err != nil {
|
||||
return err
|
||||
}
|
||||
fullStatus := []byte{0x00, dev.attrs.QuadEnableBitMask}
|
||||
if dev.attrs.WriteStatusSplit {
|
||||
err = dev.trans.writeCommand(cmdWriteStatus2, fullStatus[1:])
|
||||
} else if dev.attrs.SingleStatusByte {
|
||||
err = dev.trans.writeCommand(cmdWriteStatus, fullStatus[1:])
|
||||
} else {
|
||||
err = dev.trans.writeCommand(cmdWriteStatus, fullStatus)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// disable sector protection if the chip has it
|
||||
if dev.attrs.HasSectorProtection {
|
||||
if err := dev.WriteEnable(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := dev.trans.writeCommand(cmdWriteStatus, []byte{0x00}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// write disable
|
||||
if err := dev.trans.runCommand(cmdWriteDisable); err != nil {
|
||||
return err
|
||||
}
|
||||
return dev.WaitUntilReady()
|
||||
}
|
||||
|
||||
// Attrs returns the attributes of the device determined from the most recent
|
||||
// call to Configure(). If no call to Configure() has been made, this will be
|
||||
// the zero value of the Attrs struct.
|
||||
func (dev *Device) Attrs() Attrs {
|
||||
return dev.attrs
|
||||
}
|
||||
|
||||
// ReadJEDEC reads the JEDEC ID from the device; this ID can then be used to
|
||||
// ascertain the attributes of the chip from a list of known devices.
|
||||
func (dev *Device) ReadJEDEC() (JedecID, error) {
|
||||
jedecID := make([]byte, 3)
|
||||
if err := dev.trans.readCommand(cmdReadJedecID, jedecID); err != nil {
|
||||
return JedecID{}, err
|
||||
}
|
||||
return JedecID{jedecID[0], jedecID[1], jedecID[2]}, nil
|
||||
}
|
||||
|
||||
// ReadSerialNumber reads the serial numbers from the connected device.
|
||||
// TODO: maybe check if byte order / endianess is correct, probably is not
|
||||
func (dev *Device) ReadSerialNumber() (SerialNumber, error) {
|
||||
sn := make([]byte, 12)
|
||||
if err := dev.trans.readCommand(0x4B, sn); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return SerialNumber(uint64(sn[11]) | uint64(sn[10])<<0x8 |
|
||||
uint64(sn[9])<<0x10 | uint64(sn[8])<<0x18 | uint64(sn[7])<<0x20 |
|
||||
uint64(sn[6])<<0x28 | uint64(sn[5])<<0x30 | uint64(sn[4])<<0x38), nil
|
||||
}
|
||||
|
||||
// Size returns the size of this memory, in bytes.
|
||||
func (dev *Device) Size() int64 {
|
||||
if dev.attrs.TotalSize < 1 {
|
||||
// in case a DeviceIdentifier function wasn't used, use the capacity
|
||||
// specified in the JEDEC ID instead
|
||||
return int64(dev.attrs.Capacity)
|
||||
}
|
||||
return int64(dev.attrs.TotalSize)
|
||||
}
|
||||
|
||||
// ReadAt satisfies the io.ReaderAt interface, and fills the provided buffer
|
||||
// with memory read from the device starting at the provided address.
|
||||
func (dev *Device) ReadAt(buf []byte, addr int64) (int, error) {
|
||||
if err := dev.WaitUntilReady(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if err := dev.trans.readMemory(uint32(addr), buf); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return len(buf), nil
|
||||
}
|
||||
|
||||
// WriteAt satisfies the io.WriterAt interface and writes data to the device,
|
||||
// one page at a time, starting at the provided address. This method assumes
|
||||
// that the destination is already erased.
|
||||
func (dev *Device) WriteAt(buf []byte, addr int64) (n int, err error) {
|
||||
remain := uint32(len(buf))
|
||||
idx := uint32(0)
|
||||
loc := uint32(addr)
|
||||
for remain > 0 {
|
||||
if err = dev.WaitUntilReady(); err != nil {
|
||||
return
|
||||
}
|
||||
if err = dev.WriteEnable(); err != nil {
|
||||
return
|
||||
}
|
||||
leftOnPage := PageSize - (loc & (PageSize - 1))
|
||||
toWrite := remain
|
||||
if leftOnPage < remain {
|
||||
toWrite = leftOnPage
|
||||
}
|
||||
if err = dev.trans.writeMemory(loc, buf[idx:idx+toWrite]); err != nil {
|
||||
return
|
||||
}
|
||||
idx += toWrite
|
||||
loc += toWrite
|
||||
remain -= toWrite
|
||||
}
|
||||
return len(buf) - int(remain), nil
|
||||
}
|
||||
|
||||
// WriteBlockSize returns the block size in which data can be written to
|
||||
// memory. It can be used by a client to optimize writes, non-aligned writes
|
||||
// should always work correctly.
|
||||
// For SPI NOR flash this is the page size, usually/always 256.
|
||||
func (dev *Device) WriteBlockSize() int64 {
|
||||
return PageSize
|
||||
}
|
||||
|
||||
// EraseBlockSize returns the smallest erasable area on this particular chip
|
||||
// in bytes. This is used for the block size in EraseBlocks.
|
||||
// For SPI NOR flash this is the sector size, usually/always 4096.
|
||||
func (dev *Device) EraseBlockSize() int64 {
|
||||
return SectorSize
|
||||
}
|
||||
|
||||
// EraseBlocks erases the given number of blocks. An implementation may
|
||||
// transparently coalesce ranges of blocks into larger bundles if the chip
|
||||
// supports this. The start and len parameters are in block numbers, use
|
||||
// EraseBlockSize to map addresses to blocks.
|
||||
func (dev *Device) EraseBlocks(start, len int64) error {
|
||||
// TODO: maybe combine sector erase operations into block erase operations
|
||||
for i := start; i < start+len; i++ {
|
||||
if err := dev.EraseSector(uint32(i)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dev *Device) WriteEnable() error {
|
||||
return dev.trans.runCommand(cmdWriteEnable)
|
||||
}
|
||||
|
||||
// EraseBlock erases a block of memory at the specified index
|
||||
func (dev *Device) EraseBlock(blockNumber uint32) error {
|
||||
if err := dev.WaitUntilReady(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := dev.WriteEnable(); err != nil {
|
||||
return err
|
||||
}
|
||||
return dev.trans.eraseCommand(cmdEraseBlock, blockNumber*BlockSize)
|
||||
}
|
||||
|
||||
// EraseSector erases a sector of memory at the given index
|
||||
func (dev *Device) EraseSector(sectorNumber uint32) error {
|
||||
if err := dev.WaitUntilReady(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := dev.WriteEnable(); err != nil {
|
||||
return err
|
||||
}
|
||||
return dev.trans.eraseCommand(cmdEraseSector, sectorNumber*SectorSize)
|
||||
}
|
||||
|
||||
// EraseChip erases the entire flash memory chip
|
||||
func (dev *Device) EraseAll() error {
|
||||
if err := dev.WaitUntilReady(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := dev.WriteEnable(); err != nil {
|
||||
return err
|
||||
}
|
||||
return dev.trans.runCommand(cmdEraseChip)
|
||||
}
|
||||
|
||||
// ReadStatus reads the value from status register 1 of the device
|
||||
func (dev *Device) ReadStatus() (status byte, err error) {
|
||||
buf := make([]byte, 1)
|
||||
err = dev.trans.readCommand(cmdReadStatus, buf)
|
||||
return buf[0], err
|
||||
}
|
||||
|
||||
// ReadStatus2 reads the value from status register 2 of the device
|
||||
func (dev *Device) ReadStatus2() (status byte, err error) {
|
||||
buf := make([]byte, 1)
|
||||
err = dev.trans.readCommand(cmdReadStatus2, buf)
|
||||
return buf[0], err
|
||||
}
|
||||
|
||||
// WaitUntilReady queries the status register until the device is ready for the
|
||||
// next operation.
|
||||
func (dev *Device) WaitUntilReady() error {
|
||||
expire := time.Now().UnixNano() + int64(1*time.Second)
|
||||
for s, err := dev.ReadStatus(); (s & 0x03) > 0; s, err = dev.ReadStatus() {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if time.Now().UnixNano() > expire {
|
||||
return ErrWaitExpired
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
const (
|
||||
cmdRead = 0x03 // read memory using single-bit transfer
|
||||
cmdQuadRead = 0x6B // read with 1 line address, 4 line data
|
||||
cmdReadJedecID = 0x9F // read the JEDEC ID from the device
|
||||
cmdPageProgram = 0x02 // write a page of memory using single-bit transfer
|
||||
cmdQuadPageProgram = 0x32 // write with 1 line address, 4 line data
|
||||
cmdReadStatus = 0x05 // read status register 1
|
||||
cmdReadStatus2 = 0x35 // read status register 2
|
||||
cmdWriteStatus = 0x01 // write status register 1
|
||||
cmdWriteStatus2 = 0x31 // write status register 2
|
||||
cmdEnableReset = 0x66 // enable reset
|
||||
cmdReset = 0x99 // perform reset
|
||||
cmdWriteEnable = 0x06 // write-enable memory
|
||||
cmdWriteDisable = 0x04 // write-protect memory
|
||||
cmdEraseSector = 0x20 // erase a sector of memory
|
||||
cmdEraseBlock = 0xD8 // erase a block of memory
|
||||
cmdEraseChip = 0xC7 // erase the entire chip
|
||||
)
|
||||
|
||||
type Error uint8
|
||||
|
||||
const (
|
||||
_ = iota
|
||||
ErrInvalidClockSpeed Error = iota
|
||||
ErrInvalidAddrRange
|
||||
ErrWaitExpired
|
||||
)
|
||||
|
||||
func (err Error) Error() string {
|
||||
switch err {
|
||||
case ErrInvalidClockSpeed:
|
||||
return "flash: invalid clock speed"
|
||||
case ErrInvalidAddrRange:
|
||||
return "flash: invalid address range"
|
||||
case ErrWaitExpired:
|
||||
return "flash: wait until ready expired"
|
||||
default:
|
||||
return "flash: unspecified error"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,247 @@
|
||||
// +build atsamd51
|
||||
|
||||
package flash
|
||||
|
||||
import (
|
||||
"device/sam"
|
||||
"machine"
|
||||
"runtime/volatile"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// NewQSPI returns a pointer to a flash device that uses the QSPI peripheral to
|
||||
// communicate with a serial memory chip.
|
||||
func NewQSPI(cs, sck, d0, d1, d2, d3 machine.Pin) *Device {
|
||||
return &Device{
|
||||
trans: &qspiTransport{
|
||||
cs: cs,
|
||||
sck: sck,
|
||||
d0: d0,
|
||||
d1: d1,
|
||||
d2: d2,
|
||||
d3: d3,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// QSPI address space on SAMD51 is 0x04000000 to 0x05000000
|
||||
const (
|
||||
// Low address of the QSPI address space on SAMD51
|
||||
qspi_AHB_LO = 0x04000000
|
||||
|
||||
// High address of the QSPI address space on SAMD51
|
||||
qspi_AHB_HI = 0x05000000
|
||||
|
||||
// Instruction frame for running sending a command to the device
|
||||
iframeRunCommand = 0x0 |
|
||||
sam.QSPI_INSTRFRAME_WIDTH_SINGLE_BIT_SPI |
|
||||
sam.QSPI_INSTRFRAME_ADDRLEN_24BITS |
|
||||
sam.QSPI_INSTRFRAME_INSTREN |
|
||||
(sam.QSPI_INSTRFRAME_TFRTYPE_READ << sam.QSPI_INSTRFRAME_TFRTYPE_Pos)
|
||||
|
||||
// Instruction frame for running a command that returns data
|
||||
iframeReadCommand = 0x0 |
|
||||
sam.QSPI_INSTRFRAME_WIDTH_SINGLE_BIT_SPI |
|
||||
sam.QSPI_INSTRFRAME_ADDRLEN_24BITS |
|
||||
sam.QSPI_INSTRFRAME_INSTREN |
|
||||
sam.QSPI_INSTRFRAME_DATAEN |
|
||||
(sam.QSPI_INSTRFRAME_TFRTYPE_READ << sam.QSPI_INSTRFRAME_TFRTYPE_Pos)
|
||||
|
||||
// Instruction frame to set up the device to read from memory
|
||||
iframeReadMemory = 0x0 |
|
||||
sam.QSPI_INSTRFRAME_WIDTH_QUAD_OUTPUT |
|
||||
sam.QSPI_INSTRFRAME_ADDRLEN_24BITS |
|
||||
sam.QSPI_INSTRFRAME_INSTREN |
|
||||
sam.QSPI_INSTRFRAME_DATAEN |
|
||||
sam.QSPI_INSTRFRAME_ADDREN |
|
||||
(8 << sam.QSPI_INSTRFRAME_DUMMYLEN_Pos) |
|
||||
(sam.QSPI_INSTRFRAME_TFRTYPE_READMEMORY << sam.QSPI_INSTRFRAME_TFRTYPE_Pos)
|
||||
|
||||
// Instruction frame for running a command that requires parameter data
|
||||
iframeWriteCommand = 0x0 |
|
||||
sam.QSPI_INSTRFRAME_WIDTH_SINGLE_BIT_SPI |
|
||||
sam.QSPI_INSTRFRAME_ADDRLEN_24BITS |
|
||||
sam.QSPI_INSTRFRAME_INSTREN |
|
||||
(sam.QSPI_INSTRFRAME_TFRTYPE_WRITE << sam.QSPI_INSTRFRAME_TFRTYPE_Pos)
|
||||
|
||||
// Instruction frame to set up the device for writing to memory
|
||||
iframeWriteMemory = 0x0 |
|
||||
sam.QSPI_INSTRFRAME_WIDTH_QUAD_OUTPUT |
|
||||
sam.QSPI_INSTRFRAME_ADDRLEN_24BITS |
|
||||
sam.QSPI_INSTRFRAME_INSTREN |
|
||||
sam.QSPI_INSTRFRAME_ADDREN |
|
||||
sam.QSPI_INSTRFRAME_DATAEN |
|
||||
(sam.QSPI_INSTRFRAME_TFRTYPE_WRITEMEMORY << sam.QSPI_INSTRFRAME_TFRTYPE_Pos)
|
||||
|
||||
// Instruction frame for running an erase command that requires and address
|
||||
iframeEraseCommand = 0x0 |
|
||||
sam.QSPI_INSTRFRAME_WIDTH_SINGLE_BIT_SPI |
|
||||
sam.QSPI_INSTRFRAME_ADDRLEN_24BITS |
|
||||
sam.QSPI_INSTRFRAME_INSTREN |
|
||||
sam.QSPI_INSTRFRAME_ADDREN |
|
||||
(sam.QSPI_INSTRFRAME_TFRTYPE_WRITE << sam.QSPI_INSTRFRAME_TFRTYPE_Pos)
|
||||
)
|
||||
|
||||
type qspiTransport struct {
|
||||
cs machine.Pin
|
||||
sck machine.Pin
|
||||
d0 machine.Pin
|
||||
d1 machine.Pin
|
||||
d2 machine.Pin
|
||||
d3 machine.Pin
|
||||
}
|
||||
|
||||
func (q qspiTransport) configure(config *DeviceConfig) {
|
||||
|
||||
// enable main clocks
|
||||
sam.MCLK.APBCMASK.SetBits(sam.MCLK_APBCMASK_QSPI_)
|
||||
sam.MCLK.AHBMASK.SetBits(sam.MCLK_AHBMASK_QSPI_)
|
||||
sam.MCLK.AHBMASK.ClearBits(sam.MCLK_AHBMASK_QSPI_2X_)
|
||||
|
||||
sam.QSPI.CTRLA.SetBits(sam.QSPI_CTRLA_SWRST)
|
||||
|
||||
// enable all pins to be PinCom
|
||||
q.d0.Configure(machine.PinConfig{Mode: machine.PinCom})
|
||||
q.d1.Configure(machine.PinConfig{Mode: machine.PinCom})
|
||||
q.d2.Configure(machine.PinConfig{Mode: machine.PinCom})
|
||||
q.d3.Configure(machine.PinConfig{Mode: machine.PinCom})
|
||||
q.cs.Configure(machine.PinConfig{Mode: machine.PinCom})
|
||||
q.sck.Configure(machine.PinConfig{Mode: machine.PinCom})
|
||||
|
||||
// start out with 4Mhz
|
||||
// can ignore the error, 4Mhz is always a valid speed
|
||||
_ = q.setClockSpeed(4e6)
|
||||
|
||||
// configure the CTRLB register
|
||||
sam.QSPI.CTRLB.Set(sam.QSPI_CTRLB_MODE_MEMORY |
|
||||
(sam.QSPI_CTRLB_DATALEN_8BITS << sam.QSPI_CTRLB_DATALEN_Pos) |
|
||||
(sam.QSPI_CTRLB_CSMODE_LASTXFER << sam.QSPI_CTRLB_CSMODE_Pos))
|
||||
|
||||
// enable the peripheral
|
||||
sam.QSPI.CTRLA.SetBits(sam.QSPI_CTRLA_ENABLE)
|
||||
}
|
||||
|
||||
func (q qspiTransport) supportQuadMode() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (q qspiTransport) setClockSpeed(hz uint32) error {
|
||||
// The clock speed for the QSPI peripheral is controlled by a divider, so
|
||||
// we can't set the requested speed exactly. Instead we will increment the
|
||||
// divider until the speed is less than or equal to the speed requested.
|
||||
for div, freq := uint32(1), machine.CPUFrequency(); div < 256; div++ {
|
||||
if freq/div <= hz {
|
||||
sam.QSPI.BAUD.Set(div << sam.QSPI_BAUD_BAUD_Pos)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return ErrInvalidClockSpeed
|
||||
}
|
||||
|
||||
func (q qspiTransport) runCommand(cmd byte) (err error) {
|
||||
q.runInstruction(cmd, iframeRunCommand)
|
||||
q.endTransfer()
|
||||
return
|
||||
}
|
||||
|
||||
func (q qspiTransport) readCommand(cmd byte, buf []byte) (err error) {
|
||||
q.disableAndClearCache()
|
||||
q.runInstruction(cmd, iframeReadCommand)
|
||||
q.readInto(buf, 0)
|
||||
q.endTransfer()
|
||||
q.enableCache()
|
||||
return
|
||||
}
|
||||
|
||||
func (q qspiTransport) readMemory(addr uint32, buf []byte) (err error) {
|
||||
if (addr + uint32(len(buf))) > (qspi_AHB_HI - qspi_AHB_LO) {
|
||||
return ErrInvalidAddrRange
|
||||
}
|
||||
q.disableAndClearCache()
|
||||
q.runInstruction(cmdQuadRead, iframeReadMemory)
|
||||
q.readInto(buf, addr)
|
||||
q.endTransfer()
|
||||
q.enableCache()
|
||||
return
|
||||
}
|
||||
|
||||
func (q qspiTransport) writeCommand(cmd byte, data []byte) (err error) {
|
||||
var dataen uint32
|
||||
if len(data) > 0 {
|
||||
dataen = sam.QSPI_INSTRFRAME_DATAEN
|
||||
}
|
||||
q.disableAndClearCache()
|
||||
q.runInstruction(cmd, iframeWriteCommand|dataen)
|
||||
q.writeFrom(data, 0)
|
||||
q.endTransfer()
|
||||
q.enableCache()
|
||||
return
|
||||
}
|
||||
|
||||
func (q qspiTransport) writeMemory(addr uint32, data []byte) (err error) {
|
||||
if (addr + uint32(len(data))) > (qspi_AHB_HI - qspi_AHB_LO) {
|
||||
return ErrInvalidAddrRange
|
||||
}
|
||||
q.disableAndClearCache()
|
||||
q.runInstruction(cmdQuadPageProgram, iframeWriteMemory)
|
||||
q.writeFrom(data, addr)
|
||||
q.endTransfer()
|
||||
q.enableCache()
|
||||
return
|
||||
}
|
||||
|
||||
func (q qspiTransport) eraseCommand(cmd byte, addr uint32) (err error) {
|
||||
q.disableAndClearCache()
|
||||
sam.QSPI.INSTRADDR.Set(addr)
|
||||
q.runInstruction(cmd, iframeEraseCommand)
|
||||
q.endTransfer()
|
||||
q.enableCache()
|
||||
return
|
||||
}
|
||||
|
||||
func (q qspiTransport) runInstruction(cmd byte, iframe uint32) {
|
||||
sam.QSPI.INSTRCTRL.Set(uint32(cmd))
|
||||
sam.QSPI.INSTRFRAME.Set(iframe)
|
||||
sam.QSPI.INSTRFRAME.Get() // dummy read for synchronization, as per datasheet
|
||||
}
|
||||
|
||||
func (q qspiTransport) enableCache() {
|
||||
sam.CMCC.CTRL.SetBits(sam.CMCC_CTRL_CEN)
|
||||
}
|
||||
|
||||
func (q qspiTransport) disableAndClearCache() {
|
||||
sam.CMCC.CTRL.ClearBits(sam.CMCC_CTRL_CEN)
|
||||
for sam.CMCC.SR.HasBits(sam.CMCC_SR_CSTS) {
|
||||
}
|
||||
sam.CMCC.MAINT0.SetBits(sam.CMCC_MAINT0_INVALL)
|
||||
}
|
||||
|
||||
func (q qspiTransport) endTransfer() {
|
||||
sam.QSPI.CTRLA.Set(sam.QSPI_CTRLA_ENABLE | sam.QSPI_CTRLA_LASTXFER)
|
||||
for !sam.QSPI.INTFLAG.HasBits(sam.QSPI_INTFLAG_INSTREND) {
|
||||
}
|
||||
sam.QSPI.INTFLAG.Set(sam.QSPI_INTFLAG_INSTREND)
|
||||
}
|
||||
|
||||
func (q qspiTransport) readInto(buf []byte, addr uint32) {
|
||||
var ptr = qspi_AHB_LO + uintptr(addr)
|
||||
for i := range buf {
|
||||
buf[i] = volatile.LoadUint8((*uint8)(unsafe.Pointer(ptr)))
|
||||
ptr++
|
||||
}
|
||||
/* // NB(bcg): for some reason this reads data that results from commands in
|
||||
// a different byte order than the loop above, but works fine for reading
|
||||
// from memory. Oddly, the above loop seems to work fine in both cases.
|
||||
ln := len(buf)
|
||||
sl := (*[1 << 28]byte)(unsafe.Pointer(uintptr(qspi_AHB_LO + addr)))[:ln:ln]
|
||||
copy(buf, sl)
|
||||
*/
|
||||
}
|
||||
|
||||
func (q qspiTransport) writeFrom(buf []byte, addr uint32) {
|
||||
var ptr = qspi_AHB_LO + uintptr(addr)
|
||||
for i := range buf {
|
||||
volatile.StoreUint8((*uint8)(unsafe.Pointer(ptr)), buf[i])
|
||||
ptr++
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
package flash
|
||||
|
||||
import "machine"
|
||||
|
||||
type transport interface {
|
||||
configure(config *DeviceConfig)
|
||||
supportQuadMode() bool
|
||||
setClockSpeed(hz uint32) (err error)
|
||||
runCommand(cmd byte) (err error)
|
||||
readCommand(cmd byte, rsp []byte) (err error)
|
||||
writeCommand(cmd byte, data []byte) (err error)
|
||||
eraseCommand(cmd byte, address uint32) (err error)
|
||||
readMemory(addr uint32, rsp []byte) (err error)
|
||||
writeMemory(addr uint32, data []byte) (err error)
|
||||
}
|
||||
|
||||
// NewSPI returns a pointer to a flash device that uses a SPI peripheral to
|
||||
// communicate with a serial memory chip.
|
||||
func NewSPI(spi *machine.SPI, mosi, miso, sck, cs machine.Pin) *Device {
|
||||
return &Device{
|
||||
trans: &spiTransport{
|
||||
spi: spi,
|
||||
mosi: mosi,
|
||||
miso: miso,
|
||||
sck: sck,
|
||||
ss: cs,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type spiTransport struct {
|
||||
spi *machine.SPI
|
||||
mosi machine.Pin
|
||||
miso machine.Pin
|
||||
sck machine.Pin
|
||||
ss machine.Pin
|
||||
}
|
||||
|
||||
func (tr *spiTransport) configure(config *DeviceConfig) {
|
||||
// Configure spi bus
|
||||
tr.setClockSpeed(5000000)
|
||||
|
||||
// Configure chip select pin
|
||||
tr.ss.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
tr.ss.High()
|
||||
}
|
||||
|
||||
func (tr *spiTransport) setClockSpeed(hz uint32) error {
|
||||
// TODO: un-hardcode this max speed; it is probably a sensible
|
||||
// default maximum for atsamd and nrf at least
|
||||
if hz > 24*1e6 {
|
||||
hz = 24 * 1e6
|
||||
}
|
||||
tr.spi.Configure(machine.SPIConfig{
|
||||
Frequency: hz,
|
||||
MISO: tr.miso,
|
||||
MOSI: tr.mosi,
|
||||
SCK: tr.sck,
|
||||
LSBFirst: false,
|
||||
Mode: 0,
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tr *spiTransport) supportQuadMode() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (tr *spiTransport) runCommand(cmd byte) (err error) {
|
||||
tr.ss.Low()
|
||||
_, err = tr.spi.Transfer(byte(cmd))
|
||||
tr.ss.High()
|
||||
return
|
||||
}
|
||||
|
||||
func (tr *spiTransport) readCommand(cmd byte, rsp []byte) (err error) {
|
||||
tr.ss.Low()
|
||||
if _, err := tr.spi.Transfer(byte(cmd)); err == nil {
|
||||
err = tr.readInto(rsp)
|
||||
}
|
||||
tr.ss.High()
|
||||
return
|
||||
}
|
||||
|
||||
func (tr *spiTransport) readCommandByte(cmd byte) (rsp byte, err error) {
|
||||
tr.ss.Low()
|
||||
if _, err := tr.spi.Transfer(byte(cmd)); err == nil {
|
||||
rsp, err = tr.spi.Transfer(0xFF)
|
||||
}
|
||||
tr.ss.High()
|
||||
return
|
||||
}
|
||||
|
||||
func (tr *spiTransport) writeCommand(cmd byte, data []byte) (err error) {
|
||||
tr.ss.Low()
|
||||
if _, err := tr.spi.Transfer(byte(cmd)); err == nil {
|
||||
err = tr.writeFrom(data)
|
||||
}
|
||||
tr.ss.High()
|
||||
return
|
||||
}
|
||||
|
||||
func (tr *spiTransport) eraseCommand(cmd byte, address uint32) (err error) {
|
||||
tr.ss.Low()
|
||||
err = tr.sendAddress(cmd, address)
|
||||
tr.ss.High()
|
||||
return
|
||||
}
|
||||
|
||||
func (tr *spiTransport) readMemory(addr uint32, rsp []byte) (err error) {
|
||||
tr.ss.Low()
|
||||
if err = tr.sendAddress(cmdRead, addr); err == nil {
|
||||
err = tr.readInto(rsp)
|
||||
}
|
||||
tr.ss.High()
|
||||
return
|
||||
}
|
||||
|
||||
func (tr *spiTransport) writeMemory(addr uint32, data []byte) (err error) {
|
||||
tr.ss.Low()
|
||||
if err = tr.sendAddress(cmdPageProgram, addr); err == nil {
|
||||
err = tr.writeFrom(data)
|
||||
}
|
||||
tr.ss.High()
|
||||
return
|
||||
}
|
||||
|
||||
func (tr *spiTransport) sendAddress(cmd byte, addr uint32) error {
|
||||
_, err := tr.spi.Transfer(byte(cmd))
|
||||
if err == nil {
|
||||
_, err = tr.spi.Transfer(byte((addr >> 16) & 0xFF))
|
||||
}
|
||||
if err == nil {
|
||||
_, err = tr.spi.Transfer(byte((addr >> 8) & 0xFF))
|
||||
}
|
||||
if err == nil {
|
||||
_, err = tr.spi.Transfer(byte(addr & 0xFF))
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (tr *spiTransport) readInto(rsp []byte) (err error) {
|
||||
for i, c := 0, len(rsp); i < c && err == nil; i++ {
|
||||
rsp[i], err = tr.spi.Transfer(0xFF)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (tr *spiTransport) writeFrom(data []byte) (err error) {
|
||||
for i, c := 0, len(data); i < c && err == nil; i++ {
|
||||
_, err = tr.spi.Transfer(data[i])
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
// Package hcsr04 provides a driver for the HC-SR04 ultrasonic distance sensor
|
||||
//
|
||||
// Datasheet:
|
||||
// https://cdn.sparkfun.com/datasheets/Sensors/Proximity/HCSR04.pdf
|
||||
package hcsr04
|
||||
|
||||
import (
|
||||
"machine"
|
||||
"time"
|
||||
)
|
||||
|
||||
const TIMEOUT = 23324 // max sensing distance (4m)
|
||||
|
||||
// Device holds the pins
|
||||
type Device struct {
|
||||
trigger machine.Pin
|
||||
echo machine.Pin
|
||||
}
|
||||
|
||||
// New returns a new ultrasonic driver given 2 pins
|
||||
func New(trigger, echo machine.Pin) Device {
|
||||
return Device{
|
||||
trigger: trigger,
|
||||
echo: echo,
|
||||
}
|
||||
}
|
||||
|
||||
// Configure configures the pins of the Device
|
||||
func (d *Device) Configure() {
|
||||
d.trigger.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
d.echo.Configure(machine.PinConfig{Mode: machine.PinInput})
|
||||
}
|
||||
|
||||
// ReadDistance returns the distance of the object in mm
|
||||
func (d *Device) ReadDistance() int32 {
|
||||
pulse := d.ReadPulse()
|
||||
|
||||
// sound speed is 343000 mm/s
|
||||
// pulse is roundtrip measured in microseconds
|
||||
// distance = velocity * time
|
||||
// 2 * distance = 343000 * (pulse/1000000)
|
||||
return (pulse * 1715) / 10000 //mm
|
||||
}
|
||||
|
||||
// ReadPulse returns the time of the pulse (roundtrip) in microseconds
|
||||
func (d *Device) ReadPulse() int32 {
|
||||
t := time.Now()
|
||||
d.trigger.Low()
|
||||
time.Sleep(2 * time.Microsecond)
|
||||
d.trigger.High()
|
||||
time.Sleep(10 * time.Microsecond)
|
||||
d.trigger.Low()
|
||||
i := uint8(0)
|
||||
for {
|
||||
if d.echo.Get() {
|
||||
t = time.Now()
|
||||
break
|
||||
}
|
||||
i++
|
||||
if i > 10 {
|
||||
if time.Since(t).Microseconds() > TIMEOUT {
|
||||
return 0
|
||||
}
|
||||
i = 0
|
||||
}
|
||||
}
|
||||
i = 0
|
||||
for {
|
||||
if !d.echo.Get() {
|
||||
return int32(time.Since(t).Microseconds())
|
||||
}
|
||||
i++
|
||||
if i > 10 {
|
||||
if time.Since(t).Microseconds() > TIMEOUT {
|
||||
return 0
|
||||
}
|
||||
i = 0
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
@@ -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,310 @@
|
||||
package ili9341
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"image/color"
|
||||
"machine"
|
||||
"time"
|
||||
)
|
||||
|
||||
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
|
||||
d.rotation = config.Rotation
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
d.SetRotation(d.rotation)
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// SetScrollWindow sets an area to scroll with fixed top and bottom parts of the display
|
||||
func (d *Device) SetScrollArea(topFixedArea, bottomFixedArea int16) {
|
||||
d.sendCommand(VSCRDEF, []uint8{
|
||||
uint8(topFixedArea >> 8), uint8(topFixedArea),
|
||||
uint8(d.height - topFixedArea - bottomFixedArea>>8),
|
||||
uint8(d.height - topFixedArea - bottomFixedArea),
|
||||
uint8(bottomFixedArea >> 8), uint8(bottomFixedArea),
|
||||
})
|
||||
}
|
||||
|
||||
// SetScroll sets the vertical scroll address of the display.
|
||||
func (d *Device) SetScroll(line int16) {
|
||||
d.sendCommand(VSCRSADD, []uint8{uint8(line >> 8), uint8(line)})
|
||||
}
|
||||
|
||||
// SpotScroll returns the display to its normal state
|
||||
func (d *Device) StopScroll() {
|
||||
d.sendCommand(NORON, nil)
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
+1
-1
@@ -166,7 +166,7 @@ func (d *Device) ReadRotation() (x int32, y int32, z int32) {
|
||||
return
|
||||
}
|
||||
|
||||
// ReadTemperature returns the temperature in celsius milli degrees (ºC/1000)
|
||||
// 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)
|
||||
|
||||
|
||||
+1
-1
@@ -50,7 +50,7 @@ func (d Device) ReadMagnetic() (x int16, y int16, z int16) {
|
||||
}
|
||||
|
||||
// ReadTemperature reads and returns the current die temperature in
|
||||
// celsius milli degrees (ºC/1000).
|
||||
// celsius milli degrees (°C/1000).
|
||||
func (d Device) ReadTemperature() (int32, error) {
|
||||
data := make([]byte, 1)
|
||||
d.bus.ReadRegister(uint8(d.Address), DIE_TEMP, data)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -8,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
|
||||
@@ -19,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
|
||||
@@ -71,6 +76,12 @@ func (c *mqttclient) Connect() Token {
|
||||
return &mqtttoken{err: errors.New("invalid protocol")}
|
||||
}
|
||||
|
||||
c.mid = 1
|
||||
c.inbound = make(chan packets.ControlPacket, 10)
|
||||
c.stop = make(chan struct{})
|
||||
c.incomingPubChan = make(chan *packets.PublishPacket, 10)
|
||||
c.msgRouter.matchAndDispatch(c.incomingPubChan, c.opts.Order, c)
|
||||
|
||||
// send the MQTT connect message
|
||||
connectPkt := packets.NewControlPacket(packets.Connect).(*packets.ConnectPacket)
|
||||
connectPkt.Qos = 0
|
||||
@@ -84,36 +95,35 @@ 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
|
||||
connectPkt.Keepalive = 60
|
||||
|
||||
err = connectPkt.Write(c.conn)
|
||||
if err != nil {
|
||||
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{}
|
||||
}
|
||||
|
||||
@@ -129,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
|
||||
@@ -144,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{}
|
||||
}
|
||||
|
||||
@@ -173,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,178 @@
|
||||
// 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
|
||||
hd(client, m)
|
||||
//TODO: m.Ack()
|
||||
}
|
||||
sent = true
|
||||
}
|
||||
}
|
||||
if !sent && r.defaultHandler != nil {
|
||||
if order {
|
||||
handlers = append(handlers, r.defaultHandler)
|
||||
} else {
|
||||
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,32 @@
|
||||
// +build pybadge
|
||||
|
||||
package shifter
|
||||
|
||||
import "machine"
|
||||
|
||||
const (
|
||||
BUTTON_LEFT = 0
|
||||
BUTTON_UP = 1
|
||||
BUTTON_DOWN = 2
|
||||
BUTTON_RIGHT = 3
|
||||
BUTTON_SELECT = 4
|
||||
BUTTON_START = 5
|
||||
BUTTON_A = 6
|
||||
BUTTON_B = 7
|
||||
)
|
||||
|
||||
// NewButtons returns a new shifter device for the buttons on an AdaFruit PyBadge
|
||||
func NewButtons() Device {
|
||||
return Device{
|
||||
latch: machine.BUTTON_LATCH,
|
||||
clk: machine.BUTTON_CLK,
|
||||
out: machine.BUTTON_OUT,
|
||||
Pins: make([]ShiftPin, int(EIGHT_BITS)),
|
||||
bits: EIGHT_BITS,
|
||||
}
|
||||
}
|
||||
|
||||
// ReadInput returns the latest input readings from the PyBadge.
|
||||
func (d *Device) ReadInput() (uint8, error) {
|
||||
return d.Read8Input()
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
// 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
|
||||
pressed bool
|
||||
}
|
||||
|
||||
// New returns a new shifter driver given the correct pins.
|
||||
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 updates the internal pins' states and returns it as 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 updates the internal pins' states and returns it as 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 updates the internal pins' states and returns it as 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 pin's state for a specific ShiftPin.
|
||||
// Read{8|16|32}Input should be called before to update the state. Read{8|16|32}Input updates
|
||||
// all the pins, no need to call it for each pin individually.
|
||||
func (p ShiftPin) Get() bool {
|
||||
return p.pressed
|
||||
}
|
||||
|
||||
// Configure here just for interface compatibility.
|
||||
func (p ShiftPin) Configure() {
|
||||
}
|
||||
|
||||
// readInput reads howMany bits from the shift register and updates the internal pins' states.
|
||||
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.Pins[i].pressed = true
|
||||
} else {
|
||||
d.Pins[i].pressed = false
|
||||
}
|
||||
d.clk.High()
|
||||
}
|
||||
d.latch.Low()
|
||||
return data
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
// Package shiftregister is for 8bit shift output register using 3 GPIO pins like SN74ALS164A, SN74AHC594, SN74AHC595, ...
|
||||
package shiftregister
|
||||
|
||||
import (
|
||||
"machine"
|
||||
)
|
||||
|
||||
type NumberBit int8
|
||||
|
||||
// Bit number of the register
|
||||
const (
|
||||
EIGHT_BITS NumberBit = 8
|
||||
SIXTEEN_BITS NumberBit = 16
|
||||
THIRTYTWO_BITS NumberBit = 32
|
||||
)
|
||||
|
||||
// Device holds pin number
|
||||
type Device struct {
|
||||
latch, clock, out machine.Pin // IC wiring
|
||||
bits NumberBit // Pin number
|
||||
mask uint32 // keep all pins state
|
||||
}
|
||||
|
||||
// ShiftPin is the implementation of the ShiftPin interface.
|
||||
// ShiftPin provide an interface like regular machine.Pin
|
||||
type ShiftPin struct {
|
||||
mask uint32 // Bit representing the pin
|
||||
d *Device // Reference to the register
|
||||
}
|
||||
|
||||
// New returns a new shift output register device
|
||||
func New(Bits NumberBit, Latch, Clock, Out machine.Pin) *Device {
|
||||
return &Device{
|
||||
latch: Latch,
|
||||
clock: Clock,
|
||||
out: Out,
|
||||
bits: Bits,
|
||||
}
|
||||
}
|
||||
|
||||
// Configure set hardware configuration
|
||||
func (d *Device) Configure() {
|
||||
d.latch.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
d.clock.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
d.out.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
d.latch.High()
|
||||
}
|
||||
|
||||
// WriteMask applies mask's bits to register's outputs pin
|
||||
// mask's MSB set Q1, LSB set Q8 (for 8 bits mask)
|
||||
func (d *Device) WriteMask(mask uint32) {
|
||||
d.mask = mask // Keep the mask for individual addressing
|
||||
d.latch.Low()
|
||||
for i := 0; i < int(d.bits); i++ {
|
||||
d.clock.Low()
|
||||
d.out.Set(mask&1 != 0)
|
||||
mask = mask >> 1
|
||||
d.clock.High()
|
||||
}
|
||||
d.latch.High()
|
||||
}
|
||||
|
||||
// GetShiftPin return an individually addressable pin
|
||||
func (d *Device) GetShiftPin(pin int) *ShiftPin {
|
||||
if pin < 0 || pin > int(d.bits) {
|
||||
panic("invalid pin number")
|
||||
}
|
||||
return &ShiftPin{
|
||||
mask: 1 << pin,
|
||||
d: d,
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Set changes the value of this register pin.
|
||||
func (p ShiftPin) Set(value bool) {
|
||||
d := p.d
|
||||
if value {
|
||||
d.WriteMask(d.mask | p.mask)
|
||||
} else {
|
||||
d.WriteMask(d.mask & ^p.mask)
|
||||
}
|
||||
}
|
||||
|
||||
// High sets this shift register pin to high.
|
||||
func (p ShiftPin) High() {
|
||||
p.Set(true)
|
||||
}
|
||||
|
||||
// Low sets this shift register pin to low.
|
||||
func (p ShiftPin) Low() {
|
||||
p.Set(false)
|
||||
}
|
||||
+1
-1
@@ -29,7 +29,7 @@ func New(bus machine.I2C) Device {
|
||||
}
|
||||
}
|
||||
|
||||
// Read returns the temperature in celsius milli degrees (ºC/1000).
|
||||
// Read returns the temperature in celsius milli degrees (°C/1000).
|
||||
func (d *Device) ReadTemperature() (tempMilliCelsius int32, err error) {
|
||||
tempMilliCelsius, _, err = d.ReadTemperatureHumidity()
|
||||
return tempMilliCelsius, err
|
||||
|
||||
@@ -46,6 +46,8 @@ const (
|
||||
PWCTR6 = 0xFC
|
||||
GMCTRP1 = 0xE0
|
||||
GMCTRN1 = 0xE1
|
||||
VSCRDEF = 0x33
|
||||
VSCRSADD = 0x37
|
||||
|
||||
GREENTAB Model = 0
|
||||
MINI80x160 Model = 1
|
||||
|
||||
+23
-35
@@ -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()
|
||||
|
||||
+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)
|
||||
|
||||
@@ -60,7 +60,7 @@ func (d *Device) Configure() {
|
||||
d.adc.Configure()
|
||||
}
|
||||
|
||||
// ReadTemperature returns the temperature in celsius milli degrees (ºC/1000)
|
||||
// ReadTemperature returns the temperature in celsius milli degrees (°C/1000)
|
||||
func (d *Device) ReadTemperature() (temperature int32, err error) {
|
||||
var reading uint32
|
||||
if d.HighSide {
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package tmp102
|
||||
|
||||
const (
|
||||
// Default I2C address
|
||||
Address = 0x48
|
||||
|
||||
// Temperature register address
|
||||
RegTemperature = 0x00
|
||||
|
||||
// Configuration register address
|
||||
RegConfiguration = 0x01
|
||||
|
||||
// Low limit register address
|
||||
RegLimitLow = 0x02
|
||||
|
||||
// High limit register address
|
||||
RegLimitHigh = 0x03
|
||||
)
|
||||
@@ -0,0 +1,58 @@
|
||||
// Package tmp102 implements a driver for the TMP102 digital temperature sensor.
|
||||
//
|
||||
// Datasheet: https://download.mikroe.com/documents/datasheets/tmp102-data-sheet.pdf
|
||||
|
||||
package tmp102 // import "tinygo.org/x/drivers/tmp102"
|
||||
|
||||
import (
|
||||
"machine"
|
||||
)
|
||||
|
||||
// Device holds the already configured I2C bus and the address of the sensor.
|
||||
type Device struct {
|
||||
bus machine.I2C
|
||||
address uint8
|
||||
}
|
||||
|
||||
// Config is the configuration for the TMP102.
|
||||
type Config struct {
|
||||
Address uint8
|
||||
}
|
||||
|
||||
// New creates a new TMP102 connection. The I2C bus must already be configured.
|
||||
func New(bus machine.I2C) Device {
|
||||
return Device{
|
||||
bus: bus,
|
||||
}
|
||||
}
|
||||
|
||||
// Configure initializes the sensor with the given parameters.
|
||||
func (d *Device) Configure(cfg Config) {
|
||||
if cfg.Address == 0 {
|
||||
cfg.Address = Address
|
||||
}
|
||||
|
||||
d.address = cfg.Address
|
||||
}
|
||||
|
||||
// Reads the temperature from the sensor and returns it in celsius milli degrees (°C/1000).
|
||||
func (d *Device) ReadTemperature() (temperature int32, err error) {
|
||||
|
||||
tmpData := make([]byte, 2)
|
||||
|
||||
err = d.bus.ReadRegister(d.address, RegTemperature, tmpData)
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
temperatureSum := int32((int16(tmpData[0])<<8 | int16(tmpData[1])) >> 4)
|
||||
|
||||
if (temperatureSum & int32(1<<11)) == int32(1<<11) {
|
||||
temperatureSum |= int32(0xf800)
|
||||
}
|
||||
|
||||
temperature = temperatureSum * 625
|
||||
|
||||
return temperature / 10, nil
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
+1
-1
@@ -2,4 +2,4 @@ package drivers
|
||||
|
||||
// Version returns a user-readable string showing the version of the drivers package for support purposes.
|
||||
// Update this value before release of new version of software.
|
||||
const Version = "0.7.0"
|
||||
const Version = "0.12.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
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user