Compare commits

...

67 Commits

Author SHA1 Message Date
Ron Evans 9f23761c5e Updates for v0.12.0 release
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2020-04-13 21:21:45 +02:00
Ron Evans 2ea620026b docs: rearrange list of drivers to be in alpha order
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2020-04-12 16:34:44 +02:00
Daniel Esteban 04be2320b7 Added HC-SR04 ultrasonic distance sensor. (#143)
* Added HC-SR04 ultrasonic distance sensor.
2020-04-12 16:29:46 +02:00
BCG b1529dcf7a Low-level IO driver for serial flash memory via SPI and QSPI (#124)
* QSPI/SPI: flash memory functions
2020-04-11 17:59:58 +02:00
Ron Evans 1987f424ad mqtt: reduce use of goroutines in router to not start a new goroutine for each invocation of each callback
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2020-04-11 16:58:37 +02:00
Yannis Huber 6f213e97c3 Add driver for TMP102 low-power digital temperature sensor (#141)
* tmp102: add driver and example
2020-04-03 13:11:46 +02:00
Daniel Esteban ebceed6014 AMG88xx thermal camera module 2020-03-17 12:11:03 +01:00
Ron Evans 08cc84ce48 Updates for version 0.11 release
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2020-03-07 09:16:59 -08:00
Ron Evans 0c7b6c0d3c docs: update readme to correct count of supported drivers
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2020-03-07 09:16:08 -08:00
Ron Evans 8b11387d30 license: update year to 2020
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2020-03-07 09:11:30 -08:00
Ron Evans 41c6e3be0b shifter: simplify API surface for PyBadge (#137)
* shifter: simplify API surface and use build directive to directly match the PyBadge

Signed-off-by: Ron Evans <ron@hybridgroup.com>

* shifter: further simplify API for PyBadge

Signed-off-by: Ron Evans <ron@hybridgroup.com>
2020-03-06 07:29:30 +01:00
Daniel Esteban 583e80026a new API for shifter driver.
this doesn't break existing code, but it might no longer work as expected
2020-03-06 01:30:27 +00:00
Jean François CASSAN c7cbd7c6cd Shiftregister implementation (#135)
* Basic support of SIPO shift register

* typo

* add example of shiftregister for arduino and nucleo

* Fix build flag for nucleof103rb

* Fix wrong data pin configuration

* Change README.md for Shift registers

* Add API for individual register's output pin

* Rewrite shift register example to show ShiftPin usage

* Fix target for shiftregister example smoke test

* Fix type in makefile

* Add shiftregister compatble IC

* Edit comment and readme
2020-03-04 18:48:06 +01:00
Ron Evans 12ac4c2c06 mqtt: use buffered channels for incoming messages to handle bursts
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2020-03-03 09:29:18 +01:00
Ayke van Laethem 6380ad5ed5 semihosting: add example 2020-02-26 19:56:13 +01:00
Daniel Esteban d61fe99ef1 fix typo on StartScanNetworks 2020-02-26 19:55:12 +01:00
Daniel Esteban 8c7eed8d38 some fixes on ili9341 driver 2020-02-24 08:28:15 +01:00
BCG fbe36b62fd Adding scroll functionality for ili9341 (#121)
* ili9341: Adding scroll functionality
2020-02-08 21:12:20 +01:00
Daniel Esteban 0519540d0c fix celsius symbol 2020-02-08 19:54:31 +01:00
Jordan Christiansen 2034f92d11 README: Use degree sign instead of ordinal
º is the masculine ordinal in Spanish. ° is the degree sign. They look different in some fonts.
2020-02-08 19:32:10 +01:00
Ron Evans 850df0a25c release: update for version 0.10.0
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2020-01-31 18:43:12 +01:00
Ron Evans 5f4806f0f9 ws2812: work-arounds to allow Digispark to control WS2812 LEDs
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2020-01-30 14:24:39 +01:00
Daniel Esteban c8e62562b5 added scroll functionality to st7735 2020-01-30 10:20:45 +01:00
Ron Evans 6842bdb424 docs: update README to include list of all 44 drivers
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2020-01-28 19:27:24 +01:00
BCG d43263f764 Adding driver for four-wire resistive touchscreen (#118)
* resistive: Adding driver for four-wire resistive touchscreen, as used on the Adafruit PyPortal.
2020-01-28 18:55:31 +01:00
BCG 6716bb6c0a ILI9341 TFT driver (#115)
* ILI9341: TFT display implementation
2020-01-07 20:11:46 +01:00
BCG f4bccd1fed Added nrf52840 tag to ws2812 2020-01-06 07:37:21 +01:00
Ron Evans d5aa295b76 l9110x: add support for L9110x h-bridge motor driver
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2020-01-03 10:37:30 +01:00
Ron Evans c6e8af3057 l293x: added support for h-bridge motor controller
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2020-01-03 10:05:23 +01:00
Ron Evans 38076352eb wifinina: update docs and add Dockerfile to build firmware
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-12-29 17:45:41 +01:00
Ron Evans 086415605e wifinina: update docs and info on how to install WiFiNINA driver
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-12-26 16:34:35 +01:00
BCG 1d0f04af6b Support for ADT7410 temperature sensor (#109)
* ADT7410: add support for i2c temperature sensor
2019-12-26 13:53:06 +01:00
Daniel Esteban dc883d913d remove stxx defaults offsets 2019-12-24 17:36:20 +01:00
Ron Evans 3bb5b4519b docs: correct driver count in README
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-12-23 17:32:07 +01:00
Ron Evans c12f78eb50 Release: v0.9.0
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-12-23 17:09:00 +01:00
Daniel Esteban adb0c2c261 added generic 8bit shift register (#107)
* shifter: added generic 8bit shift register driver with ShiftPin Pin-compatible GPIO interface
2019-12-22 21:32:49 +01:00
Daniel Esteban 18f0722728 Dual stepper (#101)
* easystepper: added _DualDevice_ to move two motors at the same time
2019-12-22 10:33:02 +01:00
BCG e80a22d0ba Client driver for WiFiNINA firmware (#98)
* wifinina: implementation of WiFiNINA driver, including:
   - TCP client example is working
   - reading sockets and mqtt working
   - switched over to common net package also used by espat package
   - smoke tests and updated README for wifinina
2019-12-22 10:25:10 +01:00
BCG cc5ecafacf Decoupled net package from espat 2019-12-17 22:09:23 +01:00
Ayke van Laethem 7b56e61d52 ws2812: add support for simulation
Call a special __tinygo_ws2812_write_byte function to send a single
byte.
2019-12-14 22:16:41 +01:00
Ayke van Laethem 514b436889 ws2812: fix "invalid symbol redefinition" error
Named local labels shouldn't be used in LLVM because they might get
duplicated resulting in multiple symbol definitions and a compiler
error. Instead, use numeric labels.
2019-12-14 22:11:10 +01:00
BCG 7233452819 Added Feather M0 and Trinket M0 to build tags for WS2812 2019-12-12 14:28:21 +01:00
Ayke van Laethem 7929aa10ea all: add md5sum to smoke tests
This makes it easier to quickly compare all smoke tests for any changes.
2019-12-11 22:13:48 +01:00
Daniel Esteban 02084fd8a5 improvements in st7735/st7789 drivers 2019-12-08 16:38:05 +01:00
Jonathan Basseri f822da51fe Add support for Cortex-M4 120MHz
This adjusts the timing for 120MHz as seen on atsamd51 boards. The
timing was kept as close as possible to the Cortex-M4 64MHz logic in
ws2812_m4_64m.go.

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

Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-11-25 15:56:09 +01:00
Ayke van Laethem 2e606b090a semihosting: initial implementation of ARM semihosting
Useful for logging output to the host console.
2019-11-06 18:53:33 +01:00
Ron Evans e0cdc931e7 mcp3008: add implementation for MCP3008 ADC with SPI interface
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-10-31 11:49:49 +01:00
Daniel Esteban 130d9de03b Merge pull request #92 from tinygo-org/st7789-buffer-overflow
st7789: fix index out of bounds error
2019-10-28 12:30:44 +01:00
Ayke van Laethem 2c2f1d3db4 st7789: fix index out of bounds error
This commit adds the same check to the st7789 that also exists in the
st7735.
2019-10-28 12:23:56 +01:00
Ron Evans 2413eb86e0 release: update versions to 0.7.0
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-10-17 13:18:24 +02:00
Ron Evans c7555a1469 espat: change all examples to use Arduino Nano33 IoT by default
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-10-17 13:16:39 +02:00
Ron Evans 3fca96e0ef docs: complete missing GoDocs for main and sub-packages
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-10-17 11:50:03 +02:00
Ron Evans c7981f72ec core: add Version string for support purposes
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-10-16 23:23:10 +02:00
Ron Evans 5df157230f lis3dh: example uses I2C1 so requires config to specify pins since they are not default
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-10-16 21:44:58 +02:00
Daniel Esteban bcb291992c ssd1331: make SPI TX faster
same change as PR #89
2019-10-13 02:02:22 +02:00
Daniel Esteban 7b710e3a48 Merge pull request #89 from tinygo-org/st7735-shrink-tx
st7735: make SPI Tx faster
2019-10-11 18:12:20 +02:00
Ayke van Laethem dcfd9c066d st7735: make SPI Tx faster
Most of the ceremony around pin toggling was in fact unnecessary. This
improves performance measurably (rougly 6% in one measurement) and cuts
down on binary size by 80 bytes.
2019-10-10 20:35:50 +02:00
cn 955b3a56e8 veml6070: add Vishay UV light sensor 2019-09-21 10:58:28 +02:00
Ron Evans 21b8d953f4 Update for 0.6.0 release
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-09-09 12:48:57 +02:00
BCG d1b917b835 Support software SPI for APA102 (Itsy Bitsy M0 on-board "Dotstar" LED as example) (#86)
* Added implementation and example to support software-based SPI for APA102, for use with boards like Adafruit Itsy Bitsy M0 for instance.
2019-09-09 12:31:17 +02:00
Ron Evans 2cd73e3204 release: update changelog for 0.5.0
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-08-26 11:34:59 +02:00
Daniel Esteban 3ae5895183 Initial support for LSM6DS3 IMU (#83)
* Initial support for LSM6DS3 IMU
2019-08-26 08:56:48 +02:00
Ayke van Laethem d80f619c9f ws2812: fix timings for the nrf51
The timings needed to be changed slightly for ws2811 chips which are
slightly slower.
2019-08-19 10:55:00 +02:00
Brad Erickson c91888a099 ws2812: Add build tag for Arduino Nano33 IoT
Uses `arduino_nano33` to handle only this board. Other boards with
the same chip will need a separate tag.
2019-08-15 17:38:27 +02:00
116 changed files with 10092 additions and 482 deletions
+107
View File
@@ -1,3 +1,110 @@
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**
- veml6070: add Vishay UV light sensor
- **enhancements**
- lis3dh: example uses I2C1 so requires config to specify pins since they are not default
- ssd1331: make SPI TX faster
- st7735: make SPI Tx faster
- **docs**
- complete missing GoDocs for main and sub-packages
- **core**
- add Version string for support purposes
- **examples**
- Change all espat driver examples to use Arduino Nano33 IoT by default
0.6.0
---
- **new devices**
- Support software SPI for APA102 (Itsy Bitsy M0 on-board "Dotstar" LED as example)
0.5.0
---
- **new devices**
- LSM6DS3 accelerometer
- **bugfixes**
- ws2812: fix timings for the nrf51
- **enhancements**
- ws2812: Add build tag for Arduino Nano33 IoT
0.4.0
---
- **new devices**
+1 -1
View File
@@ -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
+122 -39
View File
@@ -9,44 +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=microbit ./examples/at24cx/main.go
tinygo build -size short -o ./build/test.elf -target=itsybitsy-m0 ./examples/bh1750/main.go
tinygo build -size short -o ./build/test.elf -target=itsybitsy-m0 ./examples/blinkm/main.go
tinygo build -size short -o ./build/test.elf -target=itsybitsy-m0 ./examples/bmp180/main.go
tinygo build -size short -o ./build/test.elf -target=bluepill ./examples/ds1307/sram/main.go
tinygo build -size short -o ./build/test.elf -target=bluepill ./examples/ds1307/time/main.go
tinygo build -size short -o ./build/test.elf -target=itsybitsy-m0 ./examples/ds3231/main.go
tinygo build -size short -o ./build/test.elf -target=microbit ./examples/easystepper/main.go
tinygo build -size short -o ./build/test.elf -target=itsybitsy-m0 ./examples/espat/espconsole/main.go
tinygo build -size short -o ./build/test.elf -target=itsybitsy-m0 ./examples/espat/esphub/main.go
tinygo build -size short -o ./build/test.elf -target=itsybitsy-m0 ./examples/espat/espstation/main.go
tinygo build -size short -o ./build/test.elf -target=feather-m0 ./examples/gps/i2c/main.go
tinygo build -size short -o ./build/test.elf -target=feather-m0 ./examples/gps/uart/main.go
tinygo build -size short -o ./build/test.elf -target=microbit ./examples/hd44780/customchar/main.go
tinygo build -size short -o ./build/test.elf -target=microbit ./examples/hd44780/text/main.go
tinygo build -size short -o ./build/test.elf -target=microbit ./examples/hub75/main.go
tinygo build -size short -o ./build/test.elf -target=circuitplay-express ./examples/lis3dh/main.go
tinygo build -size short -o ./build/test.elf -target=itsybitsy-m0 ./examples/mag3110/main.go
tinygo build -size short -o ./build/test.elf -target=microbit ./examples/microbitmatrix/main.go
tinygo build -size short -o ./build/test.elf -target=itsybitsy-m0 ./examples/mma8653/main.go
tinygo build -size short -o ./build/test.elf -target=itsybitsy-m0 ./examples/mpu6050/main.go
tinygo build -size short -o ./build/test.elf -target=microbit ./examples/pcd8544/setbuffer/main.go
tinygo build -size short -o ./build/test.elf -target=microbit ./examples/pcd8544/setpixel/main.go
tinygo build -size short -o ./build/test.elf -target=microbit ./examples/sht3x/main.go
tinygo build -size short -o ./build/test.elf -target=microbit ./examples/ssd1306/i2c_128x32/main.go
tinygo build -size short -o ./build/test.elf -target=microbit ./examples/ssd1306/spi_128x64/main.go
tinygo build -size short -o ./build/test.elf -target=microbit ./examples/ssd1331/main.go
tinygo build -size short -o ./build/test.elf -target=microbit ./examples/st7735/main.go
tinygo build -size short -o ./build/test.elf -target=microbit ./examples/st7789/main.go
tinygo build -size short -o ./build/test.elf -target=circuitplay-express ./examples/thermistor/main.go
tinygo build -size short -o ./build/test.elf -target=itsybitsy-m0 ./examples/vl53l1x/main.go
tinygo build -size short -o ./build/test.elf -target=microbit ./examples/waveshare-epd/epd2in13/main.go
tinygo build -size short -o ./build/test.elf -target=microbit ./examples/waveshare-epd/epd2in13x/main.go
tinygo build -size short -o ./build/test.elf -target=circuitplay-express ./examples/ws2812/main.go
tinygo build -size short -o ./build/test.elf -target=trinket-m0 ./examples/bme280/main.go
tinygo build -size short -o ./build/test.elf -target=circuitplay-express ./examples/microphone/main.go
tinygo build -size short -o ./build/test.elf -target=circuitplay-express ./examples/buzzer/main.go
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/adt7410/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/adxl345/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=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
+23 -4
View File
@@ -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,26 +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
+90
View File
@@ -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])
}
+24
View File
@@ -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
)
+158
View File
@@ -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
}
+46
View File
@@ -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
)
+15 -2
View File
@@ -21,15 +21,28 @@ const (
// Device wraps APA102 SPI LEDs.
type Device struct {
bus machine.SPI
bus SPI
Order int
}
// The SPI interface specifies the minimum functionality that a bus
// implementation needs to provide for use by the APA102 driver. Hardware
// SPI from the TinyGo "machine" package implements this already.
type SPI interface {
Tx(w, r []byte) error
}
// New returns a new APA102 driver. Pass in a fully configured SPI bus.
func New(b machine.SPI) Device {
func New(b SPI) Device {
return Device{bus: b, Order: BGR}
}
// NewSoftwareSPI returns a new APA102 driver that will use a software based
// implementation of the SPI protocol.
func NewSoftwareSPI(sckPin, mosiPin machine.Pin, delay uint32) Device {
return New(&bbSPI{SCK: sckPin, MOSI: mosiPin, Delay: delay})
}
// WriteColors writes the given RGBA color slice out using the APA102 protocol.
// The A value (Alpha channel) is used for brightness, set to 0xff (255) for maximum.
func (d Device) WriteColors(cs []color.RGBA) (n int, err error) {
+68
View File
@@ -0,0 +1,68 @@
package apa102
import "machine"
// bbSPI is a dumb bit-bang implementation of SPI protocol that is hardcoded
// to mode 0 and ignores trying to receive data. Just enough for the APA102.
// Note: making this unexported for now because it is probable not suitable
// most purposes other than the APA102 package. It might be desirable to make
// this more generic and include it in the TinyGo "machine" package instead.
type bbSPI struct {
SCK machine.Pin
MOSI machine.Pin
Delay uint32
}
// Configure sets up the SCK and MOSI pins as outputs and sets them low
func (s *bbSPI) Configure() {
s.SCK.Configure(machine.PinConfig{Mode: machine.PinOutput})
s.MOSI.Configure(machine.PinConfig{Mode: machine.PinOutput})
s.SCK.Low()
s.MOSI.Low()
if s.Delay == 0 {
s.Delay = 1
}
}
// Tx matches signature of machine.SPI.Tx() and is used to send multiple bytes.
// The r slice is ignored and no error will ever be returned.
func (s *bbSPI) Tx(w []byte, r []byte) error {
s.Configure()
for _, b := range w {
s.Transfer(b)
}
return nil
}
// delay represents a quarter of the clock cycle
func (s *bbSPI) delay() {
for i := uint32(0); i < s.Delay; {
i++
}
}
// Transfer is used to send a single byte.
func (s *bbSPI) Transfer(b byte) {
for i := uint8(0); i < 8; i++ {
// half clock cycle high to start
s.SCK.High()
s.delay()
// write the value to MOSI (MSB first)
if b&(1<<(7-i)) == 0 {
s.MOSI.Low()
} else {
s.MOSI.High()
}
s.delay()
// half clock cycle low
s.SCK.Low()
s.delay()
// for actual SPI would try to read the MISO value here
s.delay()
}
}
+1 -1
View File
@@ -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
View File
@@ -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 {
+4 -4
View File
@@ -1,5 +1,5 @@
// Package drivers provides a collection of hardware drivers for devices that
// can be used together with TinyGo (https://tinygo.org).
// Package drivers provides a collection of hardware drivers for TinyGo (https://tinygo.org)
// for devices such as sensors and displays.
//
// Here is an example in TinyGo that uses the BMP180 digital barometer:
//
@@ -9,7 +9,7 @@
// "time"
// "machine"
//
// "github.com/tinygo-org/drivers/bmp180"
// "tinygo.org/x/drivers/bmp180"
// )
//
// func main() {
@@ -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")
+101 -21
View File
@@ -1,4 +1,4 @@
// Simple driver to rotate a 4-wire stepper motor
// Package easystepper provides a simple driver to rotate a 4-wire stepper motor.
package easystepper // import "tinygo.org/x/drivers/easystepper"
import (
@@ -10,47 +10,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
View File
@@ -19,10 +19,13 @@
package espat // import "tinygo.org/x/drivers/espat"
import (
"errors"
"machine"
"strconv"
"strings"
"time"
"tinygo.org/x/drivers/net"
)
// Device wraps UART connection to the ESP8266/ESP32.
@@ -47,6 +50,7 @@ func New(b machine.UART) *Device {
// Configure sets up the device for communication.
func (d Device) Configure() {
ActiveDevice = &d
net.ActiveDevice = ActiveDevice
}
// Connected checks if there is communication with the ESP8266/ESP32.
@@ -54,11 +58,11 @@ func (d *Device) Connected() bool {
d.Execute(Test)
// handle response here, should include "OK"
r := d.Response(100)
if strings.Contains(string(r), "OK") {
return true
_, err := d.Response(100)
if err != nil {
return false
}
return false
return true
}
// Write raw bytes to the UART.
@@ -72,7 +76,7 @@ func (d *Device) Read(b []byte) (n int, err error) {
}
// how long in milliseconds to pause after sending AT commands
const pause = 100
const pause = 300
// Execute sends an AT command to the ESP8266/ESP32.
func (d Device) Execute(cmd string) error {
@@ -97,7 +101,11 @@ func (d Device) Set(cmd, params string) error {
// Version returns the ESP8266/ESP32 firmware version info.
func (d Device) Version() []byte {
d.Execute(Version)
return d.Response(100)
r, err := d.Response(100)
if err != nil {
return []byte("unknown")
}
return r
}
// Echo sets the ESP8266/ESP32 echo setting.
@@ -122,7 +130,7 @@ func (d Device) Reset() {
// ReadSocket returns the data that has already been read in from the responses.
func (d *Device) ReadSocket(b []byte) (n int, err error) {
// make sure no data in buffer
d.Response(100)
d.Response(300)
count := len(b)
if len(b) >= len(d.socketdata) {
@@ -142,118 +150,73 @@ func (d *Device) ReadSocket(b []byte) (n int, err error) {
// Response gets the next response bytes from the ESP8266/ESP32.
// The call will retry for up to timeout milliseconds before returning nothing.
func (d *Device) Response(timeout int) []byte {
var i int
pause := 10 // pause to wait for 10 ms
func (d *Device) Response(timeout int) ([]byte, error) {
// read data
var size int
var start, end int
pause := 100 // pause to wait for 100 ms
retries := timeout / pause
header := make([]byte, 2)
for {
for d.bus.Buffered() > 0 {
// get the first 2 bytes
header[0], _ = d.bus.ReadByte()
header[1], _ = d.bus.ReadByte()
size = d.bus.Buffered()
if d.isLeadingCRLF(header) {
// skip it
header[0], _ = d.bus.ReadByte()
header[1], _ = d.bus.ReadByte()
if size > 0 {
end += size
d.bus.Read(d.response[start:end])
// if "+IPD" then read socket data
if strings.Contains(string(d.response[:end]), "+IPD") {
// handle socket data
return nil, d.parseIPD(end)
}
if d.isIPD(header) {
// is socket data packet
d.parseIPD()
} else {
// no, so put into response
d.response[i] = header[0]
i++
d.response[i] = header[1]
i++
// if "OK" then the command worked
if strings.Contains(string(d.response[:end]), "OK") {
return d.response[start:end], nil
}
// read the rest of normal command response
for d.bus.Buffered() > 0 {
data, err := d.bus.ReadByte()
if err != nil {
return nil
}
d.response[i] = data
i++
// if "Error" then the command failed
if strings.Contains(string(d.response[:end]), "ERROR") {
return d.response[start:end], errors.New("response error:" + string(d.response[start:end]))
}
// if anything else, then keep reading data in?
start = end
}
// wait longer?
retries--
if retries == 0 {
break
return nil, errors.New("response timeout error:" + string(d.response[start:end]))
}
// pause to make sure is no more data to be read
time.Sleep(time.Duration(pause) * time.Millisecond)
}
return d.response[:i]
}
func (d *Device) isLeadingCRLF(b []byte) bool {
if len(b) < 2 {
return false
}
if b[0] == 13 && b[1] == 10 {
return true
}
return false
}
func (d *Device) parseIPD(end int) error {
// find the "+IPD," to get length
s := strings.Index(string(d.response[:end]), "+IPD,")
func (d *Device) isIPD(b []byte) bool {
if len(b) < 2 {
return false
}
if b[0] == '+' && b[1] == 'I' {
return true
}
return false
}
// find the ":"
e := strings.Index(string(d.response[:end]), ":")
func (d *Device) parseIPD() bool {
data, _ := d.bus.ReadByte()
if data != 'P' {
// error
return false
}
data, _ = d.bus.ReadByte()
if data != 'D' {
// error
return false
}
data, _ = d.bus.ReadByte()
if data != ',' {
// error
return false
}
// find the data length
val := string(d.response[s+5 : e])
// get the expected data length
// skip remaining header up to the ":"
buf := []byte{}
data, _ = d.bus.ReadByte()
for data != ':' {
// put into the buffer with int value here
buf = append(buf, data)
// read next value
data, _ = d.bus.ReadByte()
}
val := string(buf)
count, err := strconv.Atoi(val)
// TODO: verify count
_, err := strconv.Atoi(val)
if err != nil {
// not expected data here. what to do?
return false
return err
}
// load up the socket data
// only read the expected amount of data
for m := 0; m < count; m++ {
data, _ = d.bus.ReadByte()
d.socketdata = append(d.socketdata, data)
}
return true
d.socketdata = append(d.socketdata, d.response[e+1:end]...)
return nil
}
// IsSocketDataAvailable returns of there is socket data available
func (d *Device) IsSocketDataAvailable() bool {
return len(d.socketdata) > 0 || d.bus.Buffered() > 0
}
+48 -26
View File
@@ -17,7 +17,14 @@ const (
// GetDNS returns the IP address for a domain name.
func (d *Device) GetDNS(domain string) (string, error) {
d.Set(TCPDNSLookup, "\""+domain+"\"")
r := strings.Split(string(d.Response(1000)), ":")
resp, err := d.Response(1000)
if err != nil {
return "", err
}
if !strings.Contains(string(resp), ":") {
return "", errors.New("GetDNS error:" + string(resp))
}
r := strings.Split(string(resp), ":")
if len(r) != 2 {
return "", errors.New("Invalid domain lookup result")
}
@@ -30,24 +37,30 @@ func (d *Device) GetDNS(domain string) (string, error) {
func (d *Device) ConnectTCPSocket(addr, port string) error {
protocol := "TCP"
val := "\"" + protocol + "\",\"" + addr + "\"," + port + ",120"
d.Set(TCPConnect, val)
r := d.Response(1000)
if strings.Contains(string(r), "OK") {
return nil
err := d.Set(TCPConnect, val)
if err != nil {
return err
}
return errors.New("ConnectTCPSocket error:" + string(r))
_, e := d.Response(3000)
if e != nil {
return e
}
return nil
}
// ConnectUDPSocket creates a new UDP connection for the ESP8266/ESP32.
func (d *Device) ConnectUDPSocket(addr, sendport, listenport string) error {
protocol := "UDP"
val := "\"" + protocol + "\",\"" + addr + "\"," + sendport + "," + listenport + ",2"
d.Set(TCPConnect, val)
r := d.Response(pause)
if strings.Contains(string(r), "OK") {
return nil
err := d.Set(TCPConnect, val)
if err != nil {
return err
}
return errors.New("ConnectUDPSocket error:" + string(r))
_, e := d.Response(3000)
if e != nil {
return e
}
return nil
}
// ConnectSSLSocket creates a new SSL socket connection for the ESP8266/ESP32.
@@ -57,17 +70,23 @@ func (d *Device) ConnectSSLSocket(addr, port string) error {
val := "\"" + protocol + "\",\"" + addr + "\"," + port + ",120"
d.Set(TCPConnect, val)
// this operation takes longer, so wait up to 6 seconds to complete.
r := d.Response(6000)
if strings.Contains(string(r), "CONNECT") {
return nil
_, err := d.Response(6000)
if err != nil {
return err
}
return errors.New("ConnectSSLSocket error:" + string(r))
return nil
}
// DisconnectSocket disconnects the ESP8266/ESP32 from the current TCP/UDP connection.
func (d *Device) DisconnectSocket() error {
d.Execute(TCPClose)
d.Response(pause)
err := d.Execute(TCPClose)
if err != nil {
return err
}
_, e := d.Response(pause)
if e != nil {
return e
}
return nil
}
@@ -76,14 +95,14 @@ func (d *Device) DisconnectSocket() error {
func (d *Device) SetMux(mode int) error {
val := strconv.Itoa(mode)
d.Set(TCPMultiple, val)
d.Response(pause)
return nil
_, err := d.Response(pause)
return err
}
// GetMux returns the ESP8266/ESP32 current client TCP/UDP configuration for concurrent connections.
func (d *Device) GetMux() ([]byte, error) {
d.Query(TCPMultiple)
return d.Response(pause), nil
return d.Response(pause)
}
// SetTCPTransferMode sets the ESP8266/ESP32 current client TCP/UDP transfer mode.
@@ -91,12 +110,12 @@ func (d *Device) GetMux() ([]byte, error) {
func (d *Device) SetTCPTransferMode(mode int) error {
val := strconv.Itoa(mode)
d.Set(TransmissionMode, val)
d.Response(pause)
return nil
_, err := d.Response(pause)
return err
}
// GetTCPTransferMode returns the ESP8266/ESP32 current client TCP/UDP transfer mode.
func (d *Device) GetTCPTransferMode() []byte {
func (d *Device) GetTCPTransferMode() ([]byte, error) {
d.Query(TransmissionMode)
return d.Response(pause)
}
@@ -108,7 +127,10 @@ func (d *Device) StartSocketSend(size int) error {
// when ">" is received, it indicates
// ready to receive data
r := d.Response(pause)
r, err := d.Response(2000)
if err != nil {
return err
}
if strings.Contains(string(r), ">") {
return nil
}
@@ -120,6 +142,6 @@ func (d *Device) StartSocketSend(size int) error {
func (d *Device) EndSocketSend() error {
d.Write([]byte("+++"))
d.Response(pause)
return nil
_, err := d.Response(pause)
return err
}
+40 -30
View File
@@ -16,7 +16,7 @@ const (
)
// GetWifiMode returns the ESP8266/ESP32 wifi mode.
func (d *Device) GetWifiMode() []byte {
func (d *Device) GetWifiMode() ([]byte, error) {
d.Query(WifiMode)
return d.Response(100)
}
@@ -25,14 +25,14 @@ func (d *Device) GetWifiMode() []byte {
func (d *Device) SetWifiMode(mode int) error {
val := strconv.Itoa(mode)
d.Set(WifiMode, val)
d.Response(pause)
return nil
_, err := d.Response(pause)
return err
}
// Wifi Client
// GetConnectedAP returns the ESP8266/ESP32 is currently connected to as a client.
func (d *Device) GetConnectedAP() []byte {
func (d *Device) GetConnectedAP() ([]byte, error) {
d.Query(ConnectAP)
return d.Response(100)
}
@@ -42,37 +42,43 @@ func (d *Device) GetConnectedAP() []byte {
func (d *Device) ConnectToAP(ssid, pwd string, ws int) error {
val := "\"" + ssid + "\",\"" + pwd + "\""
d.Set(ConnectAP, val)
d.Response(ws * 1000)
_, err := d.Response(ws * 1000)
if err != nil {
return err
}
return nil
}
// DisconnectFromAP disconnects the ESP8266/ESP32 from the current access point.
func (d *Device) DisconnectFromAP() error {
d.Execute(Disconnect)
d.Response(1000)
return nil
_, err := d.Response(1000)
return err
}
// GetClientIP returns the ESP8266/ESP32 current client IP addess when connected to an Access Point.
func (d *Device) GetClientIP() string {
func (d *Device) GetClientIP() (string, error) {
d.Query(SetStationIP)
return string(d.Response(100))
r, err := d.Response(1000)
return string(r), err
}
// SetClientIP sets the ESP8266/ESP32 current client IP addess when connected to an Access Point.
func (d *Device) SetClientIP(ipaddr string) []byte {
func (d *Device) SetClientIP(ipaddr string) error {
val := "\"" + ipaddr + "\""
d.Set(ConnectAP, val)
d.Response(500)
return nil
_, err := d.Response(500)
return err
}
// Access Point
// GetAPConfig returns the ESP8266/ESP32 current configuration when acting as an Access Point.
func (d *Device) GetAPConfig() string {
func (d *Device) GetAPConfig() (string, error) {
d.Query(SoftAPConfigCurrent)
return string(d.Response(100))
r, err := d.Response(100)
return string(r), err
}
// SetAPConfig sets the ESP8266/ESP32 current configuration when acting as an Access Point.
@@ -83,35 +89,38 @@ func (d *Device) SetAPConfig(ssid, pwd string, ch, security int) error {
ecnval := strconv.Itoa(security)
val := "\"" + ssid + "\",\"" + pwd + "\"," + chval + "," + ecnval
d.Set(SoftAPConfigCurrent, val)
d.Response(1000)
return nil
_, err := d.Response(1000)
return err
}
// GetAPClients returns the ESP8266/ESP32 current clients when acting as an Access Point.
func (d *Device) GetAPClients() string {
func (d *Device) GetAPClients() (string, error) {
d.Query(ListConnectedIP)
return string(d.Response(100))
r, err := d.Response(100)
return string(r), err
}
// GetAPIP returns the ESP8266/ESP32 current IP addess when configured as an Access Point.
func (d *Device) GetAPIP() string {
func (d *Device) GetAPIP() (string, error) {
d.Query(SetSoftAPIPCurrent)
return string(d.Response(100))
r, err := d.Response(100)
return string(r), err
}
// SetAPIP sets the ESP8266/ESP32 current IP addess when configured as an Access Point.
func (d *Device) SetAPIP(ipaddr string) error {
val := "\"" + ipaddr + "\""
d.Set(SetSoftAPIPCurrent, val)
d.Response(500)
return nil
_, err := d.Response(500)
return err
}
// GetAPConfigFlash returns the ESP8266/ESP32 current configuration acting as an Access Point
// from flash storage. These settings are those used after a reset.
func (d *Device) GetAPConfigFlash() string {
func (d *Device) GetAPConfigFlash() (string, error) {
d.Query(SoftAPConfigFlash)
return string(d.Response(100))
r, err := d.Response(100)
return string(r), err
}
// SetAPConfigFlash sets the ESP8266/ESP32 current configuration acting as an Access Point,
@@ -123,15 +132,16 @@ func (d *Device) SetAPConfigFlash(ssid, pwd string, ch, security int) error {
ecnval := strconv.Itoa(security)
val := "\"" + ssid + "\",\"" + pwd + "\"," + chval + "," + ecnval
d.Set(SoftAPConfigFlash, val)
d.Response(1000)
return nil
_, err := d.Response(1000)
return err
}
// GetAPIPFlash returns the ESP8266/ESP32 IP address as saved to flash storage.
// This is the IP address that will be used after a reset.
func (d *Device) GetAPIPFlash() string {
func (d *Device) GetAPIPFlash() (string, error) {
d.Query(SetSoftAPIPFlash)
return string(d.Response(100))
r, err := d.Response(100)
return string(r), err
}
// SetAPIPFlash sets the ESP8266/ESP32 current IP addess when configured as an Access Point.
@@ -139,6 +149,6 @@ func (d *Device) GetAPIPFlash() string {
func (d *Device) SetAPIPFlash(ipaddr string) error {
val := "\"" + ipaddr + "\""
d.Set(SetSoftAPIPFlash, val)
d.Response(500)
return nil
_, err := d.Response(500)
return err
}
+27
View File
@@ -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
+56
View File
@@ -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])
}
}
}
}
+84
View File
@@ -0,0 +1,84 @@
// This example demostrates how to control the "Dotstar" (APA102) LED included
// on the Adafruit Itsy Bitsy M0 board. It implements a "rainbow effect" based
// on the following example:
// https://github.com/adafruit/Adafruit_Learning_System_Guides/blob/master/CircuitPython_Essentials/CircuitPython_Internal_RGB_LED_rainbow.py
package main
import (
"image/color"
"machine"
"time"
"tinygo.org/x/drivers/apa102"
)
var (
apa apa102.Device
led = machine.PWM{machine.LED}
leds = make([]color.RGBA, 1)
wheel = &Wheel{Brightness: 0x10}
)
func init() {
// APA102 on Itsy Bitsy is connected to pins that require a software-based
// SPI implementation.
apa = apa102.NewSoftwareSPI(machine.PA00, machine.PA01, 1)
// Configure the regular on-board LED for PWM fading
machine.InitPWM()
led.Configure()
}
func main() {
// We'll fade the on-board LED in a goroutine to show/ensure that the APA102
// works fine with the scheduler enabled. Comment this out to test this code
// with the scheduler disabled.
go func() {
for i, brightening := uint8(0), false; ; i++ {
if i == 0 {
brightening = !brightening
continue
}
var brightness uint16 = uint16(i) << 8
if !brightening {
brightness = 0xFFFF - brightness
}
led.Set(brightness)
time.Sleep(5 * time.Millisecond)
}
}()
// Use the "wheel" function from Adafruit's example to cycle the APA102
for {
leds[0] = wheel.Next()
apa.WriteColors(leds)
time.Sleep(25 * time.Millisecond)
}
}
// Wheel is a port of Adafruit's Circuit Python example referenced above.
type Wheel struct {
Brightness uint8
pos uint8
}
// Next increments the internal state of the color and returns the new RGBA
func (w *Wheel) Next() (c color.RGBA) {
pos := w.pos
if w.pos < 85 {
c = color.RGBA{R: 0xFF - pos*3, G: pos * 3, B: 0x0, A: w.Brightness}
} else if w.pos < 170 {
pos -= 85
c = color.RGBA{R: 0x0, G: 0xFF - pos*3, B: pos * 3, A: w.Brightness}
} else {
pos -= 170
c = color.RGBA{R: pos * 3, G: 0x0, B: 0xFF - pos*3, A: w.Brightness}
}
w.pos++
return
}
+1 -1
View File
@@ -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()
+1 -1
View File
@@ -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")
+1 -1
View File
@@ -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)
}
+1
View File
@@ -9,6 +9,7 @@ import (
func main() {
motor := easystepper.New(machine.P13, machine.P15, machine.P14, machine.P16, 200, 75)
motor.Configure()
for {
println("CLOCKWISE")
+47 -31
View File
@@ -23,11 +23,12 @@ const actAsAP = false
const ssid = "YOURSSID"
const pass = "YOURPASS"
// these are the default pins for the Arduino Nano33 IoT.
// change these to connect to a different UART or pins for the ESP8266/ESP32
var (
uart = machine.UART1
tx = machine.D10
rx = machine.D11
tx = machine.PA22
rx = machine.PA23
console = machine.UART0
@@ -42,28 +43,20 @@ func main() {
adaptor.Configure()
// first check if connected
if adaptor.Connected() {
if connectToESP() {
println("Connected to wifi adaptor.")
adaptor.Echo(false)
console.Write([]byte("\r\n"))
console.Write([]byte("ESP-AT console enabled.\r\n"))
console.Write([]byte("Firmware version:\r\n"))
console.Write(adaptor.Version())
console.Write([]byte("\r\n"))
if actAsAP {
provideAP()
} else {
connectToAP()
}
console.Write([]byte("Type an AT command then press enter:\r\n"))
prompt()
connectToAP()
} else {
console.Write([]byte("\r\n"))
console.Write([]byte("Unable to connect to wifi adaptor.\r\n"))
println("")
failMessage("Unable to connect to wifi adaptor.")
return
}
println("Type an AT command then press enter:")
prompt()
input := make([]byte, 64)
i := 0
for {
@@ -81,7 +74,8 @@ func main() {
adaptor.Write(input[:i+2])
// display response
console.Write(adaptor.Response(100))
r, _ := adaptor.Response(500)
console.Write(r)
// prompt
prompt()
@@ -100,28 +94,50 @@ func main() {
}
func prompt() {
console.Write([]byte("ESPAT>"))
print("ESPAT>")
}
// connect to ESP8266/ESP32
func connectToESP() bool {
for i := 0; i < 5; i++ {
println("Connecting to wifi adaptor...")
if adaptor.Connected() {
return true
}
time.Sleep(1 * time.Second)
}
return false
}
// connect to access point
func connectToAP() {
console.Write([]byte("Connecting to wifi network...\r\n"))
println("Connecting to wifi network '" + ssid + "'")
adaptor.SetWifiMode(espat.WifiModeClient)
adaptor.ConnectToAP(ssid, pass, 10)
console.Write([]byte("Connected.\r\n"))
console.Write([]byte(adaptor.GetClientIP()))
console.Write([]byte("\r\n"))
println("Connected.")
ip, err := adaptor.GetClientIP()
if err != nil {
failMessage(err.Error())
}
println(ip)
}
// provide access point
func provideAP() {
time.Sleep(500 * time.Millisecond)
console.Write([]byte("Starting wifi network as access point '"))
console.Write([]byte(ssid))
console.Write([]byte("'...\r\n"))
println("Starting wifi network as access point '" + ssid + "'...")
adaptor.SetWifiMode(espat.WifiModeAP)
adaptor.SetAPConfig(ssid, pass, 7, espat.WifiAPSecurityWPA2_PSK)
console.Write([]byte("Ready.\r\n"))
console.Write([]byte(adaptor.GetAPIP()))
console.Write([]byte("\r\n"))
println("Ready.")
ip, _ := adaptor.GetAPIP()
println(ip)
}
func failMessage(msg string) {
for {
println(msg)
time.Sleep(1 * time.Second)
}
}
+39 -15
View File
@@ -11,7 +11,7 @@ import (
"time"
"tinygo.org/x/drivers/espat"
"tinygo.org/x/drivers/espat/net"
"tinygo.org/x/drivers/net"
)
// change actAsAP to true to act as an access point instead of connecting to one.
@@ -21,11 +21,12 @@ const actAsAP = false
const ssid = "YOURSSID"
const pass = "YOURPASS"
// these are the default pins for the Arduino Nano33 IoT.
// change these to connect to a different UART or pins for the ESP8266/ESP32
var (
uart = machine.UART1
tx = machine.D10
rx = machine.D11
tx = machine.PA22
rx = machine.PA23
adaptor *espat.Device
)
@@ -42,17 +43,14 @@ func main() {
readyled.High()
// first check if connected
if adaptor.Connected() {
if connectToESP() {
println("Connected to wifi adaptor.")
adaptor.Echo(false)
if actAsAP {
provideAP()
} else {
connectToAP()
}
connectToAP()
} else {
println("Unable to connect to wifi adaptor.")
println("")
failMessage("Unable to connect to wifi adaptor.")
return
}
@@ -85,21 +83,47 @@ func main() {
println("Done.")
}
// connect to ESP8266/ESP32
func connectToESP() bool {
for i := 0; i < 5; i++ {
println("Connecting to wifi adaptor...")
if adaptor.Connected() {
return true
}
time.Sleep(1 * time.Second)
}
return false
}
// connect to access point
func connectToAP() {
println("Connecting to wifi network...")
println("Connecting to wifi network '" + ssid + "'")
adaptor.SetWifiMode(espat.WifiModeClient)
adaptor.ConnectToAP(ssid, pass, 10)
println("Connected.")
println(adaptor.GetClientIP())
ip, err := adaptor.GetClientIP()
if err != nil {
failMessage(err.Error())
}
println(ip)
}
// provide access point
func provideAP() {
println("Starting wifi network as access point:")
println(ssid)
println("Starting wifi network as access point '" + ssid + "'...")
adaptor.SetWifiMode(espat.WifiModeAP)
adaptor.SetAPConfig(ssid, pass, 7, espat.WifiAPSecurityWPA2_PSK)
println("Ready.")
println(adaptor.GetAPIP())
ip, _ := adaptor.GetAPIP()
println(ip)
}
func failMessage(msg string) {
for {
println(msg)
time.Sleep(1 * time.Second)
}
}
+35 -7
View File
@@ -11,7 +11,7 @@ import (
"time"
"tinygo.org/x/drivers/espat"
"tinygo.org/x/drivers/espat/net"
"tinygo.org/x/drivers/net"
)
// access point info
@@ -21,11 +21,12 @@ const pass = "YOURPASS"
// IP address of the listener aka "hub". Replace with your own info.
const hubIP = "0.0.0.0"
// these are the default pins for the Arduino Nano33 IoT.
// change these to connect to a different UART or pins for the ESP8266/ESP32
var (
uart = machine.UART1
tx = machine.D10
rx = machine.D11
tx = machine.PA22
rx = machine.PA23
adaptor *espat.Device
)
@@ -38,13 +39,14 @@ func main() {
adaptor.Configure()
// first check if connected
if adaptor.Connected() {
if connectToESP() {
println("Connected to wifi adaptor.")
adaptor.Echo(false)
connectToAP()
} else {
println("Unable to connect to wifi adaptor.")
println("")
failMessage("Unable to connect to wifi adaptor.")
return
}
@@ -70,11 +72,37 @@ func main() {
println("Done.")
}
// connect to ESP8266/ESP32
func connectToESP() bool {
for i := 0; i < 5; i++ {
println("Connecting to wifi adaptor...")
if adaptor.Connected() {
return true
}
time.Sleep(1 * time.Second)
}
return false
}
// connect to access point
func connectToAP() {
println("Connecting to wifi network...")
println("Connecting to wifi network '" + ssid + "'")
adaptor.SetWifiMode(espat.WifiModeClient)
adaptor.ConnectToAP(ssid, pass, 10)
println("Connected.")
println(adaptor.GetClientIP())
ip, err := adaptor.GetClientIP()
if err != nil {
failMessage(err.Error())
}
println(ip)
}
func failMessage(msg string) {
for {
println(msg)
time.Sleep(1 * time.Second)
}
}
+17 -10
View File
@@ -17,7 +17,7 @@ import (
"time"
"tinygo.org/x/drivers/espat"
"tinygo.org/x/drivers/espat/mqtt"
"tinygo.org/x/drivers/net/mqtt"
)
// access point info
@@ -25,14 +25,16 @@ const ssid = "YOURSSID"
const pass = "YOURPASS"
// IP address of the MQTT broker to use. Replace with your own info.
//const server = "tcp://test.mosquitto.org:1883"
const server = "ssl://test.mosquitto.org:8883"
const server = "tcp://test.mosquitto.org:1883"
//const server = "ssl://test.mosquitto.org:8883"
// these are the default pins for the Arduino Nano33 IoT.
// change these to connect to a different UART or pins for the ESP8266/ESP32
var (
uart = machine.UART1
tx = machine.D10
rx = machine.D11
uart = machine.UART2
tx = machine.PA22
rx = machine.PA23
console = machine.UART0
@@ -62,10 +64,10 @@ func main() {
return
}
opts := mqtt.NewClientOptions(adaptor)
opts := mqtt.NewClientOptions()
opts.AddBroker(server).SetClientID("tinygo-client-" + randomString(10))
println("Connectng to MQTT...")
println("Connecting to MQTT broker at", server)
cl := mqtt.NewClient(opts)
if token := cl.Connect(); token.Wait() && token.Error() != nil {
failMessage(token.Error().Error())
@@ -104,13 +106,18 @@ func connectToESP() bool {
// connect to access point
func connectToAP() {
println("Connecting to wifi network...")
println("Connecting to wifi network '" + ssid + "'")
adaptor.SetWifiMode(espat.WifiModeClient)
adaptor.ConnectToAP(ssid, pass, 10)
println("Connected.")
println(adaptor.GetClientIP())
ip, err := adaptor.GetClientIP()
if err != nil {
failMessage(err.Error())
}
println(ip)
}
// Returns an int >= min, < max
+163
View File
@@ -0,0 +1,163 @@
// This is a sensor station that uses a ESP8266 or ESP32 running on the device UART1.
// It creates an MQTT connection that publishes a message every second
// to an MQTT broker.
//
// In other words:
// Your computer <--> UART0 <--> MCU <--> UART1 <--> ESP8266 <--> Internet <--> MQTT broker.
//
// You must also install the Paho MQTT package to build this program:
//
// go get -u github.com/eclipse/paho.mqtt.golang
//
package main
import (
"fmt"
"machine"
"math/rand"
"time"
"tinygo.org/x/drivers/espat"
"tinygo.org/x/drivers/net/mqtt"
)
// access point info
const ssid = "YOURSSID"
const pass = "YOURPASS"
// IP address of the MQTT broker to use. Replace with your own info.
//const server = "tcp://test.mosquitto.org:1883"
const server = "ssl://test.mosquitto.org:8883"
// change these to connect to a different UART or pins for the ESP8266/ESP32
var (
// these are defaults for the Arduino Nano33 IoT.
uart = machine.UART1
tx = machine.PA22
rx = machine.PA23
console = machine.UART0
adaptor *espat.Device
cl mqtt.Client
topicTx = "tinygo/tx"
topicRx = "tinygo/rx"
)
func subHandler(client mqtt.Client, msg mqtt.Message) {
fmt.Printf("[%s] ", msg.Topic())
fmt.Printf("%s\r\n", msg.Payload())
}
func main() {
time.Sleep(3000 * time.Millisecond)
uart.Configure(machine.UARTConfig{TX: tx, RX: rx})
rand.Seed(time.Now().UnixNano())
// Init esp8266/esp32
adaptor = espat.New(uart)
adaptor.Configure()
// first check if connected
if connectToESP() {
println("Connected to wifi adaptor.")
adaptor.Echo(false)
connectToAP()
} else {
println("")
failMessage("Unable to connect to wifi adaptor.")
return
}
opts := mqtt.NewClientOptions()
opts.AddBroker(server).SetClientID("tinygo-client-" + randomString(10))
println("Connecting to MQTT broker at", server)
cl = mqtt.NewClient(opts)
if token := cl.Connect(); token.Wait() && token.Error() != nil {
failMessage(token.Error().Error())
}
// subscribe
token := cl.Subscribe(topicRx, 0, subHandler)
token.Wait()
if token.Error() != nil {
failMessage(token.Error().Error())
}
go publishing()
select {}
// Right now this code is never reached. Need a way to trigger it...
println("Disconnecting MQTT...")
cl.Disconnect(100)
println("Done.")
}
func publishing() {
for {
println("Publishing MQTT message...")
data := []byte("{\"e\":[{ \"n\":\"hello\", \"v\":101 }]}")
token := cl.Publish(topicTx, 0, false, data)
token.Wait()
if token.Error() != nil {
println(token.Error().Error())
}
time.Sleep(1000 * time.Millisecond)
}
}
// connect to ESP8266/ESP32
func connectToESP() bool {
for i := 0; i < 5; i++ {
println("Connecting to wifi adaptor...")
if adaptor.Connected() {
return true
}
time.Sleep(1 * time.Second)
}
return false
}
// connect to access point
func connectToAP() {
println("Connecting to wifi network '" + ssid + "'")
adaptor.SetWifiMode(espat.WifiModeClient)
adaptor.ConnectToAP(ssid, pass, 10)
println("Connected.")
ip, err := adaptor.GetClientIP()
if err != nil {
failMessage(err.Error())
}
println(ip)
}
// Returns an int >= min, < max
func randomInt(min, max int) int {
return min + rand.Intn(max-min)
}
// Generate a random string of A-Z chars with len = l
func randomString(len int) string {
bytes := make([]byte, len)
for i := 0; i < len; i++ {
bytes[i] = byte(randomInt(65, 90))
}
return string(bytes)
}
func failMessage(msg string) {
for {
println(msg)
time.Sleep(1 * time.Second)
}
}
+37 -6
View File
@@ -11,7 +11,7 @@ import (
"time"
"tinygo.org/x/drivers/espat"
"tinygo.org/x/drivers/espat/net"
"tinygo.org/x/drivers/net"
)
// access point info
@@ -21,6 +21,7 @@ const pass = "YOURPASS"
// IP address of the server aka "hub". Replace with your own info.
const serverIP = "0.0.0.0"
// these are the default pins for the Arduino Nano33 IoT.
// change these to connect to a different UART or pins for the ESP8266/ESP32
var (
uart = machine.UART1
@@ -38,13 +39,14 @@ func main() {
adaptor.Configure()
// first check if connected
if adaptor.Connected() {
if connectToESP() {
println("Connected to wifi adaptor.")
adaptor.Echo(false)
connectToAP()
} else {
println("Unable to connect to wifi adaptor.")
println("")
failMessage("Unable to connect to wifi adaptor.")
return
}
@@ -54,7 +56,10 @@ func main() {
laddr := &net.TCPAddr{Port: 8080}
println("Dialing TCP connection...")
conn, _ := net.DialTCP("tcp", laddr, raddr)
conn, err := net.DialTCP("tcp", laddr, raddr)
if err != nil {
failMessage(err.Error())
}
for {
// send data
@@ -70,11 +75,37 @@ func main() {
println("Done.")
}
// connect to ESP8266/ESP32
func connectToESP() bool {
for i := 0; i < 5; i++ {
println("Connecting to wifi adaptor...")
if adaptor.Connected() {
return true
}
time.Sleep(1 * time.Second)
}
return false
}
// connect to access point
func connectToAP() {
println("Connecting to wifi network...")
println("Connecting to wifi network '" + ssid + "'")
adaptor.SetWifiMode(espat.WifiModeClient)
adaptor.ConnectToAP(ssid, pass, 10)
println("Connected.")
println(adaptor.GetClientIP())
ip, err := adaptor.GetClientIP()
if err != nil {
failMessage(err.Error())
}
println(ip)
}
func failMessage(msg string) {
for {
println(msg)
time.Sleep(1 * time.Second)
}
}
+259
View File
@@ -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("==> ")
}
+21
View File
@@ -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,
),
)
}
+20
View File
@@ -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,
),
)
}
+20
View File
@@ -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)
}
}
+47
View File
@@ -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
+234
View File
@@ -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)
}
}
+49
View File
@@ -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)
}
}
+34
View File
@@ -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()
}
+45
View File
@@ -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()
}
+34
View File
@@ -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()
}
+45
View File
@@ -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()
}
+1 -1
View File
@@ -11,7 +11,7 @@ import (
var i2c = machine.I2C1
func main() {
i2c.Configure(machine.I2CConfig{})
i2c.Configure(machine.I2CConfig{SCL: machine.SCL1_PIN, SDA: machine.SDA1_PIN})
accel := lis3dh.New(i2c)
accel.Address = lis3dh.Address1 // address on the Circuit Playground Express
+30
View File
@@ -0,0 +1,30 @@
// Connects to an LSM6DS3 I2C a 6 axis Inertial Measurement Unit (IMU)
package main
import (
"machine"
"time"
"tinygo.org/x/drivers/lsm6ds3"
)
func main() {
machine.I2C0.Configure(machine.I2CConfig{})
accel := lsm6ds3.New(machine.I2C0)
accel.Configure(lsm6ds3.Configuration{})
if !accel.Connected() {
println("LSM6DS3 not connected")
return
}
for {
x, y, z := accel.ReadAcceleration()
println("Acceleration:", float32(x)/1000000, float32(y)/1000000, float32(z)/1000000)
x, y, z = accel.ReadRotation()
println("Gyroscope:", float32(x)/1000000, float32(y)/1000000, float32(z)/1000000)
x, _ = accel.ReadTemperature()
println("Degrees C", float32(x)/1000, "\n\n")
time.Sleep(time.Millisecond * 1000)
}
}
+1 -1
View File
@@ -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)
}
+32
View File
@@ -0,0 +1,32 @@
// Connects to a MCP3008 ADC via SPI.
package main
import (
"machine"
"time"
"tinygo.org/x/drivers/mcp3008"
)
var (
spi = machine.SPI0
csPin = machine.D12
)
func main() {
spi.Configure(machine.SPIConfig{
Frequency: 4000000,
Mode: 3})
adc := mcp3008.New(spi, csPin)
adc.Configure()
// get "CH0" aka "machine.ADC" interface to channel 0 from ADC.
p := adc.CH0
for {
val := p.Get()
println(val)
time.Sleep(50 * time.Millisecond)
}
}
+23
View File
@@ -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)
}
}
+44
View File
@@ -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)
}
}
+124
View File
@@ -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,
}
+1 -1
View File
@@ -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)
}
+1 -1
View File
@@ -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)
}
+28
View File
@@ -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)
}
}
+80
View File
@@ -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)
}
}
+41
View File
@@ -0,0 +1,41 @@
package main
import (
"time"
"machine"
"tinygo.org/x/drivers/veml6070"
)
func main() {
machine.I2C0.Configure(machine.I2CConfig{})
sensor := veml6070.New(machine.I2C0)
if !sensor.Configure() {
println("VEML6070 could not be configured")
return
}
println("VEML6070 configured")
for {
intensity, _ := sensor.ReadUVALightIntensity()
println("UVA light intensity:", float32(intensity)/1000.0, "W/(m*m)")
switch sensor.GetEstimatedRiskLevel(intensity) {
case veml6070.UVI_RISK_LOW:
println("UV risk level: low")
case veml6070.UVI_RISK_MODERATE:
println("UV risk level: moderate")
case veml6070.UVI_RISK_HIGH:
println("UV risk level: high")
case veml6070.UVI_RISK_VERY_HIGH:
println("UV risk level: very high")
case veml6070.UVI_RISK_EXTREME:
println("UV risk level: extreme")
}
time.Sleep(2 * time.Second)
}
}
+149
View File
@@ -0,0 +1,149 @@
// This is a sensor station that uses a ESP8266 or ESP32 running on the device UART1.
// It creates an MQTT connection that publishes a message every second
// to an MQTT broker.
//
// In other words:
// Your computer <--> UART0 <--> MCU <--> UART1 <--> ESP8266 <--> Internet <--> MQTT broker.
//
// You must install the Paho MQTT package to build this program:
//
// go get -u github.com/eclipse/paho.mqtt.golang
//
package main
import (
"fmt"
"machine"
"math/rand"
"time"
"tinygo.org/x/drivers/net/mqtt"
"tinygo.org/x/drivers/wifinina"
)
// access point info
const ssid = ""
const pass = ""
// IP address of the MQTT broker to use. Replace with your own info.
const server = "tcp://test.mosquitto.org:1883"
//const server = "ssl://test.mosquitto.org:8883"
// these are the default pins for the Arduino Nano33 IoT.
// change these to connect to a different UART or pins for the ESP8266/ESP32
var (
// these are the default pins for the Arduino Nano33 IoT.
uart = machine.UART2
tx = machine.NINA_TX
rx = machine.NINA_RX
spi = machine.NINA_SPI
// this is the ESP chip that has the WIFININA firmware flashed on it
adaptor = &wifinina.Device{
SPI: spi,
CS: machine.NINA_CS,
ACK: machine.NINA_ACK,
GPIO0: machine.NINA_GPIO0,
RESET: machine.NINA_RESETN,
}
console = machine.UART0
topic = "tinygo"
)
func main() {
time.Sleep(3000 * time.Millisecond)
uart.Configure(machine.UARTConfig{TX: tx, RX: rx})
rand.Seed(time.Now().UnixNano())
// Configure SPI for 8Mhz, Mode 0, MSB First
spi.Configure(machine.SPIConfig{
Frequency: 8 * 1e6,
MOSI: machine.NINA_MOSI,
MISO: machine.NINA_MISO,
SCK: machine.NINA_SCK,
})
// Init esp8266/esp32
adaptor.Configure()
connectToAP()
opts := mqtt.NewClientOptions()
opts.AddBroker(server).SetClientID("tinygo-client-" + randomString(10))
println("Connectng to MQTT...")
cl := mqtt.NewClient(opts)
if token := cl.Connect(); token.Wait() && token.Error() != nil {
failMessage(token.Error().Error())
}
for i := 0; ; i++ {
println("Publishing MQTT message...")
data := []byte(fmt.Sprintf(`{"e":[{"n":"hello %d","v":101}]}`, i))
token := cl.Publish(topic, 0, false, data)
token.Wait()
if err := token.Error(); err != nil {
switch t := err.(type) {
case wifinina.Error:
println(t.Error(), "attempting to reconnect")
if token := cl.Connect(); token.Wait() && token.Error() != nil {
failMessage(token.Error().Error())
}
default:
println(err.Error())
}
}
time.Sleep(1 * time.Millisecond)
}
// Right now this code is never reached. Need a way to trigger it...
println("Disconnecting MQTT...")
cl.Disconnect(100)
println("Done.")
}
// connect to access point
func connectToAP() {
time.Sleep(2 * time.Second)
println("Connecting to " + ssid)
adaptor.SetPassphrase(ssid, pass)
for st, _ := adaptor.GetConnectionStatus(); st != wifinina.StatusConnected; {
println("Connection status: " + st.String())
time.Sleep(1 * time.Second)
st, _ = adaptor.GetConnectionStatus()
}
println("Connected.")
time.Sleep(2 * time.Second)
ip, _, _, err := adaptor.GetIP()
for ; err != nil; ip, _, _, err = adaptor.GetIP() {
println(err.Error())
time.Sleep(1 * time.Second)
}
println(ip.String())
}
// Returns an int >= min, < max
func randomInt(min, max int) int {
return min + rand.Intn(max-min)
}
// Generate a random string of A-Z chars with len = l
func randomString(len int) string {
bytes := make([]byte, len)
for i := 0; i < len; i++ {
bytes[i] = byte(randomInt(65, 90))
}
return string(bytes)
}
func failMessage(msg string) {
for {
println(msg)
time.Sleep(1 * time.Second)
}
}
+162
View File
@@ -0,0 +1,162 @@
// This is a sensor station that uses a ESP8266 or ESP32 running on the device UART1.
// It creates an MQTT connection that publishes a message every second
// to an MQTT broker.
//
// In other words:
// Your computer <--> UART0 <--> MCU <--> UART1 <--> ESP8266 <--> Internet <--> MQTT broker.
//
// You must also install the Paho MQTT package to build this program:
//
// go get -u github.com/eclipse/paho.mqtt.golang
//
package main
import (
"fmt"
"machine"
"math/rand"
"time"
"tinygo.org/x/drivers/net/mqtt"
"tinygo.org/x/drivers/wifinina"
)
// access point info
const ssid = ""
const pass = ""
// IP address of the MQTT broker to use. Replace with your own info.
const server = "tcp://test.mosquitto.org:1883"
//const server = "ssl://test.mosquitto.org:8883"
// change these to connect to a different UART or pins for the ESP8266/ESP32
var (
// these are the default pins for the Arduino Nano33 IoT.
uart = machine.UART2
tx = machine.NINA_TX
rx = machine.NINA_RX
spi = machine.NINA_SPI
// this is the ESP chip that has the WIFININA firmware flashed on it
adaptor = &wifinina.Device{
SPI: spi,
CS: machine.NINA_CS,
ACK: machine.NINA_ACK,
GPIO0: machine.NINA_GPIO0,
RESET: machine.NINA_RESETN,
}
console = machine.UART0
cl mqtt.Client
topicTx = "tinygo/tx"
topicRx = "tinygo/rx"
)
func subHandler(client mqtt.Client, msg mqtt.Message) {
fmt.Printf("[%s] ", msg.Topic())
fmt.Printf("%s\r\n", msg.Payload())
}
func main() {
time.Sleep(3000 * time.Millisecond)
uart.Configure(machine.UARTConfig{TX: tx, RX: rx})
rand.Seed(time.Now().UnixNano())
// Configure SPI for 8Mhz, Mode 0, MSB First
spi.Configure(machine.SPIConfig{
Frequency: 8 * 1e6,
MOSI: machine.NINA_MOSI,
MISO: machine.NINA_MISO,
SCK: machine.NINA_SCK,
})
// Init esp8266/esp32
adaptor.Configure()
connectToAP()
opts := mqtt.NewClientOptions()
opts.AddBroker(server).SetClientID("tinygo-client-" + randomString(10))
println("Connecting to MQTT broker at", server)
cl = mqtt.NewClient(opts)
if token := cl.Connect(); token.Wait() && token.Error() != nil {
failMessage(token.Error().Error())
}
// subscribe
token := cl.Subscribe(topicRx, 0, subHandler)
token.Wait()
if token.Error() != nil {
failMessage(token.Error().Error())
}
go publishing()
select {}
// Right now this code is never reached. Need a way to trigger it...
println("Disconnecting MQTT...")
cl.Disconnect(100)
println("Done.")
}
func publishing() {
for i := 0; ; i++ {
println("Publishing MQTT message...")
data := []byte(fmt.Sprintf(`{"e":[{"n":"hello %d","v":101}]}`, i))
token := cl.Publish(topicRx, 0, false, data)
token.Wait()
if token.Error() != nil {
println(token.Error().Error())
}
time.Sleep(100 * time.Millisecond)
}
}
// connect to access point
func connectToAP() {
time.Sleep(2 * time.Second)
println("Connecting to " + ssid)
adaptor.SetPassphrase(ssid, pass)
for st, _ := adaptor.GetConnectionStatus(); st != wifinina.StatusConnected; {
println("Connection status: " + st.String())
time.Sleep(1 * time.Second)
st, _ = adaptor.GetConnectionStatus()
}
println("Connected.")
time.Sleep(2 * time.Second)
ip, _, _, err := adaptor.GetIP()
for ; err != nil; ip, _, _, err = adaptor.GetIP() {
println(err.Error())
time.Sleep(1 * time.Second)
}
println(ip.String())
}
// Returns an int >= min, < max
func randomInt(min, max int) int {
return min + rand.Intn(max-min)
}
// Generate a random string of A-Z chars with len = l
func randomString(len int) string {
bytes := make([]byte, len)
for i := 0; i < len; i++ {
bytes[i] = byte(randomInt(65, 90))
}
return string(bytes)
}
func failMessage(msg string) {
for {
println(msg)
time.Sleep(1 * time.Second)
}
}
+143
View File
@@ -0,0 +1,143 @@
// This example opens a TCP connection using a device with WiFiNINA firmware
// and sends some data, for the purpose of testing speed and connectivity.
//
// You can open a server to accept connections from this program using:
//
// nc -w 5 -lk 8080
//
package main
import (
"bytes"
"fmt"
"machine"
"time"
"tinygo.org/x/drivers/net"
"tinygo.org/x/drivers/wifinina"
)
// access point info
const ssid = ""
const pass = ""
// IP address of the server aka "hub". Replace with your own info.
const serverIP = ""
// these are the default pins for the Arduino Nano33 IoT.
// change these to connect to a different UART or pins for the ESP8266/ESP32
var (
// these are the default pins for the Arduino Nano33 IoT.
uart = machine.UART2
tx = machine.NINA_TX
rx = machine.NINA_RX
spi = machine.NINA_SPI
// this is the ESP chip that has the WIFININA firmware flashed on it
adaptor = &wifinina.Device{
SPI: spi,
CS: machine.NINA_CS,
ACK: machine.NINA_ACK,
GPIO0: machine.NINA_GPIO0,
RESET: machine.NINA_RESETN,
}
console = machine.UART0
)
var buf = &bytes.Buffer{}
func main() {
uart.Configure(machine.UARTConfig{TX: tx, RX: rx})
// Configure SPI for 8Mhz, Mode 0, MSB First
spi.Configure(machine.SPIConfig{
Frequency: 8 * 1e6,
MOSI: machine.NINA_MOSI,
MISO: machine.NINA_MISO,
SCK: machine.NINA_SCK,
})
adaptor.Configure()
connectToAP()
for {
sendBatch()
time.Sleep(500 * time.Millisecond)
}
println("Done.")
}
func sendBatch() {
// make TCP connection
ip := net.ParseIP(serverIP)
raddr := &net.TCPAddr{IP: ip, Port: 8080}
laddr := &net.TCPAddr{Port: 8080}
message("---------------\r\nDialing TCP connection")
conn, err := net.DialTCP("tcp", laddr, raddr)
for ; err != nil; conn, err = net.DialTCP("tcp", laddr, raddr) {
message(err.Error())
time.Sleep(5 * time.Second)
}
n := 0
w := 0
start := time.Now()
// send data
message("Sending data")
for i := 0; i < 1000; i++ {
buf.Reset()
fmt.Fprint(buf,
"\r---------------------------- i == ", i, " ----------------------------"+
"\r---------------------------- i == ", i, " ----------------------------")
if w, err = conn.Write(buf.Bytes()); err != nil {
println("error:", err.Error(), "\r")
continue
}
n += w
}
buf.Reset()
ms := time.Now().Sub(start).Milliseconds()
fmt.Fprint(buf, "\nWrote ", n, " bytes in ", ms, " ms\r\n")
message(buf.String())
if _, err := conn.Write(buf.Bytes()); err != nil {
println("error:", err.Error(), "\r")
}
// Right now this code is never reached. Need a way to trigger it...
println("Disconnecting TCP...")
conn.Close()
}
// connect to access point
func connectToAP() {
time.Sleep(2 * time.Second)
message("Connecting to " + ssid)
adaptor.SetPassphrase(ssid, pass)
for st, _ := adaptor.GetConnectionStatus(); st != wifinina.StatusConnected; {
message("Connection status: " + st.String())
time.Sleep(1 * time.Second)
st, _ = adaptor.GetConnectionStatus()
}
message("Connected.")
time.Sleep(2 * time.Second)
ip, _, _, err := adaptor.GetIP()
for ; err != nil; ip, _, _, err = adaptor.GetIP() {
message(err.Error())
time.Sleep(1 * time.Second)
}
message(ip.String())
}
func message(msg string) {
println(msg, "\r")
}
+153
View File
@@ -0,0 +1,153 @@
// This example opens a TCP connection using a device with WiFiNINA firmware
// and sends a HTTP request to retrieve a webpage, based on the following
// Arduino example:
//
// https://github.com/arduino-libraries/WiFiNINA/blob/master/examples/WiFiWebClientRepeating/
//
package main
import (
"fmt"
"machine"
"time"
"tinygo.org/x/drivers/net"
"tinygo.org/x/drivers/wifinina"
)
// access point info
const ssid = ""
const pass = ""
// IP address of the server aka "hub". Replace with your own info.
const server = "tinygo.org"
// these are the default pins for the Arduino Nano33 IoT.
// change these to connect to a different UART or pins for the ESP8266/ESP32
var (
// these are the default pins for the Arduino Nano33 IoT.
uart = machine.UART2
tx = machine.NINA_TX
rx = machine.NINA_RX
spi = machine.NINA_SPI
// this is the ESP chip that has the WIFININA firmware flashed on it
adaptor = &wifinina.Device{
SPI: spi,
CS: machine.NINA_CS,
ACK: machine.NINA_ACK,
GPIO0: machine.NINA_GPIO0,
RESET: machine.NINA_RESETN,
}
console = machine.UART0
)
var buf [256]byte
var lastRequestTime time.Time
var conn net.Conn
func main() {
uart.Configure(machine.UARTConfig{TX: tx, RX: rx})
// Configure SPI for 8Mhz, Mode 0, MSB First
spi.Configure(machine.SPIConfig{
Frequency: 8 * 1e6,
MOSI: machine.NINA_MOSI,
MISO: machine.NINA_MISO,
SCK: machine.NINA_SCK,
})
adaptor.Configure()
connectToAP()
for {
loop()
}
println("Done.")
}
func loop() {
if conn != nil {
for n, err := conn.Read(buf[:]); n > 0; n, err = conn.Read(buf[:]) {
if err != nil {
println("Read error: " + err.Error())
} else {
print(string(buf[0:n]))
}
}
}
if time.Now().Sub(lastRequestTime).Milliseconds() >= 10000 {
makeHTTPRequest()
}
}
func makeHTTPRequest() {
var err error
if conn != nil {
conn.Close()
}
// make TCP connection
ip := net.ParseIP(server)
raddr := &net.TCPAddr{IP: ip, Port: 80}
laddr := &net.TCPAddr{Port: 8080}
message("\r\n---------------\r\nDialing TCP connection")
conn, err = net.DialTCP("tcp", laddr, raddr)
for ; err != nil; conn, err = net.DialTCP("tcp", laddr, raddr) {
message("connection failed: " + err.Error())
time.Sleep(5 * time.Second)
}
println("Connected!\r")
print("Sending HTTP request...")
fmt.Fprintln(conn, "GET / HTTP/1.1")
fmt.Fprintln(conn, "Host:", server)
fmt.Fprintln(conn, "User-Agent: TinyGo/0.10.0")
fmt.Fprintln(conn, "Connection: close")
fmt.Fprintln(conn)
println("Sent!\r\n\r")
lastRequestTime = time.Now()
}
func readLine(conn *net.TCPSerialConn) string {
println("Attempting to read...\r")
b := buf[:]
for expiry := time.Now().Unix() + 10; time.Now().Unix() > expiry; {
if n, err := conn.Read(b); n > 0 && err == nil {
return string(b[0:n])
}
}
return ""
}
// connect to access point
func connectToAP() {
time.Sleep(2 * time.Second)
message("Connecting to " + ssid)
adaptor.SetPassphrase(ssid, pass)
for st, _ := adaptor.GetConnectionStatus(); st != wifinina.StatusConnected; {
message("Connection status: " + st.String())
time.Sleep(1 * time.Second)
st, _ = adaptor.GetConnectionStatus()
}
message("Connected.")
time.Sleep(2 * time.Second)
ip, _, _, err := adaptor.GetIP()
for ; err != nil; ip, _, _, err = adaptor.GetIP() {
message(err.Error())
time.Sleep(1 * time.Second)
}
message(ip.String())
}
func message(msg string) {
println(msg, "\r")
}
+10
View File
@@ -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
View File
@@ -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)
}
}
+9
View File
@@ -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
+448
View File
@@ -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
View File
@@ -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"
}
}
+247
View File
@@ -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++
}
}
+154
View File
@@ -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
}
+81
View File
@@ -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
}
+19
View File
@@ -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.
+310
View File
@@ -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))
}
+87
View File
@@ -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: &parallelDriver{
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]))
}
}
+84
View File
@@ -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
View File
@@ -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)
}
+90
View File
@@ -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)
}
+183
View File
@@ -0,0 +1,183 @@
// Package lsm6ds3 implements a driver for the LSM6DS3 a 6 axis Inertial
// Measurement Unit (IMU)
//
// Datasheet: https://www.st.com/resource/en/datasheet/lsm6ds3.pdf
//
package lsm6ds3 // import "tinygo.org/x/drivers/lsm6ds3"
import (
"machine"
)
type AccelRange uint8
type AccelSampleRate uint8
type AccelBandwidth uint8
type GyroRange uint8
type GyroSampleRate uint8
// Device wraps an I2C connection to a LSM6DS3 device.
type Device struct {
bus machine.I2C
Address uint16
accelRange AccelRange
accelSampleRate AccelSampleRate
accelBandWidth AccelBandwidth
gyroRange GyroRange
gyroSampleRate GyroSampleRate
dataBufferSix []uint8
dataBufferTwo []uint8
}
// Configuration for LSM6DS3 device.
type Configuration struct {
AccelRange AccelRange
AccelSampleRate AccelSampleRate
AccelBandWidth AccelBandwidth
GyroRange GyroRange
GyroSampleRate GyroSampleRate
IsPedometer bool
ResetStepCounter bool
}
// New creates a new LSM6DS3 connection. The I2C bus must already be
// configured.
//
// This function only creates the Device object, it does not touch the device.
func New(bus machine.I2C) Device {
return Device{bus: bus, Address: Address}
}
// Configure sets up the device for communication.
func (d *Device) Configure(cfg Configuration) {
if cfg.AccelRange != 0 {
d.accelRange = cfg.AccelRange
} else {
d.accelRange = ACCEL_2G
}
if cfg.AccelSampleRate != 0 {
d.accelSampleRate = cfg.AccelSampleRate
} else {
d.accelSampleRate = ACCEL_SR_104
}
if cfg.AccelBandWidth != 0 {
d.accelBandWidth = cfg.AccelBandWidth
} else {
d.accelBandWidth = ACCEL_BW_100
}
if cfg.GyroRange != 0 {
d.gyroRange = cfg.GyroRange
} else {
d.gyroRange = GYRO_2000DPS
}
if cfg.GyroSampleRate != 0 {
d.gyroSampleRate = cfg.GyroSampleRate
} else {
d.gyroSampleRate = GYRO_SR_104
}
d.dataBufferSix = make([]uint8, 6)
d.dataBufferTwo = make([]uint8, 2)
if cfg.IsPedometer { // CONFIGURE AS PEDOMETER
// Configure accelerometer: 2G + 26Hz
d.bus.WriteRegister(uint8(d.Address), CTRL1_XL, []byte{uint8(ACCEL_2G) | uint8(ACCEL_SR_26)})
// Configure Zen_G, Yen_G, Xen_G, reset steps
if cfg.ResetStepCounter {
d.bus.WriteRegister(uint8(d.Address), CTRL10_C, []byte{0x3E})
} else {
d.bus.WriteRegister(uint8(d.Address), CTRL10_C, []byte{0x3C})
}
// Enable pedometer
d.bus.WriteRegister(uint8(d.Address), TAP_CFG, []byte{0x40})
} else { // NORMAL USE
// Configure accelerometer
data := make([]uint8, 1)
data[0] = uint8(d.accelRange) | uint8(d.accelSampleRate) | uint8(d.accelBandWidth)
d.bus.WriteRegister(uint8(d.Address), CTRL1_XL, data)
// Set ODR bit
d.bus.ReadRegister(uint8(d.Address), CTRL4_C, data)
data[0] = data[0] &^ BW_SCAL_ODR_ENABLED
data[0] |= BW_SCAL_ODR_ENABLED
d.bus.WriteRegister(uint8(d.Address), CTRL4_C, data)
// Configure gyroscope
data[0] = uint8(d.gyroRange) | uint8(d.gyroSampleRate)
d.bus.WriteRegister(uint8(d.Address), CTRL2_G, data)
}
}
// Connected returns whether a LSM6DS3 has been found.
// It does a "who am I" request and checks the response.
func (d *Device) Connected() bool {
data := []byte{0}
d.bus.ReadRegister(uint8(d.Address), WHO_AM_I, data)
return data[0] == 0x69
}
// ReadAcceleration reads the current acceleration from the device and returns
// it in µg (micro-gravity). When one of the axes is pointing straight to Earth
// and the sensor is not moving the returned value will be around 1000000 or
// -1000000.
func (d *Device) ReadAcceleration() (x int32, y int32, z int32) {
d.bus.ReadRegister(uint8(d.Address), OUTX_L_XL, d.dataBufferSix)
// k comes from "Table 3. Mechanical characteristics" 3 of the datasheet * 1000
k := int32(61) // 2G
if d.accelRange == ACCEL_4G {
k = 122
} else if d.accelRange == ACCEL_8G {
k = 244
} else if d.accelRange == ACCEL_16G {
k = 488
}
x = int32(int16((uint16(d.dataBufferSix[1])<<8)|uint16(d.dataBufferSix[0]))) * k
y = int32(int16((uint16(d.dataBufferSix[3])<<8)|uint16(d.dataBufferSix[2]))) * k
z = int32(int16((uint16(d.dataBufferSix[5])<<8)|uint16(d.dataBufferSix[4]))) * k
return
}
// ReadRotation reads the current rotation from the device and returns it in
// µ°/s (micro-degrees/sec). This means that if you were to do a complete
// rotation along one axis and while doing so integrate all values over time,
// you would get a value close to 360000000.
func (d *Device) ReadRotation() (x int32, y int32, z int32) {
d.bus.ReadRegister(uint8(d.Address), OUTX_L_G, d.dataBufferSix)
// k comes from "Table 3. Mechanical characteristics" 3 of the datasheet * 1000
k := int32(4375) // 125DPS
if d.gyroRange == GYRO_250DPS {
k = 8750
} else if d.gyroRange == GYRO_500DPS {
k = 17500
} else if d.gyroRange == GYRO_1000DPS {
k = 35000
} else if d.gyroRange == GYRO_2000DPS {
k = 70000
}
x = int32(int16((uint16(d.dataBufferSix[1])<<8)|uint16(d.dataBufferSix[0]))) * k
y = int32(int16((uint16(d.dataBufferSix[3])<<8)|uint16(d.dataBufferSix[2]))) * k
z = int32(int16((uint16(d.dataBufferSix[5])<<8)|uint16(d.dataBufferSix[4]))) * k
return
}
// ReadTemperature returns the temperature in celsius milli degrees (°C/1000)
func (d *Device) ReadTemperature() (int32, error) {
d.bus.ReadRegister(uint8(d.Address), OUT_TEMP_L, d.dataBufferTwo)
// From "Table 5. Temperature sensor characteristics"
// temp = value/16 + 25
t := 25000 + (int32(int16((int16(d.dataBufferTwo[1])<<8)|int16(d.dataBufferTwo[0])))*125)/2
return t, nil
}
// ReadSteps returns the steps of the pedometer
func (d *Device) ReadSteps() int32 {
d.bus.ReadRegister(uint8(d.Address), STEP_COUNTER_L, d.dataBufferTwo)
return int32(int16((uint16(d.dataBufferTwo[1]) << 8) | uint16(d.dataBufferTwo[0])))
}
+83
View File
@@ -0,0 +1,83 @@
package lsm6ds3
// Constants/addresses used for I2C.
// The I2C address which this device listens to.
const Address = 0x6A
const (
WHO_AM_I = 0x0F
STATUS = 0x1E
CTRL1_XL = 0x10
CTRL2_G = 0x11
CTRL3_C = 0x12
CTRL4_C = 0x13
CTRL5_C = 0x14
CTRL6_C = 0x15
CTRL7_G = 0x16
CTRL8_XL = 0x17
CTRL9_XL = 0x18
CTRL10_C = 0x19
OUTX_L_G = 0x22
OUTX_H_G = 0x23
OUTY_L_G = 0x24
OUTY_H_G = 0x25
OUTZ_L_G = 0x26
OUTZ_H_G = 0x27
OUTX_L_XL = 0x28
OUTX_H_XL = 0x29
OUTY_L_XL = 0x2A
OUTY_H_XL = 0x2B
OUTZ_L_XL = 0x2C
OUTZ_H_XL = 0x2D
OUT_TEMP_L = 0x20
OUT_TEMP_H = 0x21
BW_SCAL_ODR_DISABLED = 0x00
BW_SCAL_ODR_ENABLED = 0x80
STEP_TIMESTAMP_L = 0x49
STEP_TIMESTAMP_H = 0x4A
STEP_COUNTER_L = 0x4B
STEP_COUNTER_H = 0x4C
STEP_COUNT_DELTA = 0x15
TAP_CFG = 0x58
INT1_CTRL = 0x0D
ACCEL_2G AccelRange = 0x00
ACCEL_4G AccelRange = 0x08
ACCEL_8G AccelRange = 0x0C
ACCEL_16G AccelRange = 0x04
ACCEL_SR_OFF AccelSampleRate = 0x00
ACCEL_SR_13 AccelSampleRate = 0x10
ACCEL_SR_26 AccelSampleRate = 0x20
ACCEL_SR_52 AccelSampleRate = 0x30
ACCEL_SR_104 AccelSampleRate = 0x40
ACCEL_SR_208 AccelSampleRate = 0x50
ACCEL_SR_416 AccelSampleRate = 0x60
ACCEL_SR_833 AccelSampleRate = 0x70
ACCEL_SR_1666 AccelSampleRate = 0x80
ACCEL_SR_3332 AccelSampleRate = 0x90
ACCEL_SR_6664 AccelSampleRate = 0xA0
ACCEL_SR_13330 AccelSampleRate = 0xB0
ACCEL_BW_50 AccelBandwidth = 0x03
ACCEL_BW_100 AccelBandwidth = 0x02
ACCEL_BW_200 AccelBandwidth = 0x01
ACCEL_BW_400 AccelBandwidth = 0x00
//GYRO_125DPS GyroRange = 0x01
GYRO_250DPS GyroRange = 0x00
GYRO_500DPS GyroRange = 0x04
GYRO_1000DPS GyroRange = 0x08
GYRO_2000DPS GyroRange = 0x0C
GYRO_SR_OFF GyroSampleRate = 0x00
GYRO_SR_13 GyroSampleRate = 0x10
GYRO_SR_26 GyroSampleRate = 0x20
GYRO_SR_52 GyroSampleRate = 0x30
GYRO_SR_104 GyroSampleRate = 0x40
GYRO_SR_208 GyroSampleRate = 0x50
GYRO_SR_416 GyroSampleRate = 0x60
GYRO_SR_833 GyroSampleRate = 0x70
GYRO_SR_1666 GyroSampleRate = 0x80
)
+1 -1
View File
@@ -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)
+92
View File
@@ -0,0 +1,92 @@
// Package mcp3008 implements a driver for the MCP3008 Analog to Digital Converter.
//
// Datasheet: http://ww1.microchip.com/downloads/en/DeviceDoc/21295d.pdf
//
package mcp3008 // import "tinygo.org/x/drivers/mcp3008"
import (
"errors"
"machine"
)
// Device wraps MCP3008 SPI ADC.
type Device struct {
bus machine.SPI
cs machine.Pin
tx []byte
rx []byte
CH0 ADCPin
CH1 ADCPin
CH2 ADCPin
CH3 ADCPin
CH4 ADCPin
CH5 ADCPin
CH6 ADCPin
CH7 ADCPin
}
// ADCPin is the implementation of the ADConverter interface.
type ADCPin struct {
machine.Pin
d *Device
}
// New returns a new MCP3008 driver. Pass in a fully configured SPI bus.
func New(b machine.SPI, csPin machine.Pin) *Device {
d := &Device{bus: b,
cs: csPin,
tx: make([]byte, 3),
rx: make([]byte, 3),
}
// setup all channels
d.CH0 = d.GetADC(0)
d.CH1 = d.GetADC(1)
d.CH2 = d.GetADC(2)
d.CH3 = d.GetADC(3)
d.CH4 = d.GetADC(4)
d.CH5 = d.GetADC(5)
d.CH6 = d.GetADC(6)
d.CH7 = d.GetADC(7)
return d
}
// Configure sets up the device for communication
func (d *Device) Configure() {
d.cs.Configure(machine.PinConfig{Mode: machine.PinOutput})
}
// Read analog data from channel
func (d *Device) Read(ch int) (uint16, error) {
if ch < 0 || ch > 7 {
return 0, errors.New("invalid channel for MCP3008 Read")
}
return d.GetADC(ch).Get(), nil
}
// GetADC returns an ADC for a specific channel.
func (d *Device) GetADC(ch int) ADCPin {
return ADCPin{machine.Pin(ch), d}
}
// Get the current reading for a specific ADCPin.
func (p ADCPin) Get() uint16 {
p.d.tx[0] = 0x01
p.d.tx[1] = byte(8+p.Pin) << 4
p.d.tx[2] = 0x00
p.d.cs.Low()
p.d.bus.Tx(p.d.tx, p.d.rx)
// scale result to 16bit value like other ADCs
result := uint16((p.d.rx[1]&0x3))<<8 + uint16(p.d.rx[2])<<6
p.d.cs.High()
return result
}
// Configure here just for interface compatibility.
func (p ADCPin) Configure() {
}
+27
View File
@@ -0,0 +1,27 @@
package net
type DeviceDriver interface {
GetDNS(domain string) (string, error)
ConnectTCPSocket(addr, port string) error
ConnectSSLSocket(addr, port string) error
ConnectUDPSocket(addr, sendport, listenport string) error
DisconnectSocket() error
StartSocketSend(size int) error
Write(b []byte) (n int, err error)
ReadSocket(b []byte) (n int, err error)
IsSocketDataAvailable() bool
// FIXME: this is really specific to espat, and maybe shouldn't be part
// of the driver interface
Response(timeout int) ([]byte, error)
}
var ActiveDevice DeviceDriver
func UseDriver(driver DeviceDriver) {
// TODO: rethink and refactor this
if ActiveDevice != nil {
panic("net.ActiveDevice is already set")
}
ActiveDevice = driver
}
+149 -34
View File
@@ -1,3 +1,5 @@
// Package mqtt is intended to provide compatible interfaces with the
// Paho mqtt library.
package mqtt
import (
@@ -6,9 +8,8 @@ import (
"time"
"github.com/eclipse/paho.mqtt.golang/packets"
"tinygo.org/x/drivers/espat"
"tinygo.org/x/drivers/espat/net"
"tinygo.org/x/drivers/espat/tls"
"tinygo.org/x/drivers/net"
"tinygo.org/x/drivers/net/tls"
)
// NewClient will create an MQTT v3.1.1 client with all of the options specified
@@ -17,15 +18,21 @@ import (
// connection) are created before the application is actually ready.
func NewClient(o *ClientOptions) Client {
c := &mqttclient{opts: o, adaptor: o.Adaptor}
c.msgRouter, c.stopRouter = newRouter()
return c
}
type mqttclient struct {
adaptor *espat.Device
conn net.Conn
connected bool
opts *ClientOptions
mid uint16
adaptor net.DeviceDriver
conn net.Conn
connected bool
opts *ClientOptions
mid uint16
inbound chan packets.ControlPacket
stop chan struct{}
msgRouter *router
stopRouter chan bool
incomingPubChan chan *packets.PublishPacket
}
// AddRoute allows you to add a handler for messages on a specific topic
@@ -69,6 +76,12 @@ func (c *mqttclient) Connect() Token {
return &mqtttoken{err: errors.New("invalid protocol")}
}
c.mid = 1
c.inbound = make(chan packets.ControlPacket, 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
@@ -82,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{}
}
@@ -127,6 +139,10 @@ func (c *mqttclient) Disconnect(quiesce uint) {
// to the specified topic.
// Returns a token to track delivery of the message to the broker
func (c *mqttclient) Publish(topic string, qos byte, retained bool, payload interface{}) Token {
if !c.IsConnected() {
return &mqtttoken{err: errors.New("MQTT client not connected")}
}
pub := packets.NewControlPacket(packets.Publish).(*packets.PublishPacket)
pub.Qos = qos
pub.TopicName = topic
@@ -142,12 +158,37 @@ func (c *mqttclient) Publish(topic string, qos byte, retained bool, payload inte
c.mid++
err := pub.Write(c.conn)
return &mqtttoken{err: err}
if err != nil {
return &mqtttoken{err: err}
}
return &mqtttoken{}
}
// Subscribe starts a new subscription. Provide a MessageHandler to be executed when
// a message is published on the topic provided.
func (c *mqttclient) Subscribe(topic string, qos byte, callback MessageHandler) Token {
if !c.IsConnected() {
return &mqtttoken{err: errors.New("MQTT client not connected")}
}
sub := packets.NewControlPacket(packets.Subscribe).(*packets.SubscribePacket)
sub.Topics = append(sub.Topics, topic)
sub.Qoss = append(sub.Qoss, qos)
if callback != nil {
c.msgRouter.addRoute(topic, callback)
}
sub.MessageID = c.mid
c.mid++
// drop in the channel to send
err := sub.Write(c.conn)
if err != nil {
return &mqtttoken{err: err}
}
return &mqtttoken{}
}
@@ -171,18 +212,92 @@ func (c *mqttclient) OptionsReader() ClientOptionsReader {
return r
}
type mqtttoken struct {
err error
func processInbound(c *mqttclient) {
for {
select {
case msg := <-c.inbound:
switch m := msg.(type) {
case *packets.PingrespPacket:
// TODO: handle this
case *packets.SubackPacket:
// TODO: handle this
case *packets.UnsubackPacket:
// TODO: handle this
case *packets.PublishPacket:
// TODO: handle Qos
c.incomingPubChan <- m
case *packets.PubackPacket:
// TODO: handle this
case *packets.PubrecPacket:
// TODO: handle this
case *packets.PubrelPacket:
// TODO: handle this
case *packets.PubcompPacket:
// TODO: handle this
}
case <-c.stop:
return
}
}
}
func (t *mqtttoken) Wait() bool {
return true
// readMessages reads incoming messages off the wire.
// incoming messages are then send into inbound channel.
func readMessages(c *mqttclient) {
var err error
var cp packets.ControlPacket
PROCESS:
for {
if cp, err = c.ReadPacket(); err != nil {
break PROCESS
}
if cp != nil {
c.inbound <- cp
// TODO: Notify keepalive logic that we recently received a packet
}
time.Sleep(100 * time.Millisecond)
}
// TODO: handle if we received an error on read.
// If disconnect is in progress, swallow error and return
}
func (t *mqtttoken) WaitTimeout(time.Duration) bool {
return true
func (c *mqttclient) ackFunc(packet *packets.PublishPacket) func() {
return func() {
switch packet.Qos {
case 2:
// pr := packets.NewControlPacket(packets.Pubrec).(*packets.PubrecPacket)
// pr.MessageID = packet.MessageID
// DEBUG.Println(NET, "putting pubrec msg on obound")
// select {
// case c.oboundP <- &PacketAndToken{p: pr, t: nil}:
// case <-c.stop:
// }
// DEBUG.Println(NET, "done putting pubrec msg on obound")
case 1:
// pa := packets.NewControlPacket(packets.Puback).(*packets.PubackPacket)
// pa.MessageID = packet.MessageID
// DEBUG.Println(NET, "putting puback msg on obound")
// persistOutbound(c.persist, pa)
// select {
// case c.oboundP <- &PacketAndToken{p: pa, t: nil}:
// case <-c.stop:
// }
// DEBUG.Println(NET, "done putting puback msg on obound")
case 0:
// do nothing, since there is no need to send an ack packet back
}
}
}
func (t *mqtttoken) Error() error {
return t.err
// ReadPacket tries to read the next incoming packet from the MQTT broker.
// If there is no data yet but also is no error, it returns nil for both values.
func (c *mqttclient) ReadPacket() (packets.ControlPacket, error) {
// check for data first...
if net.ActiveDevice.IsSocketDataAvailable() {
return packets.ReadPacket(c.conn)
}
return nil, nil
}
+39 -4
View File
@@ -24,7 +24,8 @@ import (
"strings"
"time"
"tinygo.org/x/drivers/espat"
"github.com/eclipse/paho.mqtt.golang/packets"
"tinygo.org/x/drivers/net"
)
const (
@@ -155,6 +156,18 @@ func (m *message) Ack() {
return
}
func messageFromPublish(p *packets.PublishPacket, ack func()) Message {
return &message{
duplicate: p.Dup,
qos: p.Qos,
retained: p.Retain,
topic: p.TopicName,
messageID: p.MessageID,
payload: p.Payload,
ack: ack,
}
}
// ClientOptionsReader provides an interface for reading ClientOptions after the client has been initialized.
type ClientOptionsReader struct {
options *ClientOptions
@@ -162,7 +175,7 @@ type ClientOptionsReader struct {
// ClientOptions contains configurable options for an MQTT Client.
type ClientOptions struct {
Adaptor *espat.Device
Adaptor net.DeviceDriver
//Servers []*url.URL
Servers string
@@ -196,8 +209,8 @@ type ClientOptions struct {
}
// NewClientOptions returns a new ClientOptions struct.
func NewClientOptions(adaptor *espat.Device) *ClientOptions {
return &ClientOptions{Adaptor: adaptor, ProtocolVersion: 4}
func NewClientOptions() *ClientOptions {
return &ClientOptions{Adaptor: net.ActiveDevice, ProtocolVersion: 4}
}
// AddBroker adds a broker URI to the list of brokers to be used. The format should be
@@ -243,3 +256,25 @@ func (o *ClientOptions) SetPassword(p string) *ClientOptions {
o.Password = p
return o
}
// SetWill accepts a string will message to be set. When the client connects,
// it will give this will message to the broker, which will then publish the
// provided payload (the will) to any clients that are subscribed to the provided
// topic.
func (o *ClientOptions) SetWill(topic string, payload string, qos byte, retained bool) *ClientOptions {
o.SetBinaryWill(topic, []byte(payload), qos, retained)
return o
}
// SetBinaryWill accepts a []byte will message to be set. When the client connects,
// it will give this will message to the broker, which will then publish the
// provided payload (the will) to any clients that are subscribed to the provided
// topic.
func (o *ClientOptions) SetBinaryWill(topic string, payload []byte, qos byte, retained bool) *ClientOptions {
o.WillEnabled = true
o.WillTopic = topic
o.WillPayload = payload
o.WillQos = qos
o.WillRetained = retained
return o
}
+178
View File
@@ -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
}
}
}()
}
+19
View File
@@ -0,0 +1,19 @@
package mqtt
import "time"
type mqtttoken struct {
err error
}
func (t *mqtttoken) Wait() bool {
return true
}
func (t *mqtttoken) WaitTimeout(time.Duration) bool {
return true
}
func (t *mqtttoken) Error() error {
return t.err
}
+36 -17
View File
@@ -7,8 +7,6 @@ import (
"strconv"
"strings"
"time"
"tinygo.org/x/drivers/espat"
)
// DialUDP makes a UDP network connection. raadr is the port that the messages will
@@ -20,12 +18,15 @@ func DialUDP(network string, laddr, raddr *UDPAddr) (*UDPSerialConn, error) {
listenport := strconv.Itoa(laddr.Port)
// disconnect any old socket
espat.ActiveDevice.DisconnectSocket()
ActiveDevice.DisconnectSocket()
// connect new socket
espat.ActiveDevice.ConnectUDPSocket(addr, sendport, listenport)
err := ActiveDevice.ConnectUDPSocket(addr, sendport, listenport)
if err != nil {
return nil, err
}
return &UDPSerialConn{SerialConn: SerialConn{Adaptor: espat.ActiveDevice}, laddr: laddr, raddr: raddr}, nil
return &UDPSerialConn{SerialConn: SerialConn{Adaptor: ActiveDevice}, laddr: laddr, raddr: raddr}, nil
}
// ListenUDP listens for UDP connections on the port listed in laddr.
@@ -35,12 +36,15 @@ func ListenUDP(network string, laddr *UDPAddr) (*UDPSerialConn, error) {
listenport := strconv.Itoa(laddr.Port)
// disconnect any old socket
espat.ActiveDevice.DisconnectSocket()
ActiveDevice.DisconnectSocket()
// connect new socket
espat.ActiveDevice.ConnectUDPSocket(addr, sendport, listenport)
err := ActiveDevice.ConnectUDPSocket(addr, sendport, listenport)
if err != nil {
return nil, err
}
return &UDPSerialConn{SerialConn: SerialConn{Adaptor: espat.ActiveDevice}, laddr: laddr}, nil
return &UDPSerialConn{SerialConn: SerialConn{Adaptor: ActiveDevice}, laddr: laddr}, nil
}
// DialTCP makes a TCP network connection. raadr is the port that the messages will
@@ -50,13 +54,16 @@ func DialTCP(network string, laddr, raddr *TCPAddr) (*TCPSerialConn, error) {
addr := raddr.IP.String()
sendport := strconv.Itoa(raddr.Port)
// disconnect any old socket
espat.ActiveDevice.DisconnectSocket()
// disconnect any old socket?
//ActiveDevice.DisconnectSocket()
// connect new socket
espat.ActiveDevice.ConnectTCPSocket(addr, sendport)
err := ActiveDevice.ConnectTCPSocket(addr, sendport)
if err != nil {
return nil, err
}
return &TCPSerialConn{SerialConn: SerialConn{Adaptor: espat.ActiveDevice}, laddr: laddr, raddr: raddr}, nil
return &TCPSerialConn{SerialConn: SerialConn{Adaptor: ActiveDevice}, laddr: laddr, raddr: raddr}, nil
}
// Dial connects to the address on the named network.
@@ -87,7 +94,7 @@ func Dial(network, address string) (Conn, error) {
// SerialConn is a loosely net.Conn compatible implementation
type SerialConn struct {
Adaptor *espat.Device
Adaptor DeviceDriver
}
// UDPSerialConn is a loosely net.Conn compatible intended to support
@@ -132,8 +139,20 @@ func (c *SerialConn) Read(b []byte) (n int, err error) {
func (c *SerialConn) Write(b []byte) (n int, err error) {
// specify that is a data transfer to the
// currently open socket, not commands to the ESP8266/ESP32.
c.Adaptor.StartSocketSend(len(b))
return c.Adaptor.Write(b)
err = c.Adaptor.StartSocketSend(len(b))
if err != nil {
return
}
n, err = c.Adaptor.Write(b)
if err != nil {
return n, err
}
/* TODO(bcg): this is kind of specific to espat, should maybe refactor */
_, err = c.Adaptor.Response(1000)
if err != nil {
return n, err
}
return n, err
}
// Close closes the connection.
@@ -220,7 +239,7 @@ func ResolveTCPAddr(network, address string) (*TCPAddr, error) {
// TODO: make sure network is 'tcp'
// separate domain from port, if any
r := strings.Split(address, ":")
addr, err := espat.ActiveDevice.GetDNS(r[0])
addr, err := ActiveDevice.GetDNS(r[0])
if err != nil {
return nil, err
}
@@ -243,7 +262,7 @@ func ResolveUDPAddr(network, address string) (*UDPAddr, error) {
// TODO: make sure network is 'udp'
// separate domain from port, if any
r := strings.Split(address, ":")
addr, err := espat.ActiveDevice.GetDNS(r[0])
addr, err := ActiveDevice.GetDNS(r[0])
if err != nil {
return nil, err
}
+4 -5
View File
@@ -5,8 +5,7 @@ package tls
import (
"strconv"
"tinygo.org/x/drivers/espat"
"tinygo.org/x/drivers/espat/net"
"tinygo.org/x/drivers/net"
)
// Dial makes a TLS network connection. It tries to provide a mostly compatible interface
@@ -22,15 +21,15 @@ func Dial(network, address string, config *Config) (*net.TCPSerialConn, error) {
sendport := strconv.Itoa(raddr.Port)
// disconnect any old socket
espat.ActiveDevice.DisconnectSocket()
net.ActiveDevice.DisconnectSocket()
// connect new socket
err = espat.ActiveDevice.ConnectSSLSocket(addr, sendport)
err = net.ActiveDevice.ConnectSSLSocket(addr, sendport)
if err != nil {
return nil, err
}
return net.NewTCPSerialConn(net.SerialConn{Adaptor: espat.ActiveDevice}, nil, raddr), nil
return net.NewTCPSerialConn(net.SerialConn{Adaptor: net.ActiveDevice}, nil, raddr), nil
}
// Config is a placeholder for future compatibility with
+47
View File
@@ -0,0 +1,47 @@
// Package semihosting implements parts of the ARM semihosting specification,
// for communicating over a debug connection.
//
// If you want to use it in OpenOCD, you have to enable it first with the
// following command:
//
// arm semihosting enable
package semihosting
import (
"device/arm"
"unsafe"
)
// IOError is returned by I/O operations when they fail.
type IOError struct {
BytesWritten int
}
func (e *IOError) Error() string {
return "semihosting: I/O error"
}
// Write writes the given data to the given file descriptor. It returns an
// *IOError if the write was not successful.
func Write(fd uintptr, data []byte) error {
if len(data) == 0 {
return nil
}
params := struct {
fd uintptr
data unsafe.Pointer
len int
}{
fd: fd,
data: unsafe.Pointer(&data[0]),
len: len(data),
}
unwritten := arm.SemihostingCall(arm.SemihostingWrite, uintptr(unsafe.Pointer(&params)))
if unwritten != 0 {
// Error: unwritten is the number of bytes not written.
return &IOError{
BytesWritten: len(data) - unwritten,
}
}
return nil
}
+20
View File
@@ -0,0 +1,20 @@
package semihosting
// These three file descriptors are connected to the host stdin/stdout/stderr,
// and can be used for logging.
var (
Stdin = File{fd: 0}
Stdout = File{fd: 1}
Stderr = File{fd: 2}
)
// File represents a semihosting file descriptor.
type File struct {
fd uintptr
}
// Write writes the given data buffer to the file descriptor, returning an error
// if the write could not complete successfully.
func (f *File) Write(buf []byte) error {
return Write(f.fd, buf)
}
+32
View File
@@ -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()
}
+110
View File
@@ -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
}
+93
View File
@@ -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
View File
@@ -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
+3 -16
View File
@@ -34,7 +34,7 @@ type Config struct {
Height int16
}
// New creates a new ST7735 connection. The SPI wire must already be configured.
// New creates a new SSD1331 connection. The SPI wire must already be configured.
func New(bus machine.SPI, resetPin, dcPin, csPin machine.Pin) Device {
dcPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
resetPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
@@ -250,21 +250,8 @@ func (d *Device) Data(data uint8) {
// Tx sends data to the display
func (d *Device) Tx(data []byte, isCommand bool) {
if isCommand {
d.csPin.High()
d.dcPin.Low()
d.csPin.Low()
d.bus.Tx(data, nil)
d.csPin.High()
} else {
d.csPin.High()
d.dcPin.High()
d.csPin.Low()
d.bus.Tx(data, nil)
d.csPin.High()
}
d.dcPin.Set(!isCommand)
d.bus.Tx(data, nil)
}
// Size returns the current size of the display.
+2
View File
@@ -46,6 +46,8 @@ const (
PWCTR6 = 0xFC
GMCTRP1 = 0xE0
GMCTRN1 = 0xE1
VSCRDEF = 0x33
VSCRSADD = 0x37
GREENTAB Model = 0
MINI80x160 Model = 1
+25 -50
View File
@@ -76,21 +76,8 @@ func (d *Device) Configure(cfg Config) {
d.height = 160
}
d.rotation = cfg.Rotation
if cfg.RowOffset != 0 {
d.rowOffset = cfg.RowOffset
} else {
d.rowOffset = 1
}
if cfg.ColumnOffset != 0 {
d.columnOffset = cfg.ColumnOffset
} else {
if d.model == MINI80x160 {
d.columnOffset = 26
} else {
d.columnOffset = 2
}
}
d.rowOffset = cfg.RowOffset
d.columnOffset = cfg.ColumnOffset
d.batchLength = d.width
if d.height > d.width {
@@ -151,29 +138,9 @@ func (d *Device) Configure(cfg Config) {
if d.model == GREENTAB {
d.InvertColors(false)
d.Command(CASET)
d.Data(0x00)
d.Data(0x02)
d.Data(0x00)
d.Data(0x7F + 0x02)
d.Command(RASET)
d.Data(0x00)
d.Data(0x01)
d.Data(0x00)
d.Data(0x9F + 0x01)
} else if d.model == MINI80x160 {
d.isBGR = true
d.InvertColors(true)
d.Command(CASET)
d.Data(0x00)
d.Data(0x00)
d.Data(0x00)
d.Data(0x7F)
d.Command(RASET)
d.Data(0x00)
d.Data(0x00)
d.Data(0x00)
d.Data(0x9F)
}
// common color adjustment
@@ -257,6 +224,27 @@ func (d *Device) setWindow(x, y, w, h int16) {
d.Command(RAMWR)
}
// SetScrollWindow sets an area to scroll with fixed top and bottom parts of the display
func (d *Device) SetScrollArea(topFixedArea, bottomFixedArea int16) {
d.Command(VSCRDEF)
d.Tx([]uint8{
uint8(topFixedArea >> 8), uint8(topFixedArea),
uint8(d.height - topFixedArea - bottomFixedArea>>8), uint8(d.height - topFixedArea - bottomFixedArea),
uint8(bottomFixedArea >> 8), uint8(bottomFixedArea)},
false)
}
// SetScroll sets the vertical scroll address of the display.
func (d *Device) SetScroll(line int16) {
d.Command(VSCRSADD)
d.Tx([]uint8{uint8(line >> 8), uint8(line)}, false)
}
// SpotScroll returns the display to its normal state
func (d *Device) StopScroll() {
d.Command(NORON)
}
// FillRectangle fills a rectangle at a given coordinates with a color
func (d *Device) FillRectangle(x, y, width, height int16, c color.RGBA) error {
k, i := d.Size()
@@ -383,21 +371,8 @@ func (d *Device) Data(data uint8) {
// Tx sends data to the display
func (d *Device) Tx(data []byte, isCommand bool) {
if isCommand {
d.csPin.High()
d.dcPin.Low()
d.csPin.Low()
d.bus.Tx(data, nil)
d.csPin.High()
} else {
d.csPin.High()
d.dcPin.High()
d.csPin.Low()
d.bus.Tx(data, nil)
d.csPin.High()
}
d.dcPin.Set(!isCommand)
d.bus.Tx(data, nil)
}
// Size returns the current size of the display.
+9 -27
View File
@@ -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)
+1 -1
View File
@@ -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 {
+18
View File
@@ -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
)
+58
View File
@@ -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
}
+17
View File
@@ -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
}
+124
View File
@@ -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)
}

Some files were not shown because too many files have changed in this diff Show More