Compare commits

...

55 Commits

Author SHA1 Message Date
deadprogram ce0ce03cfe all: release 0.22.0
Signed-off-by: deadprogram <ron@hybridgroup.com>
2022-08-16 11:43:34 +02:00
jmacwhyte 5bd814c9de MQTT: adds keepalive pinging, disconnect, and graceful goroutine cleanup 2022-08-10 19:23:36 +02:00
sago35 41b7f06d05 rtl8720dn: add UDP close function 2022-08-08 08:43:57 +02:00
Kenneth Bell 749b2f6726 epd: add waveshare 2.9in (v1) 2022-08-05 21:01:04 +02:00
deadprogram 71c77d4b53 makeybutton: move pin config where it needs to be
Signed-off-by: deadprogram <ron@hybridgroup.com>
2022-07-28 08:31:53 +02:00
deadprogram 1f54c7dd23 makeybutton: use smaller buffer for faster response
Signed-off-by: deadprogram <ron@hybridgroup.com>
2022-07-28 08:31:53 +02:00
deadprogram d0163c67f5 all: update tinyfont to v0.3.0
Signed-off-by: deadprogram <ron@hybridgroup.com>
2022-07-25 17:38:48 +02:00
deadprogram 80cd4d9dac all: update tinyfs to v0.2.0
Signed-off-by: deadprogram <ron@hybridgroup.com>
2022-07-24 17:05:01 +02:00
sago35 594ba1dab2 net/http: support for cookies when https 2022-07-24 16:13:18 +02:00
sago35 625c565c0b rtl8720dn: improve error handling 2022-07-24 12:21:50 +02:00
sago35 b64d4ec3a0 net/http: improve header parsing 2022-07-24 12:09:06 +02:00
sago35 171366698b net/http: Fix http.Get() with port specification 2022-07-23 09:13:55 +02:00
jmacwhyte ca34b93e5d add last-will-and-testament to MQTT 2022-07-22 13:40:44 +02:00
Kamil Wcisło 2cfc08b560 net/mqtt: add support for retained messsages 2022-07-21 13:00:08 +02:00
sago35 1850c5effb rtl8720dn: add ./examples/rtl8720dn/version 2022-07-20 11:38:07 +02:00
Neil Davis 810888ce42 Makefile recursively finds unit-tests
This commit modifies how the Makefile selects dirs for the unit-test target
Instead of assuming all dirs have tests and manually excluding those that don't,
it now recursively finds all dirs containing files with names matching "*_test.go"

A manual exclusion list is maintained for operational use.
2022-07-19 11:58:37 +02:00
deadprogram cafe8df620 makeybutton: add driver for MakeyMakey-like button
Signed-off-by: deadprogram <ron@hybridgroup.com>
2022-07-18 20:44:46 +02:00
Neil Davis 13c43350a3 Fix irremote reporting incorrect NEC addresses and command codes (#422)
Change from reading MSB->LSB to LSB->MSB
Fixes https://github.com/tinygo-org/drivers/issues/422
2022-07-13 12:07:44 +02:00
deadprogram 3edd8e413b build: switching to GHA
Signed-off-by: deadprogram <ron@hybridgroup.com>
2022-07-12 00:15:33 +02:00
deadprogram 159f13974c all: update for release v0.21.0
Signed-off-by: deadprogram <ron@hybridgroup.com>
2022-07-05 11:15:55 +02:00
Neil Davis 8ddcd69ac0 Add support for '8-step mode' to easystepper
Currently, easystepper driver supports only '4-step mode' (12-23-34-41) when energizing the coils
This does not work with all motors & drivers (e.g. 28BJY-48 & ULN2003)

This commit adds support for 8-step mode (1-12-2-23-3-34-4-41)

Note: this commit breaks backward source compatibility for the factory/constructor functions.
2022-06-12 22:19:42 +02:00
deadprogram 1c038f5183 wifinina: add mutex to prevent communication race problems
Signed-off-by: deadprogram <ron@hybridgroup.com>
2022-06-11 10:59:41 +02:00
tgilkerson 36a8b791dc fix(ssd1351): Fix mirrored text on OLED display 2022-06-04 09:13:44 +02:00
Olaf Flebbe df0d0d29a4 ws2812: support thingplus-rp2040 board
Added 125 MHz rp2040 timing
  Added unsafe.Pointer for pointer conversion
2022-05-28 10:22:26 +02:00
Yurii Soldak 3bdab45452 lsm6ds3tr: initial implementation 2022-05-27 07:48:50 +02:00
Olaf Flebbe 9f6783670f sdcard: support thingplus-rp2040
The instances of SPI interfaces SPI0, SPI1, ... are
pointers to SPI types on rp2040 machines, rather plain
values like other machines.

This needs restructuring the sdcard API to take a pointer
to the SPI interface rather the SPI type itself.

Removed waitserial() since it needs modem control lines
not available on most boards
2022-05-24 19:16:34 +02:00
Daniel Esteban ec00fdef9b initial support for UC8151 driver, used in Pimoroni's badger2040 e-paper (#416) 2022-05-24 17:43:07 +02:00
Michael Meister e4951091f4 vl53l1x: Add functions for setting the device address
Implement functions to get and set the device address similar to its
reference implementation in C:
https://github.com/pololu/vl53l1x-arduino/blob/master/VL53L1X.cpp#L28

Signed-off-by: Michael Meister <michael.meister@bytesatwork.ch>
2022-05-18 19:43:30 +02:00
Daniel Esteban 19caee9b76 fixed link to datasheet
fixed temp/humidity values
2022-05-15 07:41:39 +02:00
deadprogram c235509580 scd4x: implement driver for CO2 sensor
Signed-off-by: deadprogram <ron@hybridgroup.com>
2022-05-15 07:41:39 +02:00
deadprogram dbff576c9e all: release 0.20.0
Signed-off-by: deadprogram <ron@hybridgroup.com>
2022-04-28 17:03:38 +02:00
Ayke van Laethem f0a260be66 ws2812: convert AVR assembly to C inline assembly
See https://github.com/tinygo-org/drivers/pull/401 for details.
I haven't converted it to autogenerated assembly because AVR is
different from many other architectures (8-bit, among others) and it
didn't seem worth the effort as many chips run at 16MHz anyway.

I ran the two AVR smoke tests for the ws2812 driver and the resulting
binary is exactly the same.
2022-04-21 11:10:18 +02:00
Ayke van Laethem 06e298c514 ws2812: write inline assembly using C instead of Go
This is better for various reasons:

  * I don't like the inline assembly implementation in TinyGo. It kinda
    works, but it's hard to use correctly.
  * The Go version had a subtle bug: the i and value variables weren't
    marked as modified. The compiler produced correct code by chance. In
    GCC-style inline assembly, it's possible to correctly mark it as an
    input+output operand.
  * LLVM 14 has some changes to inline assembly that change how memory
    operands can be created. Instead of supporting that, I'd like to get
    rid of memory operands entirely (and possibly inline assembly in
    general).

I did some light testing: the ARM assembly changed a bit but not in any
way that should have a practical effect, and the RISC-V assembly didn't
change at all.
2022-04-21 11:10:18 +02:00
Kuiki f2f470973d fix esp8266 ConnectToAccessPoint timeout args 2022-04-21 10:23:21 +02:00
Oliver Stäbler 448598cbf8 vl53l1x: Add functions for setting 'region of interest'
Implement functions to get and set 'region of interest' similar to its
reference implementation in C:
https://github.com/pololu/vl53l1x-arduino/commit/a6de3966784aa07680bb35a1f9ac369959efe19f

Signed-off-by: Oliver Stäbler <oliver.staebler@bytesatwork.ch>
2022-04-21 08:03:22 +02:00
Oliver Stäbler ec98f2dc2c vl53l1x: Fix switch-case semantics
The source code has been ported from C. It seems, the different
semantics of switch-case in Go has not been ported properly.
To fix this, remove 'break' statements and introduce 'fallthrough' where
necessary.

Signed-off-by: Oliver Stäbler <oliver.staebler@bytesatwork.ch>
2022-04-20 20:14:36 +02:00
Patricio Whittingslow 2476cd7bd8 pca9685: fix on=0 bug
Setting on to zero caused PWM signal to be set to high.
2022-04-16 19:14:01 +02:00
Neil Davis 01fed475e1 Add 'irremote' package as a basic infra-red driver
Currently contains an IR Receiver device only.
An IR Sender device will be added shortly

Supports receiving only NEC (extended address) protocol including repeat codes so far
2022-04-11 22:05:57 +02:00
soypat edfdd4e09e add l3gd20 gyro driver 2022-04-08 15:41:46 +02:00
soypat 5e54f08605 pca9685: add buffered one shot write 2022-04-04 17:12:39 +02:00
sago35 880b958d64 ili9341: added Feather board support to InitDisplay() 2022-03-12 08:42:26 +01:00
sago35 24db8b4e2c ili9341: add support for atsame5x 2022-03-11 07:53:30 +01:00
sago35 6df9247f92 ili9341: avoid heap allocations 2022-03-10 20:28:20 +01:00
Elfo404 56fb346438 dht: fix error check in example 2022-03-10 19:10:50 +01:00
Neil Davis 3d279d4d25 Add a SetIntensity() function to max7xx driver and example.
Also remove redundant byte() casts from SetScanLimit()
2022-03-10 18:05:34 +01:00
sago35 4a336f674c image: fix interface 2022-03-10 12:53:44 +01:00
Ayke van Laethem 2d32995f6a ws2812: support high-MHz ARMv6M chips like the RP2040
The possible branch distance is a lot shorter on ARMv6M (Cortex-M and
Cortex-M0+) for conditional branches. Therefore, convert this long
conditional branch into an unconditional branch.

This probably makes the code a little bit slower but because it is in
the low period of the WS2812 signal it shouldn't matter for the
protocol. And it avoids difficult workarounds specifically for the
RP2040.
2022-03-10 10:50:12 +01:00
Anton Fisher 9a98d1be55 drivers: add driver for IS31FL3731 matrix LED driver (#370)
IS31FL3731: add driver for IS31FL3731 matrix LED driver
2022-03-09 13:54:07 +01:00
Donia Chaiehloudj 02e4548966 dht: fix humidity and temperature extraction for DHT22 (#358) 2022-03-08 12:42:28 +01:00
Yurii Soldak 45dce188f5 lsmXXX: unified, error handling, memory management 2022-02-18 06:10:27 +01:00
Steven Pearson b3c0315a09 Driver for SSD1289 LCD 2022-02-18 06:01:55 +01:00
BCG 0ced12683c Revert "Added support for 125MHz to WS2812 for RP2040"
This reverts commit b4eb406a43 (erroneous
push).
2022-02-14 01:06:23 -05:00
BCG b4eb406a43 Added support for 125MHz to WS2812 for RP2040 2022-02-14 00:48:33 -05:00
sago35 10bd48c39d ws2812: add support for m5stamp-c3 2022-02-11 23:47:55 +01:00
Yurii Soldak ad3ef92cfe lps22hb: pin rename, sync with main repo 2022-01-29 23:02:42 +01:00
111 changed files with 7374 additions and 2669 deletions
-20
View File
@@ -1,20 +0,0 @@
# Golang CircleCI 2.0 configuration file
#
# Check https://circleci.com/docs/2.0/language-go/ for more details
version: 2
jobs:
build:
docker:
- image: tinygo/tinygo-dev
steps:
- checkout
- run: tinygo version
- run:
name: "Enforce Go Formatted Code"
command: make fmt-check
- run:
name: "Run unit tests"
command: make unit-test
- run:
name: "Run build and smoke tests"
command: make smoke-test
+25
View File
@@ -0,0 +1,25 @@
name: Build
on:
pull_request:
push:
branches:
- dev
- release
workflow_dispatch:
jobs:
build:
runs-on: ubuntu-latest
container: tinygo/tinygo-dev
steps:
- name: Checkout
uses: actions/checkout@v2
- name: TinyGo version check
run: tinygo version
- name: Enforce Go Formatted Code
run: make fmt-check
- name: Run unit tests
run: make unit-test
- name: Run build and smoke tests
run: make smoke-test
+97
View File
@@ -1,3 +1,100 @@
0.22.0
---
- **new devices**
- epd: add waveshare 2.9in (v1)
- makeybutton: add driver for MakeyMakey-like button
- **enhancements**
- **rtl8720dn**
- add UDP close function
- improve error handling
- **net/http**
- improve header parsing
- add last-will-and-testament to MQTT
- **net/mqtt**
- adds keepalive pinging, disconnect, and graceful goroutine cleanup
- support for cookies when https
- add support for retained messsages
- **bugfixes**
- irremote: Fix irremote reporting incorrect NEC addresses and command codes (#422)
- net/http: Fix http.Get() with port specification
- **build**
- Makefile recursively finds unit-tests
- switching to GHA
- **updates**
- update tinyfont to v0.3.0
- update tinyfs to v0.2.0
- **examples**
- rtl8720dn: add ./examples/rtl8720dn/version
0.21.0
---
- **new devices**
- lsm6ds3tr: initial implementation
- UC8151: used in Pimoroni's badger2040 e-paper (#416)
- scd4x: implement driver for CO2 sensor
- **enhancements**
- easystepper: Add support for '8-step mode'
- vl53l1x: Add functions for setting the device address
- sdcard: support thingplus-rp2040
- wifinina: add mutex to prevent communication race problems
- **ws2812**
- support thingplus-rp2040 board
- Added 125 MHz rp2040 timing
- Added unsafe.Pointer for pointer conversion
- **bugfixes**
- ssd1351: Fix mirrored text on OLED display
0.20.0
---
- **new devices**
- irremote: Add basic infra-red driver
- IS31FL3731: add driver for IS31FL3731 matrix LED driver (#370)
- l3gd20: add gyro driver
- SSD1289: Driver for SSD1289 LCD
- **enhancements**
- **ili9341**
- add support for atsame5x
- added Feather board support to InitDisplay()
- avoid heap allocations
- **lps22hb**
- pin rename, sync with main repo
- **lsmXXX**
- unified, error handling, memory management
- **max7xx**
- Add a SetIntensity() function to max7xx driver and example
- **vl53l1x**
- Add functions for setting 'region of interest'
- Fix switch-case semantics
- **ws2812**
- add support for m5stamp-c3
- convert AVR assembly to C inline assembly
- support high-MHz ARMv6M chips like the RP2040
- write inline assembly using C instead of Go
- **bugfixes**
- **dht**
- fix error check in example
- fix humidity and temperature extraction for DHT22 (#358)
- **esp8266**
- fix ConnectToAccessPoint timeout args
- **image**
- fix interface
- **pca9685**
- add buffered one shot write
- fix on=0 bug
- **wifinina**
- correct sendParamStr to handle empty strings, such as when connecting to an unsecured access point
0.19.0
---
- **new devices**
+25 -8
View File
@@ -107,6 +107,8 @@ smoke-test:
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=p1am-100 ./examples/p1am/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=pico ./examples/pca9685/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
@@ -157,6 +159,10 @@ smoke-test:
@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.bin -target=m5stamp-c3 ./examples/ws2812
@md5sum ./build/test.bin
tinygo build -size short -o ./build/test.hex -target=feather-nrf52840 ./examples/is31fl3731/main.go
@md5sum ./build/test.hex
ifneq ($(AVR), 0)
tinygo build -size short -o ./build/test.hex -target=arduino ./examples/ws2812
@md5sum ./build/test.hex
@@ -225,16 +231,27 @@ endif
@md5sum ./build/test.elf
tinygo build -size short -o ./build/test.hex -target=nucleo-wl55jc ./examples/sx126x/lora_rxtx/
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.uf2 -target=pico ./examples/ssd1289/main.go
@md5sum ./build/test.uf2
tinygo build -size short -o ./build/test.hex -target=pico ./examples/irremote/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=badger2040 ./examples/uc8151/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.uf2 -target=pico ./examples/scd4x/main.go
@md5sum ./build/test.uf2
tinygo build -size short -o ./build/test.uf2 -target=circuitplay-express ./examples/makeybutton/main.go
@md5sum ./build/test.uf2
DRIVERS = $(wildcard */)
NOTESTS = build examples flash semihosting pcd8544 shiftregister st7789 microphone mcp3008 gps microbitmatrix \
hcsr04 ssd1331 ws2812 thermistor apa102 easystepper ssd1351 ili9341 wifinina shifter hub75 \
hd44780 buzzer ssd1306 espat l9110x st7735 bmi160 l293x dht keypad4x4 max72xx p1am tone tm1637 \
pcf8563 mcp2515 servo sdcard rtl8720dn image cmd i2csoft hts221 lps22hb apds9960 axp192 xpt2046 \
ft6336 sx126x
TESTS = $(filter-out $(addsuffix /%,$(NOTESTS)),$(DRIVERS))
# rwildcard is a recursive version of $(wildcard)
# https://blog.jgc.org/2011/07/gnu-make-recursive-wildcard-function.html
rwildcard=$(foreach d,$(wildcard $1*),$(call rwildcard,$d/,$2) $(filter $(subst *,%,$2),$d))
# Recursively find all *_test.go files from cwd & reduce to unique dir names
HAS_TESTS = $(sort $(dir $(call rwildcard,,*_test.go)))
# Exclude anything we explicitly don't want to test for whatever reason
EXCLUDE_TESTS = image
TESTS = $(filter-out $(addsuffix /%,$(EXCLUDE_TESTS)),$(HAS_TESTS))
unit-test:
@go test -v $(addprefix ./,$(TESTS))
@go test -v $(addprefix ./,$(TESTS))
test: clean fmt-check unit-test smoke-test
+90 -82
View File
@@ -1,6 +1,6 @@
# TinyGo Drivers
[![PkgGoDev](https://pkg.go.dev/badge/tinygo.org/x/drivers)](https://pkg.go.dev/tinygo.org/x/drivers) [![CircleCI](https://circleci.com/gh/tinygo-org/drivers/tree/dev.svg?style=svg)](https://circleci.com/gh/tinygo-org/drivers/tree/dev)
[![PkgGoDev](https://pkg.go.dev/badge/tinygo.org/x/drivers)](https://pkg.go.dev/tinygo.org/x/drivers) [![Build](https://github.com/tinygo-org/drivers/actions/workflows/build.yml/badge.svg?branch=dev)](https://github.com/tinygo-org/drivers/actions/workflows/build.yml)
This package provides a collection of hardware drivers for devices such as sensors and displays that can be used together with [TinyGo](https://tinygo.org).
@@ -52,89 +52,97 @@ func main() {
## Currently supported devices
The following 78 devices are supported.
The following 82 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 |
| [AHT20 I2C Temperature and Humidity Sensor](http://www.aosong.com/userfiles/files/media/AHT20%20%E8%8B%B1%E6%96%87%E7%89%88%E8%AF%B4%E6%98%8E%E4%B9%A6%20A0%2020201222.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 |
| [APDS9960 Digital proximity, ambient light, RGB and gesture sensor](https://cdn.sparkfun.com/assets/learn_tutorials/3/2/1/Avago-APDS-9960-datasheet.pdf) | I2C |
| [AT24CX 2-wire serial EEPROM](https://www.openimpulse.com/blog/wp-content/uploads/wpsc/downloadables/24C32-Datasheet.pdf) | I2C |
| [AXP192 single Cell Li-Battery and Power System Management](https://github.com/m5stack/M5-Schematic/blob/master/Core/AXP192%20Datasheet_v1.1_en_draft_2211.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 |
| [BMI160 accelerometer/gyroscope](https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bmi160-ds000.pdf) | SPI |
| [BMP180 barometer](https://cdn-shop.adafruit.com/datasheets/BST-BMP180-DS000-09.pdf) | I2C |
| [BMP280 temperature/barometer](https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bmp280-ds001.pdf) | I2C |
| [BMP388 pressure sensor](https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bmp388-ds001.pdf) | I2C |
| [Buzzer](https://en.wikipedia.org/wiki/Buzzer#Piezoelectric) | GPIO |
| [DHTXX thermometer and humidity sensor](https://cdn-shop.adafruit.com/datasheets/Digital+humidity+and+temperature+sensor+AM2302.pdf) | 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 |
| [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 |
| [FT6336 touch controller](https://focuslcds.com/content/FT6236.pdf) | I2C |
| [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/I2C |
| [HTS221 digital humidity and temperature sensor](https://www.st.com/resource/en/datasheet/hts221.pdf) | I2C |
| [HUB75 RGB led matrix](https://cdn-learn.adafruit.com/downloads/pdf/32x16-32x32-rgb-led-matrix.pdf) | SPI |
| [software I2C driver](https://www.ti.com/lit/an/slva704/slva704.pdf) | GPIO |
| [ILI9341 TFT color display](https://cdn-shop.adafruit.com/datasheets/ILI9341.pdf) | SPI |
| [INA260 Volt/Amp/Power meter](https://www.ti.com/lit/ds/symlink/ina260.pdf) | I2C |
| [4x4 Membrane Keypad](https://cdn.sparkfun.com/assets/f/f/a/5/0/DS-16038.pdf) | GPIO |
| [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 |
| [LIS2MDL magnetometer](https://www.st.com/resource/en/datasheet/lis2mdl.pdf) | I2C |
| [LIS3DH accelerometer](https://www.st.com/resource/en/datasheet/lis3dh.pdf) | I2C |
| [LPS22HB MEMS nano pressure sensor](https://www.st.com/resource/en/datasheet/dm00140895.pdf) | I2C |
| [LSM6DS3 accelerometer](https://www.st.com/resource/en/datasheet/lsm6ds3.pdf) | I2C |
| [LSM6DSOX accelerometer](https://www.st.com/resource/en/datasheet/lsm6dsox.pdf) | I2C |
| [LSM303AGR accelerometer](https://www.st.com/resource/en/datasheet/lsm303agr.pdf) | I2C |
| [LSM9DS1 accelerometer](https://www.st.com/resource/en/datasheet/lsm9ds1.pdf) | I2C |
| [MAG3110 magnetometer](https://www.nxp.com/docs/en/data-sheet/MAG3110.pdf) | I2C |
| [MAX7219 & MAX7221 display driver](https://datasheets.maximintegrated.com/en/ds/MAX7219-MAX7221.pdf) | SPI |
| [MCP2515 Stand-Alone CAN Controller with SPI Interface](https://ww1.microchip.com/downloads/en/DeviceDoc/MCP2515-Family-Data-Sheet-DS20001801K.pdf) | SPI |
| [MCP3008 analog to digital converter (ADC)](http://ww1.microchip.com/downloads/en/DeviceDoc/21295d.pdf) | SPI |
| [MCP23017 port expander](https://ww1.microchip.com/downloads/en/DeviceDoc/20001952C.pdf) | I2C |
| [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 |
| [P1AM-100 Base Controller](https://facts-engineering.github.io/modules/P1AM-100/P1AM-100.html) | SPI |
| [PCD8544 display](http://eia.udg.edu/~forest/PCD8544_1.pdf) | SPI |
| [PCF8563 real time clock](https://www.nxp.com/docs/en/data-sheet/PCF8563.pdf) | I2C |
| [Resistive Touchscreen (4-wire)](http://ww1.microchip.com/downloads/en/Appnotes/doc8091.pdf) | GPIO |
| [RTL8720DN 2.4G/5G Dual Bands Wireless and BLE5.0](https://www.seeedstudio.com/Realtek8720DN-2-4G-5G-Dual-Bands-Wireless-and-BLE5-0-Combo-Module-p-4442.html) | UART |
| [Semihosting](https://wiki.segger.com/Semihosting) | Debug |
| [Servo](https://learn.sparkfun.com/tutorials/hobby-servo-tutorial/all) | PWM |
| [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 |
| 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 |
| [AHT20 I2C Temperature and Humidity Sensor](http://www.aosong.com/userfiles/files/media/AHT20%20%E8%8B%B1%E6%96%87%E7%89%88%E8%AF%B4%E6%98%8E%E4%B9%A6%20A0%2020201222.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 |
| [APDS9960 Digital proximity, ambient light, RGB and gesture sensor](https://cdn.sparkfun.com/assets/learn_tutorials/3/2/1/Avago-APDS-9960-datasheet.pdf) | I2C |
| [AT24CX 2-wire serial EEPROM](https://www.openimpulse.com/blog/wp-content/uploads/wpsc/downloadables/24C32-Datasheet.pdf) | I2C |
| [AXP192 single Cell Li-Battery and Power System Management](https://github.com/m5stack/M5-Schematic/blob/master/Core/AXP192%20Datasheet_v1.1_en_draft_2211.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 |
| [BMI160 accelerometer/gyroscope](https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bmi160-ds000.pdf) | SPI |
| [BMP180 barometer](https://cdn-shop.adafruit.com/datasheets/BST-BMP180-DS000-09.pdf) | I2C |
| [BMP280 temperature/barometer](https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bmp280-ds001.pdf) | I2C |
| [BMP388 pressure sensor](https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bmp388-ds001.pdf) | I2C |
| [Buzzer](https://en.wikipedia.org/wiki/Buzzer#Piezoelectric) | GPIO |
| [DHTXX thermometer and humidity sensor](https://cdn-shop.adafruit.com/datasheets/Digital+humidity+and+temperature+sensor+AM2302.pdf) | 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 |
| [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 |
| [FT6336 touch controller](https://focuslcds.com/content/FT6236.pdf) | I2C |
| [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/I2C |
| [HTS221 digital humidity and temperature sensor](https://www.st.com/resource/en/datasheet/hts221.pdf) | I2C |
| [HUB75 RGB led matrix](https://cdn-learn.adafruit.com/downloads/pdf/32x16-32x32-rgb-led-matrix.pdf) | SPI |
| [software I2C driver](https://www.ti.com/lit/an/slva704/slva704.pdf) | GPIO |
| [ILI9341 TFT color display](https://cdn-shop.adafruit.com/datasheets/ILI9341.pdf) | SPI |
| [INA260 Volt/Amp/Power meter](https://www.ti.com/lit/ds/symlink/ina260.pdf) | I2C |
| [Infrared remote control](https://en.wikipedia.org/wiki/Consumer_IR) | GPIO |
| [IS31FL3731 matrix LED driver](https://www.lumissil.com/assets/pdf/core/IS31FL3731_DS.pdf) | I2C |
| [4x4 Membrane Keypad](https://cdn.sparkfun.com/assets/f/f/a/5/0/DS-16038.pdf) | GPIO |
| [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 |
| [LIS2MDL magnetometer](https://www.st.com/resource/en/datasheet/lis2mdl.pdf) | I2C |
| [LIS3DH accelerometer](https://www.st.com/resource/en/datasheet/lis3dh.pdf) | I2C |
| [LPS22HB MEMS nano pressure sensor](https://www.st.com/resource/en/datasheet/dm00140895.pdf) | I2C |
| [LSM6DS3 accelerometer](https://www.st.com/resource/en/datasheet/lsm6ds3.pdf) | I2C |
| [LSM6DSOX accelerometer](https://www.st.com/resource/en/datasheet/lsm6dsox.pdf) | I2C |
| [LSM6DS3TR accelerometer](https://www.st.com/resource/en/datasheet/lsm6ds3tr.pdf) | I2C |
| [LSM303AGR accelerometer](https://www.st.com/resource/en/datasheet/lsm303agr.pdf) | I2C |
| [LSM9DS1 accelerometer](https://www.st.com/resource/en/datasheet/lsm9ds1.pdf) | I2C |
| [Makey Button](https://makeymakey.com/) | GPIO |
| [MAG3110 magnetometer](https://www.nxp.com/docs/en/data-sheet/MAG3110.pdf) | I2C |
| [MAX7219 & MAX7221 display driver](https://datasheets.maximintegrated.com/en/ds/MAX7219-MAX7221.pdf) | SPI |
| [MCP2515 Stand-Alone CAN Controller with SPI Interface](https://ww1.microchip.com/downloads/en/DeviceDoc/MCP2515-Family-Data-Sheet-DS20001801K.pdf) | SPI |
| [MCP3008 analog to digital converter (ADC)](http://ww1.microchip.com/downloads/en/DeviceDoc/21295d.pdf) | SPI |
| [MCP23017 port expander](https://ww1.microchip.com/downloads/en/DeviceDoc/20001952C.pdf) | I2C |
| [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 |
| [P1AM-100 Base Controller](https://facts-engineering.github.io/modules/P1AM-100/P1AM-100.html) | SPI |
| [PCD8544 display](http://eia.udg.edu/~forest/PCD8544_1.pdf) | SPI |
| [PCF8563 real time clock](https://www.nxp.com/docs/en/data-sheet/PCF8563.pdf) | I2C |
| [Resistive Touchscreen (4-wire)](http://ww1.microchip.com/downloads/en/Appnotes/doc8091.pdf) | GPIO |
| [RTL8720DN 2.4G/5G Dual Bands Wireless and BLE5.0](https://www.seeedstudio.com/Realtek8720DN-2-4G-5G-Dual-Bands-Wireless-and-BLE5-0-Combo-Module-p-4442.html) | UART |
| [SCD4x CO2 Sensor](https://sensirion.com/media/documents/C4B87CE6/627C2DCD/CD_DS_SCD40_SCD41_Datasheet_D1.pdf) | I2C |
| [Semihosting](https://wiki.segger.com/Semihosting) | Debug |
| [Servo](https://learn.sparkfun.com/tutorials/hobby-servo-tutorial/all) | PWM |
| [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/2_Humidity_Sensors/Datasheets/Sensirion_Humidity_Sensors_SHT3x_Datasheet_digital.pdf) | I2C |
| [SHTC3 Digital Humidity Sensor (RH/T)](https://www.sensirion.com/fileadmin/user_upload/customers/sensirion/Dokumente/2_Humidity_Sensors/Datasheets/Sensirion_Humidity_Sensors_SHTC3_Datasheet.pdf) | I2C |
| [SPI NOR Flash Memory](https://en.wikipedia.org/wiki/Flash_memory#NOR_flash) | SPI/QSPI |
| [SPI SDCARD/MMC](https://en.wikipedia.org/wiki/SD_card) | SPI |
| [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 |
| [SSD1351 OLED display](https://download.mikroe.com/documents/datasheets/ssd1351-revision-1.3.pdf) | 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 |
| [TM1637 7-segment LED display](https://www.mcielectronics.cl/website_MCI/static/documents/Datasheet_TM1637.pdf) | I2C |
| [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" (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 |
| [Waveshare 4.2" e-paper B/W display](https://www.waveshare.com/w/upload/6/6a/4.2inch-e-paper-specification.pdf) | SPI |
| [WS2812 RGB LED](https://cdn-shop.adafruit.com/datasheets/WS2812.pdf) | GPIO |
| [XPT2046 touch controller](http://grobotronics.com/images/datasheets/xpt2046-datasheet.pdf) | GPIO |
| [Semtech SX126x Lora](https://www.semtech.com/products/wireless-rf/lora-transceiv-ers/sx1261) | SPI |
| [SHTC3 Digital Humidity Sensor (RH/T)](https://www.sensirion.com/fileadmin/user_upload/customers/sensirion/Dokumente/2_Humidity_Sensors/Datasheets/Sensirion_Humidity_Sensors_SHTC3_Datasheet.pdf) | I2C |
| [SPI NOR Flash Memory](https://en.wikipedia.org/wiki/Flash_memory#NOR_flash) | SPI/QSPI |
| [SPI SDCARD/MMC](https://en.wikipedia.org/wiki/SD_card) | SPI |
| [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 |
| [SSD1351 OLED display](https://download.mikroe.com/documents/datasheets/ssd1351-revision-1.3.pdf) | 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 |
| [TM1637 7-segment LED display](https://www.mcielectronics.cl/website_MCI/static/documents/Datasheet_TM1637.pdf) | I2C |
| [TMP102 I2C Temperature Sensor](https://download.mikroe.com/documents/datasheets/tmp102-data-sheet.pdf) | I2C |
| [UC8151 All-in-one driver IC for ESL](https://www.buydisplay.com/download/ic/UC8151C.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" (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 |
| [Waveshare 2.9" e-paper display (V1)](https://www.waveshare.com/w/upload/e/e6/2.9inch_e-Paper_Datasheet.pdf) | SPI |
| [Waveshare 4.2" e-paper B/W display](https://www.waveshare.com/w/upload/6/6a/4.2inch-e-paper-specification.pdf) | SPI |
| [WS2812 RGB LED](https://cdn-shop.adafruit.com/datasheets/WS2812.pdf) | GPIO |
| [XPT2046 touch controller](http://grobotronics.com/images/datasheets/xpt2046-datasheet.pdf) | GPIO |
| [Semtech SX126x Lora](https://www.semtech.com/products/wireless-rf/lora-transceiv-ers/sx1261) | SPI |
| [SSD1289 TFT color display](http://aitendo3.sakura.ne.jp/aitendo_data/product_img/lcd/tft2/M032C1289TP/3.2-SSD1289.pdf) | GPIO |
## Contributing
+3 -27
View File
@@ -1,3 +1,6 @@
//go:build tinygo
// +build tinygo
// Package dht provides a driver for DHTXX family temperature and humidity sensors.
//
// [1] Datasheet DHT11: https://www.mouser.com/datasheet/2/758/DHT11-Technical-Data-Sheet-Translated-Version-1143054.pdf
@@ -7,34 +10,10 @@
package dht // import "tinygo.org/x/drivers/dht"
import (
"encoding/binary"
"machine"
"time"
)
// enum type for device type
type DeviceType uint8
// DeviceType specific parsing of information received from the sensor
func (d DeviceType) extractData(buf []byte) (temp int16, hum uint16) {
if d == DHT11 {
temp = int16(buf[2])
if buf[3]&0x80 > 0 {
temp = -1 - temp
}
temp *= 10
temp += int16(buf[3] & 0x0f)
hum = 10*uint16(buf[0]) + uint16(buf[1])
} else {
hum = binary.LittleEndian.Uint16(buf[0:2])
temp = int16(buf[3])<<8 + int16(buf[2]&0x7f)
if buf[2]&0x80 > 0 {
temp = -temp
}
}
return
}
// Celsius and Fahrenheit temperature scales
type TemperatureScale uint8
@@ -54,9 +33,6 @@ const (
startTimeout = time.Millisecond * 200
startingLow = time.Millisecond * 20
DHT11 DeviceType = iota
DHT22
C TemperatureScale = iota
F
+44
View File
@@ -0,0 +1,44 @@
package dht
import (
"encoding/binary"
)
// DeviceType is the enum type for device type
type DeviceType uint8
const (
DHT11 DeviceType = iota
DHT22
)
// extractData parses information received from the sensor.
// The 2 first buffers are for the humidity and
// the 2 following corresponds to the temperature.
func (d DeviceType) extractData(buf []byte) (temp int16, hum uint16) {
switch d {
case DHT11:
hum = 10*uint16(buf[0]) + uint16(buf[1])
temp = int16(buf[2])
if buf[3]&0x80 > 0 {
temp = -1 - temp
}
temp *= 10
temp += int16(buf[3] & 0x0f)
case DHT22:
hum = binary.BigEndian.Uint16(buf[0:2])
temp = int16(buf[2]&0x7f)<<8 + int16(buf[3])
// the first bit corresponds to the sign bit
if buf[2]&0x80 > 0 {
temp = -temp
}
default:
// keeping this for retro-compatibility but not tested
hum = binary.LittleEndian.Uint16(buf[0:2])
temp = int16(buf[3])<<8 + int16(buf[2]&0x7f)
if buf[2]&0x80 > 0 {
temp = -temp
}
}
return
}
+46
View File
@@ -0,0 +1,46 @@
package dht
import (
"testing"
)
func TestDeviceType_extractData(t *testing.T) {
bitStr := "0000001010001100000000010101111111101110"
buf := bitStringToBytes(bitStr)
tt := []struct {
name string
d DeviceType
buf []byte
wantTemp int16
wantHum uint16
}{
{
// temp = 35.1C hum = 65.2%
name: "DHT22", d: DHT22, buf: buf, wantTemp: 351, wantHum: 652,
},
}
for _, tc := range tt {
t.Run(tc.name, func(t *testing.T) {
gotTemp, gotHum := tc.d.extractData(tc.buf)
if gotTemp != tc.wantTemp {
t.Errorf("extractData() gotTemp = %v, want %v", gotTemp, tc.wantTemp)
}
if gotHum != tc.wantHum {
t.Errorf("extractData() gotHum = %v, want %v", gotHum, tc.wantHum)
}
})
}
}
func bitStringToBytes(s string) []byte {
b := make([]byte, (len(s)+(8-1))/8)
for i, r := range s {
if r < '0' || r > '1' {
panic("not in range")
}
b[i>>3] |= byte(r-'0') << uint(7-i&7)
}
return b
}
+3
View File
@@ -1,3 +1,6 @@
//go:build tinygo
// +build tinygo
// Package dht provides a driver for DHTXX family temperature and humidity sensors.
//
// [1] Datasheet DHT11: https://www.mouser.com/datasheet/2/758/DHT11-Technical-Data-Sheet-Translated-Version-1143054.pdf
+3
View File
@@ -1,3 +1,6 @@
//go:build tinygo
// +build tinygo
// Package dht provides a driver for DHTXX family temperature and humidity sensors.
//
// [1] Datasheet DHT11: https://www.mouser.com/datasheet/2/758/DHT11-Technical-Data-Sheet-Translated-Version-1143054.pdf
+3
View File
@@ -1,3 +1,6 @@
//go:build tinygo
// +build tinygo
package dht // import "tinygo.org/x/drivers/dht"
import (
+138 -22
View File
@@ -2,28 +2,76 @@
package easystepper // import "tinygo.org/x/drivers/easystepper"
import (
"errors"
"machine"
"time"
)
// StepMode determines the coil sequence used to perform a single step
type StepMode uint8
// Valid values for StepMode
const (
// ModeFour uses a 'four step' coil sequence (12-23-34-41). This is the default (zero-value) mode
ModeFour StepMode = iota
// ModeEight uses an 'eight step' coil sequence (1-12-2-23-3-34-4-41)
ModeEight
)
// stepCount is a helper function to return the number of steps in a StepMode sequence
func (sm StepMode) stepCount() uint {
switch sm {
default:
fallthrough
case ModeFour:
return 4
case ModeEight:
return 8
}
}
// DeviceConfig contains the configuration data for a single easystepper driver
type DeviceConfig struct {
// Pin1 ... Pin4 determines the pins to configure and use for the device
Pin1, Pin2, Pin3, Pin4 machine.Pin
// StepCount is the number of steps required to perform a full revolution of the stepper motor
StepCount uint
// RPM determines the speed of the stepper motor in 'Revolutions per Minute'
RPM uint
// Mode determines the coil sequence used to perform a single step
Mode StepMode
}
// DualDeviceConfig contains the configuration data for a dual easystepper driver
type DualDeviceConfig struct {
DeviceConfig
// Pin5 ... Pin8 determines the pins to configure and use for the second device
Pin5, Pin6, Pin7, Pin8 machine.Pin
}
// Device holds the pins and the delay between steps
type Device struct {
pins [4]machine.Pin
stepDelay int32
stepDelay time.Duration
stepNumber uint8
stepMode StepMode
}
// DualDevice holds information for controlling 2 motors
type DualDevice struct {
devices [2]Device
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 {
return Device{
pins: [4]machine.Pin{pin1, pin2, pin3, pin4},
stepDelay: 60000000 / (steps * rpm),
// New returns a new single easystepper driver given a DeviceConfig
func New(config DeviceConfig) (*Device, error) {
if config.StepCount == 0 || config.RPM == 0 {
return nil, errors.New("config.StepCount and config.RPM must be > 0")
}
return &Device{
pins: [4]machine.Pin{config.Pin1, config.Pin2, config.Pin3, config.Pin4},
stepDelay: time.Second * 60 / time.Duration((config.StepCount * config.RPM)),
stepMode: config.Mode,
}, nil
}
// Configure configures the pins of the Device
@@ -34,17 +82,23 @@ func (d *Device) Configure() {
}
// 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),
func NewDual(config DualDeviceConfig) (*DualDevice, error) {
// Create the first device
dev1, err := New(config.DeviceConfig)
if err != nil {
return nil, err
}
dual.devices[1] = Device{
pins: [4]machine.Pin{pin5, pin6, pin7, pin8},
stepDelay: 60000000 / (steps * rpm),
// Create the second device
config.DeviceConfig.Pin1 = config.Pin5
config.DeviceConfig.Pin2 = config.Pin6
config.DeviceConfig.Pin3 = config.Pin7
config.DeviceConfig.Pin4 = config.Pin8
dev2, err := New(config.DeviceConfig)
if err != nil {
return nil, err
}
return dual
// Return composite dual device
return &DualDevice{devices: [2]*Device{dev1, dev2}}, nil
}
// Configure configures the pins of the DualDevice
@@ -64,7 +118,7 @@ func (d *Device) Move(steps int32) {
var s int32
d.stepMotor(d.stepNumber)
for s = int32(d.stepNumber); s < steps; s++ {
time.Sleep(time.Duration(d.stepDelay) * time.Microsecond)
time.Sleep(d.stepDelay)
d.moveDirectionSteps(direction, s)
}
}
@@ -101,7 +155,7 @@ func (d *DualDevice) Move(stepsA, stepsB int32) {
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)
time.Sleep(d.devices[0].stepDelay)
d.devices[max].moveDirectionSteps(directions[max], s)
if ((s * stepsB) / stepsA) > minStep {
@@ -119,6 +173,18 @@ func (d *DualDevice) Off() {
// stepMotor changes the pins' state to the correct step
func (d *Device) stepMotor(step uint8) {
switch d.stepMode {
default:
fallthrough
case ModeFour:
d.stepMotor4(step)
case ModeEight:
d.stepMotor8(step)
}
}
// stepMotor4 changes the pins' state to the correct step in 4-step mode
func (d *Device) stepMotor4(step uint8) {
switch step {
case 0:
d.pins[0].High()
@@ -148,13 +214,63 @@ func (d *Device) stepMotor(step uint8) {
d.stepNumber = step
}
// stepMotor8 changes the pins' state to the correct step in 8-step mode
func (d *Device) stepMotor8(step uint8) {
switch step {
case 0:
d.pins[0].High()
d.pins[2].Low()
d.pins[1].Low()
d.pins[3].Low()
case 1:
d.pins[0].High()
d.pins[2].High()
d.pins[1].Low()
d.pins[3].Low()
case 2:
d.pins[0].Low()
d.pins[2].High()
d.pins[1].Low()
d.pins[3].Low()
case 3:
d.pins[0].Low()
d.pins[2].High()
d.pins[1].High()
d.pins[3].Low()
case 4:
d.pins[0].Low()
d.pins[2].Low()
d.pins[1].High()
d.pins[3].Low()
case 5:
d.pins[0].Low()
d.pins[2].Low()
d.pins[1].High()
d.pins[3].High()
case 6:
d.pins[0].Low()
d.pins[2].Low()
d.pins[1].Low()
d.pins[3].High()
case 7:
d.pins[0].High()
d.pins[2].Low()
d.pins[1].Low()
d.pins[3].High()
}
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, ...
// Direction true: (4-step mode) 0, 1, 2, 3, 0, 1, 2, ...
// Direction false: (4-step mode) 0, 3, 2, 1, 0, 3, 2, ...
// Direction true: (8-step mode) 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, ...
// Direction false: (8-step mode) 0, 7, 6, 5, 4, 3, 2, 1, 0, 7, 6, ...
func (d *Device) moveDirectionSteps(direction bool, step int32) {
modulus := int32(d.stepMode.stepCount())
if direction {
d.stepMotor(uint8(step % 4))
d.stepMotor(uint8(step % modulus))
} else {
d.stepMotor(uint8((step + 2*(step%2)) % 4))
d.stepMotor(uint8(((-step % modulus) + modulus) % modulus))
}
}
+1 -1
View File
@@ -12,7 +12,7 @@ func (d *Device) ConnectToAccessPoint(ssid, pass string, timeout time.Duration)
}
d.SetWifiMode(WifiModeClient)
return d.ConnectToAP(ssid, pass, 10)
return d.ConnectToAP(ssid, pass, int(timeout.Seconds()))
}
func (d *Device) Disconnect() error {
+1 -1
View File
@@ -12,7 +12,7 @@ func main() {
dhtSensor := dht.New(pin, dht.DHT11)
for {
temp, hum, err := dhtSensor.Measurements()
if err != nil {
if err == nil {
fmt.Printf("Temperature: %02d.%d°C, Humidity: %02d.%d%%\n", temp/10, temp%10, hum/10, hum%10)
} else {
fmt.Printf("Could not take measurements from the sensor: %s\n", err.Error())
+5 -1
View File
@@ -8,7 +8,11 @@ import (
)
func main() {
motor := easystepper.New(machine.P13, machine.P15, machine.P14, machine.P16, 200, 75)
config := easystepper.DeviceConfig{
Pin1: machine.P13, Pin2: machine.P15, Pin3: machine.P14, Pin4: machine.P16,
StepCount: 200, RPM: 75, Mode: easystepper.ModeFour,
}
motor, _ := easystepper.New(config)
motor.Configure()
for {
+42
View File
@@ -0,0 +1,42 @@
//go:build feather_m0 || feather_m4 || feather_m4_can || feather_nrf52840 || feather_nrf52840_sense || feather_stm32f405 || feather_rp2040
// +build feather_m0 feather_m4 feather_m4_can feather_nrf52840 feather_nrf52840_sense feather_stm32f405 feather_rp2040
package initdisplay
import (
"machine"
"tinygo.org/x/drivers/ili9341"
)
func InitDisplay() *ili9341.Device {
machine.D5.Configure(machine.PinConfig{Mode: machine.PinOutput})
machine.D6.Configure(machine.PinConfig{Mode: machine.PinOutput})
machine.SPI0.Configure(machine.SPIConfig{
SCK: machine.SPI0_SCK_PIN,
SDO: machine.SPI0_SDO_PIN,
SDI: machine.SPI0_SDI_PIN,
Frequency: 40000000,
})
// configure backlight
backlight := machine.D9
backlight.Configure(machine.PinConfig{machine.PinOutput})
display := ili9341.NewSPI(
machine.SPI0,
machine.D10, // LCD_DC,
machine.D11, // LCD_SS_PIN,
machine.D12, // LCD_RESET,
)
// configure display
display.Configure(ili9341.Config{})
backlight.High()
display.SetRotation(ili9341.Rotation270)
return display
}
+4 -2
View File
@@ -72,7 +72,8 @@ func drawPng(display *ili9341.Device) error {
}
})
return png.Decode(p)
_, err := png.Decode(p)
return err
}
func drawJpeg(display *ili9341.Device) error {
@@ -84,7 +85,8 @@ func drawJpeg(display *ili9341.Device) error {
}
})
return jpeg.Decode(p)
_, err := jpeg.Decode(p)
return err
}
func errorMessage(err error) {
+58
View File
@@ -0,0 +1,58 @@
package main
import (
"machine"
"time"
"tinygo.org/x/drivers/irremote"
)
var irCmdButtons = map[uint16]string{
0x45: "POWER",
0x47: "FUNC/STOP",
0x46: "VOL+",
0x44: "FAST BACK",
0x40: "PAUSE",
0x43: "FAST FORWARD",
0x07: "DOWN",
0x15: "VOL-",
0x09: "UP",
0x19: "EQ",
0x0D: "ST/REPT",
0x16: "0",
0x0C: "1",
0x18: "2",
0x5E: "3",
0x08: "4",
0x1C: "5",
0x5A: "6",
0x42: "7",
0x52: "8",
0x4A: "9",
}
var (
pinIRIn = machine.GP26
ir irremote.ReceiverDevice
)
func setupPins() {
ir = irremote.NewReceiver(pinIRIn)
ir.Configure()
}
func irCallback(data irremote.Data) {
msg := "Command: " + irCmdButtons[data.Command]
if data.Flags&irremote.DataFlagIsRepeat != 0 {
msg = msg + " (REPEAT)"
}
println(msg)
}
func main() {
setupPins()
ir.SetCommandHandler(irCallback)
for {
time.Sleep(time.Millisecond * 10)
}
}
+51
View File
@@ -0,0 +1,51 @@
package main
import (
"time"
"machine"
"tinygo.org/x/drivers/is31fl3731"
)
// I2CAddress -- address of led matrix
var I2CAddress uint8 = is31fl3731.I2C_ADDRESS_74
func main() {
bus := machine.I2C0
err := bus.Configure(machine.I2CConfig{})
if err != nil {
println("could not configure I2C:", err)
return
}
// Create driver for Adafruit 15x7 CharliePlex LED Matrix FeatherWing
// (CharlieWing): https://www.adafruit.com/product/3163
ledMatrix := is31fl3731.NewAdafruitCharlieWing15x7(bus, I2CAddress)
err = ledMatrix.Configure()
if err != nil {
println("could not configure is31fl3731 driver:", err)
return
}
// Fill the whole matrix on the frame #0 (visible by default)
ledMatrix.Fill(is31fl3731.FRAME_0, uint8(3))
// Draw couple pixels on the frame #1 (not visible yet)
ledMatrix.DrawPixelXY(is31fl3731.FRAME_1, uint8(0), uint8(0), uint8(10))
ledMatrix.DrawPixelXY(is31fl3731.FRAME_1, uint8(14), uint8(6), uint8(10))
// There are 8 frames available, it's a good idea to draw on an invisible
// frame and then switch to that frame to reduce flickering. Switch between
// frame #0 and #1 in a loop to show animation:
for {
println("show frame #0...")
ledMatrix.SetActiveFrame(is31fl3731.FRAME_0)
time.Sleep(time.Second * 3)
println("show frame #1...")
ledMatrix.SetActiveFrame(is31fl3731.FRAME_1)
time.Sleep(time.Second * 3)
}
}
+39
View File
@@ -0,0 +1,39 @@
package main
import (
"machine"
"time"
"tinygo.org/x/drivers/l3gd20"
)
func main() {
const (
// Default address on most breakout boards.
pcaAddr = 0x40
)
bus := machine.I2C0
err := bus.Configure(machine.I2CConfig{})
if err != nil {
panic(err.Error())
}
gyro := l3gd20.NewI2C(bus, 105)
err = gyro.Configure(l3gd20.Config{Range: l3gd20.Range_250})
if err != nil {
println(err.Error())
}
var x, y, z int32
for {
err = gyro.Update()
if err != nil {
println(err.Error())
}
x, y, z = gyro.AngularVelocity()
println(x, y, z)
time.Sleep(500 * time.Millisecond)
}
}
+16 -8
View File
@@ -13,7 +13,13 @@ func main() {
machine.I2C0.Configure(machine.I2CConfig{})
sensor := lsm303agr.New(machine.I2C0)
sensor.Configure(lsm303agr.Configuration{}) //default settings
err := sensor.Configure(lsm303agr.Configuration{}) //default settings
if err != nil {
for {
println("Failed to configure", err.Error())
time.Sleep(time.Second)
}
}
// you can specify the following options to adjust accuracy, sensor range or save power.
// see https://github.com/tinygo-org/drivers/blob/release/lsm303agr/registers.go for details:
@@ -28,22 +34,24 @@ func main() {
})
*/
if !sensor.Connected() {
println("LSM303AGR/MAG not connected!")
return
}
for {
if !sensor.Connected() {
println("LSM303AGR/MAG not connected!")
time.Sleep(time.Second)
continue
}
// accel_x, accel_y, accel_z := sensor.ReadAcceleration()
// println("ACCEL_X:", accel_x/100000, " ACCEL_Y:", accel_y/100000, " ACCEL_Z:", accel_z/100000)
// mag_x, mag_y, mag_z := sensor.ReadMagneticField()
// println("MAG_X:", mag_x/100000, " MAG_Y:", mag_y/100000, " MAG_Z:", mag_z/100000)
pitch, roll := sensor.ReadPitchRoll()
pitch, roll, _ := sensor.ReadPitchRoll()
println("Pitch:", float32(pitch)/100000, " Roll:", float32(roll)/100000)
heading := sensor.ReadCompass()
heading, _ := sensor.ReadCompass()
println("Heading:", float32(heading)/100000, "degrees")
temp, _ := sensor.ReadTemperature()
+13 -6
View File
@@ -12,16 +12,23 @@ func main() {
machine.I2C0.Configure(machine.I2CConfig{})
accel := lsm6ds3.New(machine.I2C0)
accel.Configure(lsm6ds3.Configuration{})
if !accel.Connected() {
println("LSM6DS3 not connected")
return
err := accel.Configure(lsm6ds3.Configuration{})
if err != nil {
for {
println("Failed to configure", err.Error())
time.Sleep(time.Second)
}
}
for {
x, y, z := accel.ReadAcceleration()
if !accel.Connected() {
println("LSM6DS3 not connected")
time.Sleep(time.Second)
continue
}
x, y, z, _ := accel.ReadAcceleration()
println("Acceleration:", float32(x)/1000000, float32(y)/1000000, float32(z)/1000000)
x, y, z = accel.ReadRotation()
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")
+37
View File
@@ -0,0 +1,37 @@
// Connects to an LSM6DS3TR I2C a 6 axis Inertial Measurement Unit (IMU)
package main
import (
"machine"
"time"
"tinygo.org/x/drivers/lsm6ds3tr"
)
func main() {
machine.I2C0.Configure(machine.I2CConfig{})
accel := lsm6ds3tr.New(machine.I2C0)
err := accel.Configure(lsm6ds3tr.Configuration{})
if err != nil {
for {
println("Failed to configure", err.Error())
time.Sleep(time.Second)
}
}
for {
if !accel.Connected() {
println("LSM6DS3TR not connected")
time.Sleep(time.Second)
continue
}
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)
}
}
+10 -4
View File
@@ -26,12 +26,18 @@ func main() {
machine.I2C0.Configure(machine.I2CConfig{})
device := lsm6dsox.New(machine.I2C0)
device.Configure(lsm6dsox.Configuration{
err := device.Configure(lsm6dsox.Configuration{
AccelRange: lsm6dsox.ACCEL_2G,
AccelSampleRate: lsm6dsox.ACCEL_SR_104,
GyroRange: lsm6dsox.GYRO_250DPS,
GyroSampleRate: lsm6dsox.GYRO_SR_104,
})
if err != nil {
for {
println("Failed to configure", err.Error())
time.Sleep(time.Second)
}
}
for {
@@ -46,8 +52,8 @@ func main() {
calibrateGyro(device)
}
ax, ay, az := device.ReadAcceleration()
gx, gy, gz := device.ReadRotation()
ax, ay, az, _ := device.ReadAcceleration()
gx, gy, gz, _ := device.ReadRotation()
t, _ := device.ReadTemperature()
if PLOTTER {
@@ -64,7 +70,7 @@ func main() {
func calibrateGyro(device *lsm6dsox.Device) {
for i := 0; i < 100; i++ {
gx, gy, gz := device.ReadRotation()
gx, gy, gz, _ := device.ReadRotation()
cal[0] += float32(gx) / 1000000
cal[1] += float32(gy) / 1000000
cal[2] += float32(gz) / 1000000
+1 -1
View File
@@ -41,7 +41,7 @@ func main() {
for {
if con, err := device.Connected(); !con || err != nil {
if !device.Connected() {
println("LSM9DS1 not connected")
time.Sleep(time.Second)
continue
+30
View File
@@ -0,0 +1,30 @@
package main
import (
"machine"
"time"
"tinygo.org/x/drivers/makeybutton"
)
var (
led machine.Pin = machine.LED
button machine.Pin = machine.BUTTON
key *makeybutton.Button
)
func main() {
led.Configure(machine.PinConfig{Mode: machine.PinOutput})
key = makeybutton.NewButton(button)
key.Configure()
for {
switch key.Get() {
case makeybutton.Pressed:
led.High()
case makeybutton.Released:
led.Low()
}
time.Sleep(100 * time.Millisecond)
}
}
+1
View File
@@ -29,6 +29,7 @@ func main() {
driver.StopDisplayTest()
driver.SetDecodeMode(4)
driver.SetScanLimit(4)
driver.SetIntensity(8)
driver.StopShutdownMode()
for i := 1; i < int(digitNumber); i++ {
+40
View File
@@ -0,0 +1,40 @@
package main
import (
"fmt"
"time"
"tinygo.org/x/drivers/examples/rtl8720dn"
)
var (
debug = false
)
func main() {
err := run()
for err != nil {
fmt.Printf("error: %s\r\n", err.Error())
time.Sleep(5 * time.Second)
}
}
func run() error {
//rtl8720dn.Debug(true)
rtl, err := rtl8720dn.Setup()
if err != nil {
return err
}
ver, err := rtl.Version()
if err != nil {
return nil
}
for {
fmt.Printf("RTL8270DN Firmware Version: %s\r\n", ver)
time.Sleep(10 * time.Second)
}
return nil
}
+79
View File
@@ -0,0 +1,79 @@
//go:build wioterminal
// +build wioterminal
package rtl8720dn
import (
"device/sam"
"machine"
"runtime/interrupt"
"time"
"tinygo.org/x/drivers/rtl8720dn"
)
var (
uart UARTx
debug bool
)
func handleInterrupt(interrupt.Interrupt) {
// should reset IRQ
uart.Receive(byte((uart.Bus.DATA.Get() & 0xFF)))
uart.Bus.INTFLAG.SetBits(sam.SERCOM_USART_INT_INTFLAG_RXC)
}
func Setup() (*rtl8720dn.RTL8720DN, error) {
machine.RTL8720D_CHIP_PU.Configure(machine.PinConfig{Mode: machine.PinOutput})
machine.RTL8720D_CHIP_PU.Low()
time.Sleep(100 * time.Millisecond)
machine.RTL8720D_CHIP_PU.High()
time.Sleep(1000 * time.Millisecond)
if debug {
waitSerial()
}
uart = UARTx{
UART: &machine.UART{
Buffer: machine.NewRingBuffer(),
Bus: sam.SERCOM0_USART_INT,
SERCOM: 0,
},
}
uart.Interrupt = interrupt.New(sam.IRQ_SERCOM0_2, handleInterrupt)
uart.Configure(machine.UARTConfig{TX: machine.PB24, RX: machine.PC24, BaudRate: 614400})
rtl := rtl8720dn.New(uart)
rtl.Debug(debug)
_, err := rtl.Rpc_tcpip_adapter_init()
if err != nil {
return nil, err
}
return rtl, nil
}
// Wait for user to open serial console
func waitSerial() {
for !machine.Serial.DTR() {
time.Sleep(100 * time.Millisecond)
}
}
type UARTx struct {
*machine.UART
}
func (u UARTx) Read(p []byte) (n int, err error) {
if u.Buffered() == 0 {
time.Sleep(1 * time.Millisecond)
return 0, nil
}
return u.UART.Read(p)
}
func Debug(b bool) {
debug = b
}
+39
View File
@@ -0,0 +1,39 @@
package main
import (
"machine"
"time"
"tinygo.org/x/drivers/scd4x"
)
var (
i2c = machine.I2C0
sensor = scd4x.New(i2c)
)
func main() {
time.Sleep(1500 * time.Millisecond)
i2c.Configure(machine.I2CConfig{})
if err := sensor.Configure(); err != nil {
println(err)
}
time.Sleep(1500 * time.Millisecond)
if err := sensor.StartPeriodicMeasurement(); err != nil {
println(err)
}
time.Sleep(1500 * time.Millisecond)
for {
co2, err := sensor.ReadCO2()
if err != nil {
println(err)
}
println("CO2", co2)
time.Sleep(time.Second)
}
}
+1 -1
View File
@@ -8,7 +8,7 @@ import (
)
func init() {
spi = machine.SPI0
spi = &machine.SPI0
sckPin = machine.SPI0_SCK_PIN
sdoPin = machine.SPI0_SDO_PIN
sdiPin = machine.SPI0_SDI_PIN
+1 -1
View File
@@ -8,7 +8,7 @@ import (
)
func init() {
spi = machine.SPI1
spi = &machine.SPI1
sckPin = machine.SDCARD_SCK_PIN
sdoPin = machine.SDCARD_SDO_PIN
sdiPin = machine.SDCARD_SDI_PIN
+1 -1
View File
@@ -8,7 +8,7 @@ import (
)
func init() {
spi = machine.SPI0
spi = &machine.SPI0
sckPin = machine.SPI0_SCK_PIN
sdoPin = machine.SPI0_SDO_PIN
sdiPin = machine.SPI0_SDI_PIN
+1 -9
View File
@@ -9,7 +9,7 @@ import (
)
var (
spi machine.SPI
spi *machine.SPI
sckPin machine.Pin
sdoPin machine.Pin
sdiPin machine.Pin
@@ -18,7 +18,6 @@ var (
)
func main() {
waitSerial()
fmt.Printf("sdcard console\r\n")
led := ledPin
@@ -42,10 +41,3 @@ func main() {
time.Sleep(200 * time.Millisecond)
}
}
// Wait for user to open serial console
func waitSerial() {
for !machine.Serial.DTR() {
time.Sleep(100 * time.Millisecond)
}
}
+1 -1
View File
@@ -8,7 +8,7 @@ import (
)
func init() {
spi = machine.SDCARD_SPI
spi = &machine.SDCARD_SPI
sckPin = machine.SDCARD_SCK_PIN
sdoPin = machine.SDCARD_SDO_PIN
sdiPin = machine.SDCARD_SDI_PIN
+1 -1
View File
@@ -8,7 +8,7 @@ import (
)
func init() {
spi = machine.SPI0
spi = &machine.SPI0
sckPin = machine.SPI0_SCK_PIN
sdoPin = machine.SPI0_SDO_PIN
sdiPin = machine.SPI0_SDI_PIN
+1 -1
View File
@@ -8,7 +8,7 @@ import (
)
func init() {
spi = machine.SPI0
spi = &machine.SPI0
sckPin = machine.SPI0_SCK_PIN
sdoPin = machine.SPI0_SDO_PIN
sdiPin = machine.SPI0_SDI_PIN
@@ -0,0 +1,18 @@
//go:build thingplus_rp2040
// +build thingplus_rp2040
package main
import (
"machine"
)
func init() {
spi = machine.SPI1
sckPin = machine.SPI1_SCK_PIN
sdoPin = machine.SPI1_SDO_PIN
sdiPin = machine.SPI1_SDI_PIN
csPin = machine.GPIO9
ledPin = machine.LED
}
+1 -1
View File
@@ -8,7 +8,7 @@ import (
)
func init() {
spi = machine.SPI2
spi = &machine.SPI2
sckPin = machine.SCK2
sdoPin = machine.SDO2
sdiPin = machine.SDI2
+1 -1
View File
@@ -8,7 +8,7 @@ import (
)
func init() {
spi = machine.SPI0
spi = &machine.SPI0
sckPin = machine.SPI0_SCK_PIN
sdoPin = machine.SPI0_SDO_PIN
sdiPin = machine.SPI0_SDI_PIN
+1 -1
View File
@@ -8,7 +8,7 @@ import (
)
func init() {
spi = machine.SPI1
spi = &machine.SPI1
sckPin = machine.SDCARD_SCK_PIN
sdoPin = machine.SDCARD_SDO_PIN
sdiPin = machine.SDCARD_SDI_PIN
+1 -1
View File
@@ -8,7 +8,7 @@ import (
)
func init() {
spi = machine.SPI0
spi = &machine.SPI0
sckPin = machine.SPI0_SCK_PIN
sdoPin = machine.SPI0_SDO_PIN
sdiPin = machine.SPI0_SDI_PIN
+1 -10
View File
@@ -11,7 +11,7 @@ import (
)
var (
spi machine.SPI
spi *machine.SPI
sckPin machine.Pin
sdoPin machine.Pin
sdiPin machine.Pin
@@ -20,8 +20,6 @@ var (
)
func main() {
waitSerial()
sd := sdcard.New(spi, sckPin, sdoPin, sdiPin, csPin)
err := sd.Configure()
if err != nil {
@@ -40,10 +38,3 @@ func main() {
console.RunFor(&sd, filesystem)
}
// Wait for user to open serial console
func waitSerial() {
for !machine.Serial.DTR() {
time.Sleep(100 * time.Millisecond)
}
}
+1 -1
View File
@@ -8,7 +8,7 @@ import (
)
func init() {
spi = machine.SDCARD_SPI
spi = &machine.SDCARD_SPI
sckPin = machine.SDCARD_SCK_PIN
sdoPin = machine.SDCARD_SDO_PIN
sdiPin = machine.SDCARD_SDI_PIN
+1 -1
View File
@@ -8,7 +8,7 @@ import (
)
func init() {
spi = machine.SPI0
spi = &machine.SPI0
sckPin = machine.SPI0_SCK_PIN
sdoPin = machine.SPI0_SDO_PIN
sdiPin = machine.SPI0_SDI_PIN
+1 -1
View File
@@ -8,7 +8,7 @@ import (
)
func init() {
spi = machine.SPI0
spi = &machine.SPI0
sckPin = machine.SPI0_SCK_PIN
sdoPin = machine.SPI0_SDO_PIN
sdiPin = machine.SPI0_SDI_PIN
@@ -0,0 +1,18 @@
//go:build thingplus_rp2040
// +build thingplus_rp2040
package main
import (
"machine"
)
func init() {
spi = machine.SPI1
sckPin = machine.SPI1_SCK_PIN
sdoPin = machine.SPI1_SDO_PIN
sdiPin = machine.SPI1_SDI_PIN
csPin = machine.GPIO9
ledPin = machine.LED
}
+1 -1
View File
@@ -8,7 +8,7 @@ import (
)
func init() {
spi = machine.SPI2
spi = &machine.SPI2
sckPin = machine.SCK2
sdoPin = machine.SDO2
sdiPin = machine.SDI2
+68
View File
@@ -0,0 +1,68 @@
package main
import (
"image/color"
"machine"
"math/rand"
"tinygo.org/x/drivers/ssd1289"
)
func main() {
//The SSD1289 is configured in 16 bit parallel mode and requires 16 GPIOs
//The Pin bus is the most flexible but ineffecient method it switches
//individual pins on and off. If you are able to use consecutive pins
//consider creating a more efficient bus implementation that uses
//your microcontrollers built in "ports"
//see rp2040bus.go for an example for the rapsberry pi pico
bus := ssd1289.NewPinBus([16]machine.Pin{
machine.GP4, //DB0
machine.GP5, //DB1
machine.GP6, //DB2
machine.GP7, //DB3
machine.GP8, //DB4
machine.GP9, //DB5
machine.GP10, //DB6
machine.GP11, //DB7
machine.GP12, //DB8
machine.GP13, //DB9
machine.GP14, //DB10
machine.GP15, //DB11
machine.GP16, //DB12
machine.GP17, //DB13
machine.GP18, //DB14
machine.GP19, //DB15
})
//Control pins for the SSD1289
rs := machine.GP0
wr := machine.GP1
cs := machine.GP2
rst := machine.GP3
display := ssd1289.New(rs, wr, cs, rst, bus)
display.Configure() //Sends intialization sequence to SSD1289.
//!! After configure the display will contain random data and needs to be cleared
background := color.RGBA{0, 0, 0, 255} //Black
display.FillDisplay(background) //Clears the display to the given color
for {
//Draw random filled coloured rectangles
x := int16(rand.Intn(120))
w := int16(rand.Intn(120))
y := int16(rand.Intn(160))
h := int16(rand.Intn(160))
r := uint8(rand.Intn(255))
g := uint8(rand.Intn(255))
b := uint8(rand.Intn(255))
c := color.RGBA{r, g, b, 255}
display.FillRect(x, y, w, h, c) //Fills the given rectangle the rest of the display is unaffected.
}
}
+49
View File
@@ -0,0 +1,49 @@
package main
import (
"image/color"
"machine"
"tinygo.org/x/drivers/uc8151"
)
var display uc8151.Device
var led machine.Pin
func main() {
led = machine.LED
led.Configure(machine.PinConfig{Mode: machine.PinOutput})
machine.SPI0.Configure(machine.SPIConfig{
Frequency: 12000000,
SCK: machine.EPD_SCK_PIN,
SDO: machine.EPD_SDO_PIN,
})
display = uc8151.New(machine.SPI0, machine.EPD_CS_PIN, machine.EPD_DC_PIN, machine.EPD_RESET_PIN, machine.EPD_BUSY_PIN)
display.Configure(uc8151.Config{
Rotation: uc8151.ROTATION_270,
Speed: uc8151.MEDIUM,
Blocking: true,
})
black := color.RGBA{1, 1, 1, 255}
display.ClearBuffer()
display.Display()
for i := int16(0); i < 37; i++ {
for j := int16(0); j < 16; j++ {
if (i+j)%2 == 0 {
showRect(i*8, j*8, 8, 8, black)
}
}
}
display.Display()
}
func showRect(x int16, y int16, w int16, h int16, c color.RGBA) {
for i := x; i < x+w; i++ {
for j := y; j < y+h; j++ {
display.SetPixel(i, j, c)
}
}
}
+84
View File
@@ -0,0 +1,84 @@
package main
import (
"machine"
"image/color"
"time"
"tinygo.org/x/drivers/waveshare-epd/epd2in9"
)
var display epd2in9.Device
const (
width = 128
height = 296
)
func main() {
machine.SPI0.Configure(machine.SPIConfig{
Frequency: 8000000,
Mode: 0,
})
display = epd2in9.New(machine.SPI0, machine.GPIO2, machine.GPIO3, machine.GPIO4, machine.GPIO5)
display.Configure(epd2in9.Config{
Width: width,
LogicalWidth: width,
Height: height,
})
black := color.RGBA{1, 1, 1, 255}
white := color.RGBA{0, 0, 0, 255}
display.ClearBuffer()
println("Clear the display")
display.ClearDisplay()
display.WaitUntilIdle()
println("Waiting for 2 seconds")
time.Sleep(2 * time.Second)
// Show a checkered board
for i := int16(0); i < width/8; i++ {
for j := int16(0); j < height/8; j++ {
if (i+j)%2 == 0 {
showRect(i*8, j*8, 8, 8, black)
}
}
}
println("Show checkered board")
display.Display()
display.WaitUntilIdle()
println("Waiting for 2 seconds")
time.Sleep(2 * time.Second)
println("Set partial lut")
display.SetLUT(false) // partial updates (faster, but with some ghosting)
println("Show smaller striped area")
for i := int16(40); i < 88; i++ {
for j := int16(83); j < 166; j++ {
if (i+j)%4 == 0 || (i+j)%4 == 1 {
display.SetPixel(i, j, black)
} else {
display.SetPixel(i, j, white)
}
}
}
display.Display()
display.WaitUntilIdle()
display.DeepSleep()
println("You could remove power now")
}
func showRect(x int16, y int16, w int16, h int16, c color.RGBA) {
for i := x; i < x+w; i++ {
for j := y; j < y+h; j++ {
display.SetPixel(i, j, c)
}
}
}
+2 -2
View File
@@ -1,5 +1,5 @@
//go:build !digispark && !arduino && !qtpy
// +build !digispark,!arduino,!qtpy
//go:build !digispark && !arduino && !qtpy && !m5stamp_c3 && !thingplus_rp2040
// +build !digispark,!arduino,!qtpy,!m5stamp_c3,!thingplus_rp2040
package main
+11
View File
@@ -0,0 +1,11 @@
//go:build qtpy || m5stamp_c3
// +build qtpy m5stamp_c3
package main
import "machine"
// Replace neo and led in the code below to match the pin
// that you are using if different.
var neo machine.Pin = machine.WS2812
var led = machine.NoPin
-5
View File
@@ -5,11 +5,6 @@ package main
import "machine"
// Replace neo and led in the code below to match the pin
// that you are using if different.
var neo machine.Pin = machine.NEOPIXELS
var led = machine.NoPin
func init() {
pwr := machine.NEOPIXELS_POWER
pwr.Configure(machine.PinConfig{Mode: machine.PinOutput})
+13
View File
@@ -0,0 +1,13 @@
//go:build thingplus_rp2040
// +build thingplus_rp2040
package main
import "machine"
// This is the pin assignment for the internal neopixel of the
// Sparkfun thingplus rp2040.
// Replace neo and led in the code below to match the pin
// that you are using if different.
var neo machine.Pin = machine.GPIO8
var led = machine.LED
+3 -3
View File
@@ -5,8 +5,8 @@ go 1.15
require (
github.com/eclipse/paho.mqtt.golang v1.2.0
github.com/frankban/quicktest v1.10.2
golang.org/x/net v0.0.0-20210614182718-04defd469f4e // indirect
tinygo.org/x/tinyfont v0.2.1
tinygo.org/x/tinyfs v0.1.0
golang.org/x/net v0.0.0-20210614182718-04defd469f4e
tinygo.org/x/tinyfont v0.3.0
tinygo.org/x/tinyfs v0.2.0
tinygo.org/x/tinyterm v0.1.0
)
+8 -2
View File
@@ -5,12 +5,15 @@ github.com/frankban/quicktest v1.10.2 h1:19ARM85nVi4xH7xPXuc5eM/udya5ieh7b/Sv+d8
github.com/frankban/quicktest v1.10.2/go.mod h1:K+q6oSqb0W0Ininfk863uOk1lMy69l/P6txr3mVT54s=
github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/hajimehoshi/go-jisx0208 v1.0.0/go.mod h1:yYxEStHL7lt9uL+AbdWgW9gBumwieDoZCiB1f/0X0as=
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/sago35/go-bdf v0.0.0-20200313142241-6c17821c91c4/go.mod h1:rOebXGuMLsXhZAC6mF/TjxONsm45498ZyzVhel++6KM=
github.com/valyala/fastjson v1.6.3/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY=
golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM=
golang.org/x/net v0.0.0-20210614182718-04defd469f4e h1:XpT3nA5TvE525Ne3hInMh6+GETgn27Zfm9dxsThnX2Q=
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -24,9 +27,12 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T
tinygo.org/x/drivers v0.14.0/go.mod h1:uT2svMq3EpBZpKkGO+NQHjxjGf1f42ra4OnMMwQL2aI=
tinygo.org/x/drivers v0.15.1/go.mod h1:uT2svMq3EpBZpKkGO+NQHjxjGf1f42ra4OnMMwQL2aI=
tinygo.org/x/drivers v0.16.0/go.mod h1:uT2svMq3EpBZpKkGO+NQHjxjGf1f42ra4OnMMwQL2aI=
tinygo.org/x/tinyfont v0.2.1 h1:FAaemBzw8wsfhAtG6fWW+QjyWw/K8YqEeiWo4N1pv4o=
tinygo.org/x/drivers v0.19.0/go.mod h1:uJD/l1qWzxzLx+vcxaW0eY464N5RAgFi1zTVzASFdqI=
tinygo.org/x/tinyfont v0.2.1/go.mod h1:eLqnYSrFRjt5STxWaMeOWJTzrKhXqpWw7nU3bPfKOAM=
tinygo.org/x/tinyfs v0.1.0 h1:yx1Tq9L60rpCm6HURo45x+Tnag+O9RGSbQfgeCb6XYU=
tinygo.org/x/tinyfont v0.3.0 h1:HIRLQoI3oc+2CMhPcfv+Ig88EcTImE/5npjqOnMD4lM=
tinygo.org/x/tinyfont v0.3.0/go.mod h1:+TV5q0KpwSGRWnN+ITijsIhrWYJkoUCp9MYELjKpAXk=
tinygo.org/x/tinyfs v0.1.0/go.mod h1:ysc8Y92iHfhTXeyEM9+c7zviUQ4fN9UCFgSOFfMWv20=
tinygo.org/x/tinyfs v0.2.0 h1:M0lwZC/dEGFt16XYN5GTQsif/qCkAN2qUVNxELVD1xg=
tinygo.org/x/tinyfs v0.2.0/go.mod h1:6ZHYdvB3sFYeMB3ypmXZCNEnFwceKc61ADYTYHpep1E=
tinygo.org/x/tinyterm v0.1.0 h1:80i+j+KWoxCFa/Xfp6pWbh79x+8zUdMXC1vaKj2QhkY=
tinygo.org/x/tinyterm v0.1.0/go.mod h1:/DDhNnGwNF2/tNgHywvyZuCGnbH3ov49Z/6e8LPLRR4=
+52 -44
View File
@@ -29,6 +29,33 @@ type Device struct {
rd machine.Pin
}
var cmdBuf [6]byte
var 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,
}
// Configure prepares display for use
func (d *Device) Configure(config Config) {
@@ -81,42 +108,15 @@ func (d *Device) Configure(config Config) {
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,
}
if config.DisplayInversion {
initCmd = append(initCmd, []byte{
INVON, 0x80,
}...)
initCmd = append(initCmd, INVON, 0x80)
}
initCmd = append(initCmd, []byte{
initCmd = append(initCmd,
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 {
@@ -267,24 +267,28 @@ func (d *Device) SetRotation(rotation Rotation) {
case Rotation270Mirror:
madctl = MADCTL_MX | MADCTL_MY | MADCTL_MV | MADCTL_BGR
}
d.sendCommand(MADCTL, []uint8{madctl})
cmdBuf[0] = madctl
d.sendCommand(MADCTL, cmdBuf[:1])
d.rotation = rotation
}
// SetScrollArea sets an area to scroll with fixed top/bottom or left/right parts of the display
// Rotation affects scroll direction
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),
})
cmdBuf[0] = uint8(topFixedArea >> 8)
cmdBuf[1] = uint8(topFixedArea)
cmdBuf[2] = uint8(d.height - topFixedArea - bottomFixedArea>>8)
cmdBuf[3] = uint8(d.height - topFixedArea - bottomFixedArea)
cmdBuf[4] = uint8(bottomFixedArea >> 8)
cmdBuf[5] = uint8(bottomFixedArea)
d.sendCommand(VSCRDEF, cmdBuf[:6])
}
// SetScroll sets the vertical scroll address of the display.
func (d *Device) SetScroll(line int16) {
d.sendCommand(VSCRSADD, []uint8{uint8(line >> 8), uint8(line)})
cmdBuf[0] = uint8(line >> 8)
cmdBuf[1] = uint8(line)
d.sendCommand(VSCRSADD, cmdBuf[:2])
}
// StopScroll returns the display to its normal state
@@ -298,16 +302,20 @@ func (d *Device) setWindow(x, y, w, h int16) {
//y += d.rowOffset
x1 := x + w - 1
if x != d.x0 || x1 != d.x1 {
d.sendCommand(CASET, []uint8{
uint8(x >> 8), uint8(x), uint8(x1 >> 8), uint8(x1),
})
cmdBuf[0] = uint8(x >> 8)
cmdBuf[1] = uint8(x)
cmdBuf[2] = uint8(x1 >> 8)
cmdBuf[3] = uint8(x1)
d.sendCommand(CASET, cmdBuf[:4])
d.x0, d.x1 = x, x1
}
y1 := y + h - 1
if y != d.y0 || y1 != d.y1 {
d.sendCommand(PASET, []uint8{
uint8(y >> 8), uint8(y), uint8(y1 >> 8), uint8(y1),
})
cmdBuf[0] = uint8(y >> 8)
cmdBuf[1] = uint8(y)
cmdBuf[2] = uint8(y1 >> 8)
cmdBuf[3] = uint8(y1)
d.sendCommand(PASET, cmdBuf[:4])
d.y0, d.y1 = y, y1
}
d.sendCommand(RAMWR, nil)
+2 -2
View File
@@ -1,5 +1,5 @@
//go:build !atsamd51 && !atsamd21
// +build !atsamd51,!atsamd21
//go:build !atsamd51 && !atsame5x && !atsamd21
// +build !atsamd51,!atsame5x,!atsamd21
package ili9341
+2 -2
View File
@@ -1,5 +1,5 @@
//go:build atsamd51
// +build atsamd51
//go:build atsamd51 || atsame5x
// +build atsamd51 atsame5x
package ili9341
+2 -2
View File
@@ -773,10 +773,10 @@ func (d *decoder) convertToRGB() (image.Image, error) {
// Decode reads a JPEG image from r. Different from the standard package, the
// decoded result will be received by the callback set by SetCallback().
func Decode(r io.Reader) error {
func Decode(r io.Reader) (image.Image, error) {
var d decoder
_, err := d.decode(r, false)
return err
return nil, err
}
// DecodeConfig returns the color model and dimensions of a JPEG image without
+4 -4
View File
@@ -965,7 +965,7 @@ func (d *decoder) checkHeader() error {
// Decode reads a PNG image from r. Different from the standard package, the
// decoded result will be received by the callback set by SetCallback().
func Decode(r io.Reader) error {
func Decode(r io.Reader) (image.Image, error) {
d := &decoder{
r: r,
crc: crc32.NewIEEE(),
@@ -974,17 +974,17 @@ func Decode(r io.Reader) error {
if err == io.EOF {
err = io.ErrUnexpectedEOF
}
return err
return nil, err
}
for d.stage != dsSeenIEND {
if err := d.parseChunk(); err != nil {
if err == io.EOF {
err = io.ErrUnexpectedEOF
}
return err
return nil, err
}
}
return nil
return nil, nil
}
// DecodeConfig returns the color model and dimensions of a PNG image without
+225
View File
@@ -0,0 +1,225 @@
package irremote // import "tinygo.org/x/drivers/irremote"
import (
"machine"
"time"
)
// NEC protocol references
// https://www.sbprojects.net/knowledge/ir/nec.php
// https://techdocs.altium.com/display/FPGA/NEC+Infrared+Transmission+Protocol
// https://simple-circuit.com/arduino-nec-remote-control-decoder/
// Data encapsulates the data received by the ReceiverDevice.
type Data struct {
// Code is the raw IR data received.
Code uint32
// Address is the decoded address from the IR data received.
Address uint16
// Command is the decoded command from the IR data recieved
Command uint16
// Flags provides additional information about the IR data received. See DataFlags
Flags DataFlags
}
// DataFlags provides bitwise flags representing various information about recieved IR data.
type DataFlags uint16
// Valid values for DataFlags
const (
// DataFlagIsRepeat set indicates that the IR data is a repeat commmand
DataFlagIsRepeat DataFlags = 1 << iota
)
// CommandHandler defines the callback function used to provide IR data received by the ReceiverDevice.
type CommandHandler func(data Data)
// nec_ir_state represents the various internal states used to decode the NEC IR protocol commands
type nec_ir_state uint8
// Valid values for nec_ir_state
const (
lead_pulse_start nec_ir_state = iota // Start receiving IR data, beginning of 9ms lead pulse
lead_space_start // End of 9ms lead pulse, start of 4.5ms space
lead_space_end // End of 4.5ms space, start of 562µs pulse
bit_read_start // End of 562µs pulse, start of 562µs or 1687µs space
bit_read_end // End of 562µs or 1687µs space
trail_pulse_end // End of 562µs trailing pulse
)
// ReceiverDevice is the device for receiving IR commands
type ReceiverDevice struct {
pin machine.Pin // IR input pin.
ch CommandHandler // client callback function
necState nec_ir_state // internal state machine
data Data // decoded data for client
lastTime time.Time // used to track states
bitIndex int // tracks which bit (0-31) of necCode is being read
}
// NewReceiver returns a new IR receiver device
func NewReceiver(pin machine.Pin) ReceiverDevice {
return ReceiverDevice{pin: pin}
}
// Configure configures the input pin for the IR receiver device
func (ir *ReceiverDevice) Configure() {
// The IR receiver sends logic HIGH when NOT receiving IR, and logic LOW when receiving IR
ir.pin.Configure(machine.PinConfig{Mode: machine.PinInputPullup})
}
// SetCommandHandler is used to start or stop receiving IR commands via a callback function (pass nil to stop)
func (ir *ReceiverDevice) SetCommandHandler(ch CommandHandler) {
ir.ch = ch
ir.resetStateMachine()
if ch != nil {
// Start monitoring IR output pin for changes
ir.pin.SetInterrupt(machine.PinFalling|machine.PinRising, ir.pinChange)
} else {
// Stop monitoring IR output pin for changes
ir.pin.SetInterrupt(0, nil)
}
}
// Internal helper function to reset state machine on protocol failure
func (ir *ReceiverDevice) resetStateMachine() {
ir.data = Data{}
ir.bitIndex = 0
ir.necState = lead_pulse_start
}
// Internal pin rising/falling edge interrupt handler
func (ir *ReceiverDevice) pinChange(pin machine.Pin) {
/* Currently TinyGo is sending machine.NoPin (0xff) for all pins, at least on RP2040
if pin != ir.pin {
return // This is not the pin you're looking for
}
*/
now := time.Now()
duration := now.Sub(ir.lastTime)
ir.lastTime = now
switch ir.necState {
case lead_pulse_start:
if !ir.pin.Get() {
// IR is 'on' (pin is pulled high and sent low when IR is received)
ir.necState = lead_space_start // move to next state
}
case lead_space_start:
if duration > time.Microsecond*9500 || duration < time.Microsecond*8500 {
// Invalid interval for 9ms lead pulse. Reset
ir.resetStateMachine()
} else {
// 9ms lead pulse detected, move to next state
ir.necState = lead_space_end
}
case lead_space_end:
if duration > time.Microsecond*5000 || duration < time.Microsecond*1750 {
// Invalid interval for 4.5ms lead space OR 2.25ms repeat space. Reset
ir.resetStateMachine()
} else {
// 4.5ms lead space OR 2.25ms repeat space detected
if duration > time.Microsecond*3000 {
// 4.5ms lead space detected, new code incoming, move to next state
ir.resetStateMachine()
ir.necState = bit_read_start
} else {
// 2.25ms repeat space detected.
if ir.data.Code != 0 {
// Valid repeat code. Invoke client callback with repeat flag set
ir.data.Flags |= DataFlagIsRepeat
if ir.ch != nil {
ir.ch(ir.data)
}
ir.necState = lead_pulse_start
} else {
// ir.data is not in a valid state for a repeat. Reset
ir.resetStateMachine()
}
}
}
case bit_read_start:
if duration > time.Microsecond*700 || duration < time.Microsecond*400 {
// Invalid interval for 562.5µs pulse. Reset
ir.resetStateMachine()
} else {
// 562.5µs pulse detected, move to next state
ir.necState = bit_read_end
}
case bit_read_end:
if duration > time.Microsecond*1800 || duration < time.Microsecond*400 {
// Invalid interval for 562.5µs space OR 1687.5µs space. Reset
ir.resetStateMachine()
} else {
// 562.5µs OR 1687.5µs space detected
mask := uint32(1 << ir.bitIndex)
if duration > time.Microsecond*1000 {
// 1687.5µs space detected (logic 1) - Set bit
ir.data.Code |= mask
} else {
// 562.5µs space detected (logic 0) - Clear bit
ir.data.Code &^= mask
}
ir.bitIndex++
if ir.bitIndex > 31 {
// We've read all bits for this code, move to next state
ir.necState = trail_pulse_end
} else {
// Read next bit
ir.necState = bit_read_start
}
}
case trail_pulse_end:
if duration > time.Microsecond*700 || duration < time.Microsecond*400 {
// Invalid interval for trailing 562.5µs pulse. Reset
ir.resetStateMachine()
} else {
// 562.5µs trailing pulse detected. Decode & validate data
err := ir.decode()
if err == irDecodeErrorNone {
// Valid data, invoke client callback
if ir.ch != nil {
ir.ch(ir.data)
}
// around we go again. Note: we don't resetStateMachine() since repeat codes are now possible
ir.necState = lead_pulse_start
} else {
ir.resetStateMachine()
}
}
}
}
// Error type for NEC format decoding
type irDecodeError int
// Valid values for irDecodeError
const (
irDecodeErrorNone irDecodeError = iota // no error occurred
irDecodeErrorInverseCheckFail // validation of inverse cmd does not match cmd
)
func (ir *ReceiverDevice) decode() irDecodeError {
// Decode cmd and inverse cmd and perform validation check
cmd := uint8((ir.data.Code & 0x00ff0000) >> 16)
invCmd := uint8((ir.data.Code & 0xff000000) >> 24)
if cmd != ^invCmd {
// Validation failure. cmd and inverse cmd do not match
return irDecodeErrorInverseCheckFail
}
// cmd validation pass, decode address
ir.data.Command = uint16(cmd)
addrLow := uint8(ir.data.Code & 0xff)
addrHigh := uint8((ir.data.Code & 0xff00) >> 8)
if addrHigh == ^addrLow {
// addrHigh is inverse of addrLow. This is not a valid 16-bit address in extended NEC coding
// since it is indistinguishable from 8-bit address with inverse validation. Use the 8-bit address
ir.data.Address = uint16(addrLow)
} else {
// 16-bit extended NEC address
ir.data.Address = (uint16(addrHigh) << 8) | uint16(addrLow)
}
// Clear repeat flag
ir.data.Flags &^= DataFlagIsRepeat
return irDecodeErrorNone
}
+209
View File
@@ -0,0 +1,209 @@
// Package is31fl3731 provides a driver for the Lumissil IS31FL3731 matrix LED
// driver.
//
// Driver supports following layouts:
// - any custom LED matrix layout
// - Adafruit 15x7 CharliePlex LED Matrix FeatherWing (CharlieWing)
// https://www.adafruit.com/product/3163
//
// Datasheet:
// https://www.lumissil.com/assets/pdf/core/IS31FL3731_DS.pdf
//
// This driver inspired by Adafruit Python driver:
// https://github.com/adafruit/Adafruit_CircuitPython_IS31FL3731
//
package is31fl3731
import (
"fmt"
"time"
"tinygo.org/x/drivers"
)
// Device implements TinyGo driver for Lumissil IS31FL3731 matrix LED driver
type Device struct {
Address uint8
bus drivers.I2C
// Currently selected command register (one of the frame registers or the
// function register)
selectedCommand uint8
}
// Configure chip for operating as a LED matrix display
func (d *Device) Configure() (err error) {
// Shutdown software
err = d.writeFunctionRegister(SET_SHUTDOWN, []byte{SOFTWARE_OFF})
if err != nil {
return fmt.Errorf("failed to shutdown: %w", err)
}
time.Sleep(time.Millisecond * 10)
// Wake up software
err = d.writeFunctionRegister(SET_SHUTDOWN, []byte{SOFTWARE_ON})
if err != nil {
return fmt.Errorf("failed to wake up: %w", err)
}
// Set display to a picture mode ("auto frame play mode" and "audio frame play
// mode" are not supported in this version of the driver)
err = d.writeFunctionRegister(SET_DISPLAY_MODE, []byte{DISPLAY_MODE_PICTURE})
if err != nil {
return fmt.Errorf("failed to switch to a picture move: %w", err)
}
// Enable LEDs that are present (soldered) on the board. From the datasheet:
// LEDs which are no connected must be off by LED Control Register (Frame
// Registers) or it will affect other LEDs
err = d.enableLEDs()
if err != nil {
return fmt.Errorf("failed to enable LEDs: %w", err)
}
// Disable audiosync
err = d.writeFunctionRegister(SET_AUDIOSYNC, []byte{AUDIOSYNC_OFF})
if err != nil {
return fmt.Errorf("failed to disable audiosync: %w", err)
}
// Clear all frames
for frame := FRAME_0; frame <= FRAME_7; frame++ {
err = d.Clear(frame)
if err != nil {
return fmt.Errorf("failed to clear frame %d: %w", frame, err)
}
}
// 1st frame is displayed by default
err = d.SetActiveFrame(FRAME_0)
if err != nil {
return fmt.Errorf("failed to set active frame: %w", err)
}
return nil
}
// selectCommand selects command register, can be:
// - frame registers 0-7
// - function register
func (d *Device) selectCommand(command uint8) (err error) {
if command != d.selectedCommand {
d.selectedCommand = command
return d.bus.WriteRegister(d.Address, COMMAND, []byte{command})
}
return nil
}
// writeFunctionRegister selects the function register and writes data into it
func (d *Device) writeFunctionRegister(operation uint8, data []byte) (err error) {
err = d.selectCommand(FUNCTION)
if err != nil {
return err
}
return d.bus.WriteRegister(d.Address, operation, data)
}
// enableLEDs enables only LEDs that are soldered on the set board. Enabled
// all 16x9 LEDs by default
func (d *Device) enableLEDs() (err error) {
for frame := FRAME_0; frame <= FRAME_7; frame++ {
err = d.selectCommand(frame)
if err != nil {
return err
}
// Enable every LED (16 columns x 9 rows)
for i := uint8(0); i < 16; i++ {
err = d.bus.WriteRegister(d.Address, i, []byte{0xFF})
if err != nil {
return err
}
}
}
return nil
}
// setPixelPWD sets individual pixel's PWM value [0-255] on the selected frame
func (d *Device) setPixelPWD(frame, n, value uint8) (err error) {
err = d.selectCommand(frame)
if err != nil {
return err
}
return d.bus.WriteRegister(d.Address, LED_PWM_OFFSET+n, []byte{value})
}
// SetActiveFrame sets frame to display with LEDs
func (d *Device) SetActiveFrame(frame uint8) (err error) {
if frame > FRAME_7 {
return fmt.Errorf("frame %d is out of valid range [0-7]", frame)
}
return d.writeFunctionRegister(SET_ACTIVE_FRAME, []byte{frame})
}
// Fill the whole frame with provided PWM value [0-255]
func (d *Device) Fill(frame, value uint8) (err error) {
if frame > FRAME_7 {
return fmt.Errorf("frame %d is out of valid range [0-7]", frame)
}
err = d.selectCommand(frame)
if err != nil {
return err
}
data := make([]byte, 24)
for i := range data {
data[i] = value
}
for i := uint8(0); i < 6; i++ {
err = d.bus.WriteRegister(d.Address, LED_PWM_OFFSET+i*24, data)
if err != nil {
return err
}
}
return nil
}
// Clear the whole frame
func (d *Device) Clear(frame uint8) (err error) {
return d.Fill(frame, 0x00)
}
// DrawPixelIndex draws a single pixel on the selected frame by its index with
// provided PWM value [0-255]
func (d *Device) DrawPixelIndex(frame, index, value uint8) (err error) {
if frame > FRAME_7 {
return fmt.Errorf("frame %d is out of valid range [0-7]", frame)
}
return d.setPixelPWD(frame, index, value)
}
// DrawPixelXY draws a single pixel on the selected frame by its XY coordinates
// with provided PWM value [0-255]. Raw LEDs layout assumed to be a 16x9 matrix,
// and can be used with any custom board that has IS31FL3731 driver.
func (d *Device) DrawPixelXY(frame, x, y, value uint8) (err error) {
return d.setPixelPWD(frame, 16*x+y, value)
}
// New creates a raw driver w/o any preset board layout.
// Addresses:
// - 0x74 (AD pin connected to GND)
// - 0x75 (AD pin connected to SCL)
// - 0x76 (AD pin connected to SDA)
// - 0x77 (AD pin connected to VCC)
func New(bus drivers.I2C, address uint8) Device {
return Device{
Address: address,
bus: bus,
}
}
+106
View File
@@ -0,0 +1,106 @@
package is31fl3731
import (
"fmt"
"tinygo.org/x/drivers"
)
// DeviceAdafruitCharlieWing15x7 implements TinyGo driver for Lumissil
// IS31FL3731 matrix LED driver on Adafruit 15x7 CharliePlex LED Matrix
// FeatherWing (CharlieWing) board: https://www.adafruit.com/product/3163
type DeviceAdafruitCharlieWing15x7 struct {
Device
}
// enableLEDs enables only LEDs that are soldered on the Adafruit CharlieWing
// board. The board has following LEDs matrix layout:
//
// "o" - connected (soldered) LEDs
// "x" - not connected LEDs
//
// + - - - - - - - - - - - - - - +
// | + - - - - - - - - - - - - + |
// | | | |
// | | v v
// +---------------------------------+
// | o o o o o o o o o o o o o o o x |
// | o o o o o o o o o o o o o o o x |
// | o o o o o o o o o o o o o o o x |
// | o o o o o o o o o o o o o o o x |
// | o o o o o o o o o o o o o o o x |
// | o o o o o o o o o o o o o o o x |
// | o o o o o o o o o o o o o o o x |
// | x x x x x x x x x x x x x x x x |
// +---------------------------------+
// ^ ^ | |
// | | ... - - + |
// | + - - - - - - - - - - - - - +
// |
// start (address 0x00)
//
func (d *DeviceAdafruitCharlieWing15x7) enableLEDs() (err error) {
for frame := FRAME_0; frame <= FRAME_7; frame++ {
err = d.selectCommand(frame)
if err != nil {
return err
}
// Enable left half
for i := uint8(0); i < 16; i += 2 {
err = d.bus.WriteRegister(d.Address, i, []byte{0b11111110})
if err != nil {
return err
}
}
// Enable right half
for i := uint8(3); i < 16; i += 2 {
err = d.bus.WriteRegister(d.Address, i, []byte{0b01111111})
if err != nil {
return err
}
}
// Disable invisible column on the right side
err = d.bus.WriteRegister(d.Address, 1, []byte{0b00000000})
if err != nil {
return err
}
}
return nil
}
// DrawPixelXY draws a single pixel on the selected frame by its XY coordinates
// with provided PWM value [0-255]
func (d *DeviceAdafruitCharlieWing15x7) DrawPixelXY(frame, x, y, value uint8) (err error) {
var index uint8
if x >= 15 {
return fmt.Errorf("invalid value: X is out of range [0, 15]")
} else if y >= 7 {
return fmt.Errorf("invalid value: Y is out of range [0, 7]")
}
// Board is one pixel shorter (7 vs 8 supported pixels)
if x < 8 {
index = 16*x + y + 1
} else {
index = 16*(16-x) - y - 1 - 1
}
return d.setPixelPWD(frame, index, value)
}
// NewAdafruitCharlieWing15x7 creates a new driver with Adafruit 15x7
// CharliePlex LED Matrix FeatherWing (CharlieWing) layout.
// Available addresses:
// - 0x74 (default)
// - 0x77 (when the address jumper soldered)
func NewAdafruitCharlieWing15x7(bus drivers.I2C, address uint8) DeviceAdafruitCharlieWing15x7 {
return DeviceAdafruitCharlieWing15x7{
Device: Device{
Address: address,
bus: bus,
},
}
}
+52
View File
@@ -0,0 +1,52 @@
package is31fl3731
// Registers. Names taken from the datasheet:
// https://www.lumissil.com/assets/pdf/core/IS31FL3731_DS.pdf
const (
// AD pin connected to GND
I2C_ADDRESS_74 uint8 = 0x74
// AD pin connected to SCL
I2C_ADDRESS_75 uint8 = 0x75
// AD pin connected to SDA
I2C_ADDRESS_76 uint8 = 0x76
// AD pin connected to VCC
I2C_ADDRESS_77 uint8 = 0x77
// Main command register
COMMAND uint8 = 0xFD
// Commands for each of 8 frames
FRAME_0 uint8 = 0x00
FRAME_1 uint8 = 0x01
FRAME_2 uint8 = 0x02
FRAME_3 uint8 = 0x03
FRAME_4 uint8 = 0x04
FRAME_5 uint8 = 0x05
FRAME_6 uint8 = 0x06
FRAME_7 uint8 = 0x07
// Command to set configuration
FUNCTION uint8 = 0x0B
// Configuration:
SET_DISPLAY_MODE uint8 = 0x00
SET_ACTIVE_FRAME uint8 = 0x01
SET_AUDIOSYNC uint8 = 0x06
SET_SHUTDOWN uint8 = 0x0A
// Configuration: display mode
DISPLAY_MODE_PICTURE uint8 = 0x00
// Configuration: audiosync (enable audio signal to modulate the intensity of
// the matrix)
AUDIOSYNC_OFF uint8 = 0x00
AUDIOSYNC_ON uint8 = 0x01
// Configuration: software shutdown
SOFTWARE_OFF uint8 = 0x00
SOFTWARE_ON uint8 = 0x01
// Frame LEDs
LED_CONTROL_OFFSET uint8 = 0x00 // to on/off each LED
LED_PWM_OFFSET uint8 = 0x24 // to set PWM (0-255) for each LED
)
+141
View File
@@ -0,0 +1,141 @@
package l3gd20
import (
"encoding/binary"
"time"
"tinygo.org/x/drivers"
)
const (
fifoLen = 32 * 3 * 2
)
type DevI2C struct {
addr uint8
// sensitivity or range.
mul int32
bus drivers.I2C
buf [1]byte
// gyro databuf.
databuf [6]byte
data [3]int32
}
func NewI2C(bus drivers.I2C, addr uint8) *DevI2C {
return &DevI2C{
addr: addr,
bus: bus,
mul: sensMul250,
}
}
// Initializes and configures the device.
func (d *DevI2C) Configure(cfg Config) error {
err := cfg.validate()
if err != nil {
return err
}
err = d.Reboot()
if err != nil {
return err
}
// Reset then switch to normal mode and enable all three channels.
err = d.write8(CTRL_REG1, 0)
if err != nil {
return err
}
err = d.write8(CTRL_REG1, reg1NormalBits)
if err != nil {
return err
}
// Reset REG2 values to default
err = d.write8(CTRL_REG3, 0)
if err != nil {
return err
}
// Set sensitivity
switch cfg.Range {
case 1: // debugging range
d.mul = 1
cfg.Range = Range_2000
case Range_250:
d.mul = sensMul250
case Range_500:
d.mul = sensMul500
case Range_2000:
d.mul = sensMul2000
default:
return ErrBadRange
}
err = d.write8(CTRL_REG4, cfg.Range)
if err != nil {
return err
}
// Finally verify whomai register and return error if
// board is not who it says it is. Some counterfeit boards
// have incorrect whomai but can still be used.
whoami, err := d.read8(WHOAMI)
if err != nil {
return err
}
if whoami != expectedWHOAMI && whoami != expectedWHOAMI_H {
return ErrBadIdentity
}
return nil
}
func (d *DevI2C) Update() error {
err := d.bus.ReadRegister(d.addr, OUT_X_L, d.databuf[:2])
if err != nil {
return err
}
err = d.bus.ReadRegister(d.addr, OUT_Y_L, d.databuf[2:4])
if err != nil {
return err
}
err = d.bus.ReadRegister(d.addr, OUT_Z_L, d.databuf[4:6])
if err != nil {
return err
}
x := int16(binary.LittleEndian.Uint16(d.databuf[0:]))
y := int16(binary.LittleEndian.Uint16(d.databuf[2:]))
z := int16(binary.LittleEndian.Uint16(d.databuf[4:]))
d.data[0] = d.mul * int32(x)
d.data[1] = d.mul * int32(y)
d.data[2] = d.mul * int32(z)
return nil
}
// Reboot sets reboot bit in CTRL_REG5 to true and unsets it.
func (d *DevI2C) Reboot() error {
reg5, err := d.read8(CTRL_REG5)
if err != nil {
return err
}
// Write reboot bit and then unset it.
err = d.write8(CTRL_REG5, reg5|reg5RebootBit)
if err != nil {
return err
}
time.Sleep(50 * time.Microsecond)
return d.write8(CTRL_REG5, reg5&^reg5RebootBit)
}
// AngularVelocity returns result in microradians per second.
func (d *DevI2C) AngularVelocity() (x, y, z int32) {
return d.data[0], d.data[1], d.data[2]
}
// func (d DevI2C) Update(measurement)
func (d DevI2C) read8(reg uint8) (byte, error) {
err := d.bus.ReadRegister(d.addr, reg, d.buf[:1])
return d.buf[0], err
}
func (d DevI2C) write8(reg uint8, val byte) error {
d.buf[0] = val
return d.bus.WriteRegister(d.addr, reg, d.buf[:1])
}
+49
View File
@@ -0,0 +1,49 @@
package l3gd20
import "errors"
var (
ErrBadIdentity = errors.New("got unexpected identity from WHOMAI")
ErrBadRange = errors.New("bad range configuration value")
)
// Sensitivity factors
const (
// range at bits 4-5
// 00 = 250 dps
// 01 = 500 dps
// 10 = 2000 dps
// 11 = 2000 dps
rangePos = 4
Range_250 = 0b00 << rangePos // 8.75 mdps/digit
Range_500 = 0b01 << rangePos // 17.5 mdps/digit
Range_2000 = 0b11 << rangePos // 70 mdps/digit
rangebits = Range_250 | Range_500 | Range_2000
// Sensitivities for degrees
sensDiv250dps = 800
sensDiv500dps = 400
sensDiv2000dps = 100
sens_250 = 7. / sensDiv250dps // Sensitivity at 250 dps
sens_500 = 7. / sensDiv500dps // Sensitivity at 500 dps
sens_2000 = 7. / sensDiv2000dps // Sensitivity at 500 dp
// 1e6*Pi/180. = 17453.292519943298 (constant for Degree to micro radians conversion)
// sensitivities for radians
sensMul250 = 7 * 1745329 / 100 / sensDiv250dps
sensMul500 = 7 * 1745329 / 100 / sensDiv500dps
sensMul2000 = 7 * 1745329 / 100 / sensDiv2000dps
)
type Config struct {
Range uint8
}
// validate scans config for invalid data and returns non-nil
// error indicating what data must be modified.
func (cfg *Config) validate() error {
if cfg.Range&^rangebits != 0 && cfg.Range != 1 {
return ErrBadRange
}
return nil
}
+99
View File
@@ -0,0 +1,99 @@
package l3gd20
// Expected identification number for L3GD20.
const (
// For L3GD20
expectedWHOAMI = 0xD4
// For L3GD20H
expectedWHOAMI_H = 0xD7
)
// Register bits masks
const (
reg5RebootBit = 1 << 7
reg5FIFOEnableBit = 1 << 6
reg1NormalBits = 0b1111
)
// Register addresses. Comments from https://github.com/adafruit/Adafruit_L3GD20_U/blob/master/Adafruit_L3GD20_U.cpp
const (
// The Slave ADdress (SAD) associated with the L3GD20 is 110101xb
I2CAddr = 0b1101011
// The SDO pin can be used to modify the less significant bit of the device address.
I2CAddrSDOLow = 0b1101010
WHOAMI uint8 = 0x0F
// CTRL_REG1 (0x20)
// ====================================================================
// BIT Symbol Description Default
// --- ------ --------------------------------------------- -------
// 7-6 DR1/0 Output data rate 00
// 5-4 BW1/0 Bandwidth selection 00
// 3 PD 0 = Power-down mode, 1 = normal/sleep mode 0
// 2 ZEN Z-axis enable (0 = disabled, 1 = enabled) 1
// 1 YEN Y-axis enable (0 = disabled, 1 = enabled) 1
// 0 XEN X-axis enable (0 = disabled, 1 = enabled) 1
CTRL_REG1 uint8 = 0x20
// Set CTRL_REG2 (0x21)
// ====================================================================
// BIT Symbol Description Default
// --- ------ --------------------------------------------- -------
// 5-4 HPM1/0 High-pass filter mode selection 00
// 3-0 HPCF3..0 High-pass filter cutoff frequency selection 0000
CTRL_REG2 uint8 = 0x21
// CTRL_REG3 (0x22)
// ====================================================================
// BIT Symbol Description Default
// --- ------ --------------------------------------------- -------
// 7 I1_Int1 Interrupt enable on INT1 (0=disable,1=enable) 0
// 6 I1_Boot Boot status on INT1 (0=disable,1=enable) 0
// 5 H-Lactive Interrupt active config on INT1 (0=high,1=low) 0
// 4 PP_OD Push-Pull/Open-Drain (0=PP, 1=OD) 0
// 3 I2_DRDY Data ready on DRDY/INT2 (0=disable,1=enable) 0
// 2 I2_WTM FIFO wtrmrk int on DRDY/INT2 (0=dsbl,1=enbl) 0
// 1 I2_ORun FIFO overrun int on DRDY/INT2 (0=dsbl,1=enbl) 0
// 0 I2_Empty FIFI empty int on DRDY/INT2 (0=dsbl,1=enbl) 0
CTRL_REG3 uint8 = 0x22
// CTRL_REG4 (0x23)
// ====================================================================
// BIT Symbol Description Default
// --- ------ --------------------------------------------- -------
// 7 BDU Block Data Update (0=continuous, 1=LSB/MSB) 0
// 6 BLE Big/Little-Endian (0=Data LSB, 1=Data MSB) 0
// 5-4 FS1/0 Full scale selection 00
// 00 = 250 dps
// 01 = 500 dps
// 10 = 2000 dps
// 11 = 2000 dps
// 0 SIM SPI Mode (0=4-wire, 1=3-wire) 0
CTRL_REG4 uint8 = 0x23
// CTRL_REG5 (0x24)
// ====================================================================
// BIT Symbol Description Default
// --- ------ --------------------------------------------- -------
// 7 BOOT Reboot memory content (0=normal, 1=reboot) 0
// 6 FIFO_EN FIFO enable (0=FIFO disable, 1=enable) 0
// 4 HPen High-pass filter enable (0=disable,1=enable) 0
// 3-2 INT1_SEL INT1 Selection config 00
// 1-0 OUT_SEL Out selection config 00
CTRL_REG5 uint8 = 0x24
REFERENCE uint8 = 0x25
OUT_TEMP uint8 = 0x26
STATUS_REG uint8 = 0x27
OUT_X_L uint8 = 0x28
OUT_X_H uint8 = 0x29
OUT_Y_L uint8 = 0x2A
OUT_Y_H uint8 = 0x2B
OUT_Z_L uint8 = 0x2C
OUT_Z_H uint8 = 0x2D
FIFO_CTRL_REG uint8 = 0x2E
FIFO_SRC_REG uint8 = 0x2F
INT1_CFG uint8 = 0x30
INT1_SRC uint8 = 0x31
INT1_TSH_XH uint8 = 0x32
INT1_TSH_XL uint8 = 0x33
INT1_TSH_YH uint8 = 0x34
INT1_TSH_YL uint8 = 0x35
INT1_TSH_ZH uint8 = 0x36
INT1_TSH_ZL uint8 = 0x37
INT1_DURATION uint8 = 0x38
)
+2 -2
View File
@@ -11,8 +11,8 @@ import (
// Configure sets up the LPS22HB device for communication.
func (d *Device) Configure() {
// Following lines are Nano 33 BLE specific, they have nothing to do with sensor per se
machine.LSP_PWR.Configure(machine.PinConfig{Mode: machine.PinOutput})
machine.LSP_PWR.High()
machine.LPS_PWR.Configure(machine.PinConfig{Mode: machine.PinOutput})
machine.LPS_PWR.High()
machine.I2C_PULLUP.Configure(machine.PinConfig{Mode: machine.PinOutput})
machine.I2C_PULLUP.High()
// Wait a moment
+77 -50
View File
@@ -6,6 +6,7 @@
package lsm303agr // import "tinygo.org/x/drivers/lsm303agr"
import (
"errors"
"math"
"tinygo.org/x/drivers"
@@ -22,6 +23,7 @@ type Device struct {
MagPowerMode uint8
MagSystemMode uint8
MagDataRate uint8
buf [6]uint8
}
// Configuration for LSM303AGR device.
@@ -34,12 +36,17 @@ type Configuration struct {
MagDataRate uint8
}
// New creates a new LSM303AGR connection. The I2C bus must already be
// configured.
var errNotConnected = errors.New("lsm303agr: failed to communicate with either acel or magnet sensor")
// New creates a new LSM303AGR connection. The I2C bus must already be configured.
//
// This function only creates the Device object, it does not touch the device.
func New(bus drivers.I2C) Device {
return Device{bus: bus, AccelAddress: ACCEL_ADDRESS, MagAddress: MAG_ADDRESS}
func New(bus drivers.I2C) *Device {
return &Device{
bus: bus,
AccelAddress: ACCEL_ADDRESS,
MagAddress: MAG_ADDRESS,
}
}
// Connected returns whether both sensor on LSM303AGR has been found.
@@ -52,7 +59,12 @@ func (d *Device) Connected() bool {
}
// Configure sets up the LSM303AGR device for communication.
func (d *Device) Configure(cfg Configuration) {
func (d *Device) Configure(cfg Configuration) (err error) {
// Verify unit communication
if !d.Connected() {
return errNotConnected
}
if cfg.AccelDataRate != 0 {
d.AccelDataRate = cfg.AccelDataRate
@@ -90,36 +102,46 @@ func (d *Device) Configure(cfg Configuration) {
d.MagSystemMode = MAG_SYSTEM_CONTINUOUS
}
cmd := []byte{0}
data := d.buf[:1]
cmd[0] = byte(d.AccelDataRate<<4 | d.AccelPowerMode | 0x07)
d.bus.WriteRegister(uint8(d.AccelAddress), ACCEL_CTRL_REG1_A, cmd)
data[0] = byte(d.AccelDataRate<<4 | d.AccelPowerMode | 0x07)
err = d.bus.WriteRegister(uint8(d.AccelAddress), ACCEL_CTRL_REG1_A, data)
if err != nil {
return
}
cmd[0] = byte(0x80 | d.AccelRange<<4)
d.bus.WriteRegister(uint8(d.AccelAddress), ACCEL_CTRL_REG4_A, cmd)
data[0] = byte(0x80 | d.AccelRange<<4)
err = d.bus.WriteRegister(uint8(d.AccelAddress), ACCEL_CTRL_REG4_A, data)
if err != nil {
return
}
cmd[0] = byte(0xC0)
d.bus.WriteRegister(uint8(d.AccelAddress), TEMP_CFG_REG_A, cmd)
data[0] = byte(0xC0)
err = d.bus.WriteRegister(uint8(d.AccelAddress), TEMP_CFG_REG_A, data)
if err != nil {
return
}
// Temperature compensation is on for magnetic sensor
cmd[0] = byte(0x80 | d.MagPowerMode<<4 | d.MagDataRate<<2 | d.MagSystemMode)
d.bus.WriteRegister(uint8(d.MagAddress), MAG_MR_REG_M, cmd)
data[0] = byte(0x80 | d.MagPowerMode<<4 | d.MagDataRate<<2 | d.MagSystemMode)
err = d.bus.WriteRegister(uint8(d.MagAddress), MAG_MR_REG_M, data)
if err != nil {
return
}
return nil
}
// 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) {
data1, data2, data3, data4, data5, data6 := []byte{0}, []byte{0}, []byte{0}, []byte{0}, []byte{0}, []byte{0}
d.bus.ReadRegister(uint8(d.AccelAddress), ACCEL_OUT_X_H_A, data1)
d.bus.ReadRegister(uint8(d.AccelAddress), ACCEL_OUT_X_L_A, data2)
d.bus.ReadRegister(uint8(d.AccelAddress), ACCEL_OUT_Y_H_A, data3)
d.bus.ReadRegister(uint8(d.AccelAddress), ACCEL_OUT_Y_L_A, data4)
d.bus.ReadRegister(uint8(d.AccelAddress), ACCEL_OUT_Z_H_A, data5)
d.bus.ReadRegister(uint8(d.AccelAddress), ACCEL_OUT_Z_L_A, data6)
func (d *Device) ReadAcceleration() (x, y, z int32, err error) {
data := d.buf[:6]
err = d.bus.ReadRegister(uint8(d.AccelAddress), ACCEL_OUT_X_L_A, data)
if err != nil {
return
}
rangeFactor := int16(0)
switch d.AccelRange {
@@ -133,18 +155,21 @@ func (d *Device) ReadAcceleration() (x int32, y int32, z int32) {
rangeFactor = 12 // the readings in 16G are a bit lower
}
x = int32(int32(int16((uint16(data1[0])<<8|uint16(data2[0])))>>4*rangeFactor) * 1000000 / 1024)
y = int32(int32(int16((uint16(data3[0])<<8|uint16(data4[0])))>>4*rangeFactor) * 1000000 / 1024)
z = int32(int32(int16((uint16(data5[0])<<8|uint16(data6[0])))>>4*rangeFactor) * 1000000 / 1024)
x = int32(int32(int16((uint16(data[1])<<8|uint16(data[0])))>>4*rangeFactor) * 1000000 / 1024)
y = int32(int32(int16((uint16(data[3])<<8|uint16(data[2])))>>4*rangeFactor) * 1000000 / 1024)
z = int32(int32(int16((uint16(data[5])<<8|uint16(data[4])))>>4*rangeFactor) * 1000000 / 1024)
return
}
// ReadPitchRoll reads the current pitch and roll angles from the device and
// returns it in micro-degrees. When the z axis is pointing straight to Earth
// the returned values of pitch and roll would be zero.
func (d *Device) ReadPitchRoll() (pitch int32, roll int32) {
func (d *Device) ReadPitchRoll() (pitch, roll int32, err error) {
x, y, z := d.ReadAcceleration()
x, y, z, err := d.ReadAcceleration()
if err != nil {
return
}
xf, yf, zf := float64(x), float64(y), float64(z)
pitch = int32((math.Round(math.Atan2(yf, math.Sqrt(math.Pow(xf, 2)+math.Pow(zf, 2)))*(180/math.Pi)*100) / 100) * 1000000)
roll = int32((math.Round(math.Atan2(xf, math.Sqrt(math.Pow(yf, 2)+math.Pow(zf, 2)))*(180/math.Pi)*100) / 100) * 1000000)
@@ -154,25 +179,23 @@ func (d *Device) ReadPitchRoll() (pitch int32, roll int32) {
// ReadMagneticField reads the current magnetic field from the device and returns
// it in mG (milligauss). 1 mG = 0.1 µT (microtesla).
func (d *Device) ReadMagneticField() (x int32, y int32, z int32) {
func (d *Device) ReadMagneticField() (x, y, z int32, err error) {
if d.MagSystemMode == MAG_SYSTEM_SINGLE {
cmd := []byte{0}
cmd := d.buf[:1]
cmd[0] = byte(0x80 | d.MagPowerMode<<4 | d.MagDataRate<<2 | d.MagSystemMode)
d.bus.WriteRegister(uint8(d.MagAddress), MAG_MR_REG_M, cmd)
err = d.bus.WriteRegister(uint8(d.MagAddress), MAG_MR_REG_M, cmd)
if err != nil {
return
}
}
data1, data2, data3, data4, data5, data6 := []byte{0}, []byte{0}, []byte{0}, []byte{0}, []byte{0}, []byte{0}
d.bus.ReadRegister(uint8(d.MagAddress), MAG_OUT_X_H_M, data1)
d.bus.ReadRegister(uint8(d.MagAddress), MAG_OUT_X_L_M, data2)
d.bus.ReadRegister(uint8(d.MagAddress), MAG_OUT_Y_H_M, data3)
d.bus.ReadRegister(uint8(d.MagAddress), MAG_OUT_Y_L_M, data4)
d.bus.ReadRegister(uint8(d.MagAddress), MAG_OUT_Z_H_M, data5)
d.bus.ReadRegister(uint8(d.MagAddress), MAG_OUT_Z_L_M, data6)
data := d.buf[0:6]
d.bus.ReadRegister(uint8(d.MagAddress), MAG_OUT_X_L_M, data)
x = int32(int16((uint16(data1[0])<<8 | uint16(data2[0]))))
y = int32(int16((uint16(data3[0])<<8 | uint16(data4[0]))))
z = int32(int16((uint16(data5[0])<<8 | uint16(data6[0]))))
x = int32(int16((uint16(data[1])<<8 | uint16(data[0]))))
y = int32(int16((uint16(data[3])<<8 | uint16(data[2]))))
z = int32(int16((uint16(data[5])<<8 | uint16(data[4]))))
return
}
@@ -182,23 +205,27 @@ func (d *Device) ReadMagneticField() (x int32, y int32, z int32) {
//
// However, the heading may be off due to electronic compasses would be effected
// by strong magnetic fields and require constant calibration.
func (d *Device) ReadCompass() (h int32) {
func (d *Device) ReadCompass() (h int32, err error) {
x, y, _ := d.ReadMagneticField()
x, y, _, err := d.ReadMagneticField()
if err != nil {
return
}
xf, yf := float64(x), float64(y)
h = int32(float32((180/math.Pi)*math.Atan2(yf, xf)) * 1000000)
return
}
// ReadTemperature returns the temperature in Celsius milli degrees (°C/1000)
func (d *Device) ReadTemperature() (c int32, e error) {
func (d *Device) ReadTemperature() (t int32, err error) {
data1, data2 := []byte{0}, []byte{0}
d.bus.ReadRegister(uint8(d.AccelAddress), OUT_TEMP_H_A, data1)
d.bus.ReadRegister(uint8(d.AccelAddress), OUT_TEMP_L_A, data2)
data := d.buf[:2]
err = d.bus.ReadRegister(uint8(d.AccelAddress), OUT_TEMP_L_A, data)
if err != nil {
return
}
t := int16((uint16(data1[0])<<8 | uint16(data2[0]))) >> 4 // temperature offsef from 25 °C
c = int32((float32(25) + float32(t)/8) * 1000)
e = nil
r := int16((uint16(data[1])<<8 | uint16(data[0]))) >> 4 // temperature offset from 25 °C
t = 25000 + int32((float32(r)/8)*1000)
return
}
+91 -39
View File
@@ -5,7 +5,11 @@
//
package lsm6ds3 // import "tinygo.org/x/drivers/lsm6ds3"
import "tinygo.org/x/drivers"
import (
"errors"
"tinygo.org/x/drivers"
)
type AccelRange uint8
type AccelSampleRate uint8
@@ -23,8 +27,7 @@ type Device struct {
accelBandWidth AccelBandwidth
gyroRange GyroRange
gyroSampleRate GyroSampleRate
dataBufferSix []uint8
dataBufferTwo []uint8
buf [6]uint8
}
// Configuration for LSM6DS3 device.
@@ -38,16 +41,26 @@ type Configuration struct {
ResetStepCounter bool
}
// New creates a new LSM6DS3 connection. The I2C bus must already be
// configured.
var errNotConnected = errors.New("lsm6ds3: failed to communicate with acel/gyro sensor")
// 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 drivers.I2C) Device {
return Device{bus: bus, Address: Address}
func New(bus drivers.I2C) *Device {
return &Device{
bus: bus,
Address: Address,
}
}
// Configure sets up the device for communication.
func (d *Device) Configure(cfg Configuration) {
func (d *Device) Configure(cfg Configuration) (err error) {
// Verify unit communication
if !d.Connected() {
return errNotConnected
}
if cfg.AccelRange != 0 {
d.accelRange = cfg.AccelRange
} else {
@@ -78,44 +91,67 @@ func (d *Device) Configure(cfg Configuration) {
d.gyroSampleRate = GYRO_SR_104
}
d.dataBufferSix = make([]uint8, 6)
d.dataBufferTwo = make([]uint8, 2)
data := d.buf[:1]
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)})
data[0] = uint8(ACCEL_2G) | uint8(ACCEL_SR_26)
err = d.bus.WriteRegister(uint8(d.Address), CTRL1_XL, data)
if err != nil {
return
}
// Configure Zen_G, Yen_G, Xen_G, reset steps
data[0] = 0x3C
if cfg.ResetStepCounter {
d.bus.WriteRegister(uint8(d.Address), CTRL10_C, []byte{0x3E})
} else {
d.bus.WriteRegister(uint8(d.Address), CTRL10_C, []byte{0x3C})
data[0] |= 0x02
}
err = d.bus.WriteRegister(uint8(d.Address), CTRL10_C, data)
if err != nil {
return
}
// Enable pedometer
d.bus.WriteRegister(uint8(d.Address), TAP_CFG, []byte{0x40})
data[0] = 0x40
err = d.bus.WriteRegister(uint8(d.Address), TAP_CFG, data)
if err != nil {
return
}
} 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)
err = d.bus.WriteRegister(uint8(d.Address), CTRL1_XL, data)
if err != nil {
return
}
// Set ODR bit
d.bus.ReadRegister(uint8(d.Address), CTRL4_C, data)
err = d.bus.ReadRegister(uint8(d.Address), CTRL4_C, data)
if err != nil {
return
}
data[0] = data[0] &^ BW_SCAL_ODR_ENABLED
data[0] |= BW_SCAL_ODR_ENABLED
d.bus.WriteRegister(uint8(d.Address), CTRL4_C, data)
err = d.bus.WriteRegister(uint8(d.Address), CTRL4_C, data)
if err != nil {
return
}
// Configure gyroscope
data[0] = uint8(d.gyroRange) | uint8(d.gyroSampleRate)
d.bus.WriteRegister(uint8(d.Address), CTRL2_G, data)
err = d.bus.WriteRegister(uint8(d.Address), CTRL2_G, data)
if err != nil {
return
}
}
return nil
}
// 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}
data := d.buf[:1]
d.bus.ReadRegister(uint8(d.Address), WHO_AM_I, data)
return data[0] == 0x69
}
@@ -124,8 +160,12 @@ func (d *Device) Connected() bool {
// 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)
func (d *Device) ReadAcceleration() (x, y, z int32, err error) {
data := d.buf[:6]
err = d.bus.ReadRegister(uint8(d.Address), OUTX_L_XL, data)
if err != nil {
return
}
// k comes from "Table 3. Mechanical characteristics" 3 of the datasheet * 1000
k := int32(61) // 2G
if d.accelRange == ACCEL_4G {
@@ -135,9 +175,9 @@ func (d *Device) ReadAcceleration() (x int32, y int32, z int32) {
} 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
x = int32(int16((uint16(data[1])<<8)|uint16(data[0]))) * k
y = int32(int16((uint16(data[3])<<8)|uint16(data[2]))) * k
z = int32(int16((uint16(data[5])<<8)|uint16(data[4]))) * k
return
}
@@ -145,8 +185,12 @@ func (d *Device) ReadAcceleration() (x int32, y int32, z int32) {
// µ°/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)
func (d *Device) ReadRotation() (x, y, z int32, err error) {
data := d.buf[:6]
err = d.bus.ReadRegister(uint8(d.Address), OUTX_L_G, data)
if err != nil {
return
}
// k comes from "Table 3. Mechanical characteristics" 3 of the datasheet * 1000
k := int32(4375) // 125DPS
if d.gyroRange == GYRO_250DPS {
@@ -158,24 +202,32 @@ func (d *Device) ReadRotation() (x int32, y int32, z int32) {
} 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
x = int32(int16((uint16(data[1])<<8)|uint16(data[0]))) * k
y = int32(int16((uint16(data[3])<<8)|uint16(data[2]))) * k
z = int32(int16((uint16(data[5])<<8)|uint16(data[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)
func (d *Device) ReadTemperature() (t int32, err error) {
data := d.buf[:2]
err = d.bus.ReadRegister(uint8(d.Address), OUT_TEMP_L, data)
if err != nil {
return
}
// 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
t = 25000 + (int32(int16((int16(data[1])<<8)|int16(data[0])))*125)/2
return
}
// 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])))
func (d *Device) ReadSteps() (s int32, err error) {
data := d.buf[:2]
err = d.bus.ReadRegister(uint8(d.Address), STEP_COUNTER_L, data)
if err != nil {
return
}
s = int32(int16((uint16(data[1]) << 8) | uint16(data[0])))
return
}
+189
View File
@@ -0,0 +1,189 @@
// Package lsm6ds3tr implements a driver for the LSM6DS3TR
// a 6 axis Inertial Measurement Unit (IMU)
//
// Datasheet: https://www.st.com/resource/en/datasheet/lsm6ds3tr.pdf
//
package lsm6ds3tr // import "tinygo.org/x/drivers/lsm6ds3tr"
import (
"errors"
"tinygo.org/x/drivers"
)
type AccelRange uint8
type AccelSampleRate uint8
type AccelBandwidth uint8
type GyroRange uint8
type GyroSampleRate uint8
// Device wraps an I2C connection to a LSM6DS3TR device.
type Device struct {
bus drivers.I2C
Address uint16
accelRange AccelRange
accelSampleRate AccelSampleRate
gyroRange GyroRange
gyroSampleRate GyroSampleRate
buf [6]uint8
}
// Configuration for LSM6DS3TR device.
type Configuration struct {
AccelRange AccelRange
AccelSampleRate AccelSampleRate
AccelBandWidth AccelBandwidth
GyroRange GyroRange
GyroSampleRate GyroSampleRate
IsPedometer bool
ResetStepCounter bool
}
var errNotConnected = errors.New("lsm6ds3tr: failed to communicate with acel/gyro sensor")
// New creates a new LSM6DS3TR connection. The I2C bus must already be configured.
//
// This function only creates the Device object, it does not touch the device.
func New(bus drivers.I2C) *Device {
return &Device{
bus: bus,
Address: Address,
}
}
// Configure sets up the device for communication.
func (d *Device) doConfigure(cfg Configuration) (err error) {
// Verify unit communication
if !d.Connected() {
return errNotConnected
}
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.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
}
data := d.buf[:1]
// Configure accelerometer
data[0] = uint8(d.accelRange) | uint8(d.accelSampleRate)
err = d.bus.WriteRegister(uint8(d.Address), CTRL1_XL, data)
if err != nil {
return
}
// Set ODR bit
err = d.bus.ReadRegister(uint8(d.Address), CTRL4_C, data)
if err != nil {
return
}
data[0] = data[0] &^ BW_SCAL_ODR_ENABLED
data[0] |= BW_SCAL_ODR_ENABLED
err = d.bus.WriteRegister(uint8(d.Address), CTRL4_C, data)
if err != nil {
return
}
// Configure gyroscope
data[0] = uint8(d.gyroRange) | uint8(d.gyroSampleRate)
err = d.bus.WriteRegister(uint8(d.Address), CTRL2_G, data)
if err != nil {
return
}
return nil
}
// Connected returns whether a LSM6DS3TR has been found.
// It does a "who am I" request and checks the response.
func (d *Device) Connected() bool {
data := d.buf[:1]
d.bus.ReadRegister(uint8(d.Address), WHO_AM_I, data)
return data[0] == 0x6A
}
// 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, y, z int32, err error) {
data := d.buf[:6]
err = d.bus.ReadRegister(uint8(d.Address), OUTX_L_XL, data)
if err != nil {
return
}
// 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(data[1])<<8)|uint16(data[0]))) * k
y = int32(int16((uint16(data[3])<<8)|uint16(data[2]))) * k
z = int32(int16((uint16(data[5])<<8)|uint16(data[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, y, z int32, err error) {
data := d.buf[:6]
err = d.bus.ReadRegister(uint8(d.Address), OUTX_L_G, data)
if err != nil {
return
}
// k comes from "Table 3. Mechanical characteristics" 3 of the datasheet * 1000
k := int32(4375) // 125DPS
if d.gyroRange == GYRO_245DPS {
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(data[1])<<8)|uint16(data[0]))) * k
y = int32(int16((uint16(data[3])<<8)|uint16(data[2]))) * k
z = int32(int16((uint16(data[5])<<8)|uint16(data[4]))) * k
return
}
// ReadTemperature returns the temperature in celsius milli degrees (°C/1000)
func (d *Device) ReadTemperature() (t int32, err error) {
data := d.buf[:2]
err = d.bus.ReadRegister(uint8(d.Address), OUT_TEMP_L, data)
if err != nil {
return
}
// From "Table 5. Temperature sensor characteristics"
// temp = value/256 + 25
t = 25000 + (int32(int16((int16(data[1])<<8)|int16(data[0])))*125)/32
return
}
+9
View File
@@ -0,0 +1,9 @@
//go:build !xiao_ble
// +build !xiao_ble
package lsm6ds3tr
// Configure sets up the device for communication.
func (d *Device) Configure(cfg Configuration) error {
return d.doConfigure(cfg)
}
+35
View File
@@ -0,0 +1,35 @@
//go:build xiao_ble
// +build xiao_ble
package lsm6ds3tr
import (
"device/nrf"
"machine"
"time"
)
// Configure sets up the device for communication.
func (d *Device) Configure(cfg Configuration) error {
// Following lines are XIAO BLE Sense specific, they have nothing to do with sensor per se
// Implementation adapted from https://github.com/Seeed-Studio/Seeed_Arduino_LSM6DS3/blob/master/LSM6DS3.cpp#L68-L77
// Special mode for IMU power pin on this board.
// Can not use pin.Configure() directly due to special mode and 32 bit size
pinConfig := uint32(nrf.GPIO_PIN_CNF_DIR_Output<<nrf.GPIO_PIN_CNF_DIR_Pos) |
uint32(nrf.GPIO_PIN_CNF_INPUT_Disconnect<<nrf.GPIO_PIN_CNF_INPUT_Pos) |
uint32(nrf.GPIO_PIN_CNF_PULL_Disabled<<nrf.GPIO_PIN_CNF_PULL_Pos) |
uint32(nrf.GPIO_PIN_CNF_DRIVE_H0H1<<nrf.GPIO_PIN_CNF_DRIVE_Pos) |
uint32(nrf.GPIO_PIN_CNF_SENSE_Disabled<<nrf.GPIO_PIN_CNF_SENSE_Pos)
nrf.P1.PIN_CNF[8].Set(pinConfig) // LSM_PWR == P1_08
// Enable IMU
machine.LSM_PWR.High()
// Wait a moment
time.Sleep(10 * time.Millisecond)
// Common initialisation code
return d.doConfigure(cfg)
}
+79
View File
@@ -0,0 +1,79 @@
package lsm6ds3tr
// 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
GYRO_125DPS GyroRange = 0x02
GYRO_245DPS 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
GYRO_SR_3332 GyroSampleRate = 0x90
GYRO_SR_6664 GyroSampleRate = 0xA0
)
+54 -27
View File
@@ -5,7 +5,11 @@
//
package lsm6dsox // import "tinygo.org/x/drivers/lsm6dsox"
import "tinygo.org/x/drivers"
import (
"errors"
"tinygo.org/x/drivers"
)
type AccelRange uint8
type AccelSampleRate uint8
@@ -17,10 +21,9 @@ type GyroSampleRate uint8
type Device struct {
bus drivers.I2C
Address uint16
dataBufferSix []uint8
dataBufferTwo []uint8
accelMultiplier int32
gyroMultiplier int32
buf [6]uint8
}
// Configuration for LSM6DSOX device.
@@ -31,20 +34,25 @@ type Configuration struct {
GyroSampleRate GyroSampleRate
}
var errNotConnected = errors.New("lsm6dsox: failed to communicate with acel/gyro sensor")
// New creates a new LSM6DSOX connection. The I2C bus must already be configured.
//
// This function only creates the Device object, it does not touch the device.
func New(bus drivers.I2C) *Device {
return &Device{
bus: bus,
Address: Address,
dataBufferSix: make([]uint8, 6),
dataBufferTwo: make([]uint8, 2),
bus: bus,
Address: Address,
}
}
// Configure sets up the device for communication.
func (d *Device) Configure(cfg Configuration) {
func (d *Device) Configure(cfg Configuration) (err error) {
// Verify unit communication
if !d.Connected() {
return errNotConnected
}
// Multipliers come from "Table 2. Mechanical characteristics" of the datasheet * 1000
switch cfg.AccelRange {
@@ -68,19 +76,27 @@ func (d *Device) Configure(cfg Configuration) {
d.gyroMultiplier = 70000
}
data := make([]uint8, 1)
data := d.buf[:1]
// Configure accelerometer
data[0] = uint8(cfg.AccelRange) | uint8(cfg.AccelSampleRate)
d.bus.WriteRegister(uint8(d.Address), CTRL1_XL, data)
err = d.bus.WriteRegister(uint8(d.Address), CTRL1_XL, data)
if err != nil {
return
}
// Configure gyroscope
data[0] = uint8(cfg.GyroRange) | uint8(cfg.GyroSampleRate)
d.bus.WriteRegister(uint8(d.Address), CTRL2_G, data)
err = d.bus.WriteRegister(uint8(d.Address), CTRL2_G, data)
if err != nil {
return
}
return nil
}
// Connected returns whether a LSM6DSOX has been found.
// It does a "who am I" request and checks the response.
func (d *Device) Connected() bool {
data := []byte{0}
data := d.buf[:1]
d.bus.ReadRegister(uint8(d.Address), WHO_AM_I, data)
return data[0] == 0x6C
}
@@ -89,11 +105,15 @@ func (d *Device) Connected() bool {
// 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_A, d.dataBufferSix)
x = int32(int16((uint16(d.dataBufferSix[1])<<8)|uint16(d.dataBufferSix[0]))) * d.accelMultiplier
y = int32(int16((uint16(d.dataBufferSix[3])<<8)|uint16(d.dataBufferSix[2]))) * d.accelMultiplier
z = int32(int16((uint16(d.dataBufferSix[5])<<8)|uint16(d.dataBufferSix[4]))) * d.accelMultiplier
func (d *Device) ReadAcceleration() (x, y, z int32, err error) {
data := d.buf[:6]
err = d.bus.ReadRegister(uint8(d.Address), OUTX_L_A, data)
if err != nil {
return
}
x = int32(int16((uint16(data[1])<<8)|uint16(data[0]))) * d.accelMultiplier
y = int32(int16((uint16(data[3])<<8)|uint16(data[2]))) * d.accelMultiplier
z = int32(int16((uint16(data[5])<<8)|uint16(data[4]))) * d.accelMultiplier
return
}
@@ -101,20 +121,27 @@ func (d *Device) ReadAcceleration() (x int32, y int32, z int32) {
// µ°/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)
x = int32(int16((uint16(d.dataBufferSix[1])<<8)|uint16(d.dataBufferSix[0]))) * d.gyroMultiplier
y = int32(int16((uint16(d.dataBufferSix[3])<<8)|uint16(d.dataBufferSix[2]))) * d.gyroMultiplier
z = int32(int16((uint16(d.dataBufferSix[5])<<8)|uint16(d.dataBufferSix[4]))) * d.gyroMultiplier
func (d *Device) ReadRotation() (x, y, z int32, err error) {
data := d.buf[:6]
err = d.bus.ReadRegister(uint8(d.Address), OUTX_L_G, data)
if err != nil {
return
}
x = int32(int16((uint16(data[1])<<8)|uint16(data[0]))) * d.gyroMultiplier
y = int32(int16((uint16(data[3])<<8)|uint16(data[2]))) * d.gyroMultiplier
z = int32(int16((uint16(data[5])<<8)|uint16(data[4]))) * d.gyroMultiplier
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)
func (d *Device) ReadTemperature() (t int32, err error) {
data := d.buf[:2]
err = d.bus.ReadRegister(uint8(d.Address), OUT_TEMP_L, data)
if err != nil {
return
}
// From "Table 4. Temperature sensor characteristics"
// temp = value/256 + 25
t := 25000 + (int32(int16((int16(d.dataBufferTwo[1])<<8)|int16(d.dataBufferTwo[0])))*125)/32
return t, nil
t = 25000 + (int32(int16((int16(data[1])<<8)|int16(data[0])))*125)/32
return
}
+29 -34
View File
@@ -28,8 +28,7 @@ type Device struct {
accelMultiplier int32
gyroMultiplier int32
magMultiplier int32
dataBufferSix []uint8
dataBufferTwo []uint8
buf [6]uint8
}
// Configuration for LSM9DS1 device.
@@ -50,11 +49,9 @@ var errNotConnected = errors.New("lsm9ds1: failed to communicate with either ace
// This function only creates the Device object, it does not touch the device.
func New(bus drivers.I2C) *Device {
return &Device{
bus: bus,
AccelAddress: ACCEL_ADDRESS,
MagAddress: MAG_ADDRESS,
dataBufferSix: make([]uint8, 6),
dataBufferTwo: make([]uint8, 2),
bus: bus,
AccelAddress: ACCEL_ADDRESS,
MagAddress: MAG_ADDRESS,
}
}
@@ -63,17 +60,11 @@ func New(bus drivers.I2C) *Device {
// In a rare case of an I2C bus issue, it can also return an error.
// Case of boolean false and error nil means I2C is up,
// but "who am I" responses have unexpected values.
func (d *Device) Connected() (connected bool, err error) {
data1, data2 := []byte{0}, []byte{0}
err = d.bus.ReadRegister(d.AccelAddress, WHO_AM_I, data1)
if err != nil {
return false, err
}
err = d.bus.ReadRegister(d.MagAddress, WHO_AM_I_M, data2)
if err != nil {
return false, err
}
return data1[0] == 0x68 && data2[0] == 0x3D, nil
func (d *Device) Connected() bool {
data1, data2 := d.buf[:1], d.buf[1:2]
d.bus.ReadRegister(d.AccelAddress, WHO_AM_I, data1)
d.bus.ReadRegister(d.MagAddress, WHO_AM_I_M, data2)
return data1[0] == 0x68 && data2[0] == 0x3D
}
// ReadAcceleration reads the current acceleration from the device and returns
@@ -81,13 +72,14 @@ func (d *Device) Connected() (connected bool, err error) {
// and the sensor is not moving the returned value will be around 1000000 or
// -1000000.
func (d *Device) ReadAcceleration() (x, y, z int32, err error) {
err = d.bus.ReadRegister(uint8(d.AccelAddress), OUT_X_L_XL, d.dataBufferSix)
data := d.buf[:6]
err = d.bus.ReadRegister(uint8(d.AccelAddress), OUT_X_L_XL, data)
if err != nil {
return
}
x = int32(int16((uint16(d.dataBufferSix[1])<<8)|uint16(d.dataBufferSix[0]))) * d.accelMultiplier
y = int32(int16((uint16(d.dataBufferSix[3])<<8)|uint16(d.dataBufferSix[2]))) * d.accelMultiplier
z = int32(int16((uint16(d.dataBufferSix[5])<<8)|uint16(d.dataBufferSix[4]))) * d.accelMultiplier
x = int32(int16((uint16(data[1])<<8)|uint16(data[0]))) * d.accelMultiplier
y = int32(int16((uint16(data[3])<<8)|uint16(data[2]))) * d.accelMultiplier
z = int32(int16((uint16(data[5])<<8)|uint16(data[4]))) * d.accelMultiplier
return
}
@@ -96,38 +88,41 @@ func (d *Device) ReadAcceleration() (x, y, z int32, err error) {
// 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, y, z int32, err error) {
err = d.bus.ReadRegister(uint8(d.AccelAddress), OUT_X_L_G, d.dataBufferSix)
data := d.buf[:6]
err = d.bus.ReadRegister(uint8(d.AccelAddress), OUT_X_L_G, data)
if err != nil {
return
}
x = int32(int16((uint16(d.dataBufferSix[1])<<8)|uint16(d.dataBufferSix[0]))) * d.gyroMultiplier
y = int32(int16((uint16(d.dataBufferSix[3])<<8)|uint16(d.dataBufferSix[2]))) * d.gyroMultiplier
z = int32(int16((uint16(d.dataBufferSix[5])<<8)|uint16(d.dataBufferSix[4]))) * d.gyroMultiplier
x = int32(int16((uint16(data[1])<<8)|uint16(data[0]))) * d.gyroMultiplier
y = int32(int16((uint16(data[3])<<8)|uint16(data[2]))) * d.gyroMultiplier
z = int32(int16((uint16(data[5])<<8)|uint16(data[4]))) * d.gyroMultiplier
return
}
// ReadMagneticField reads the current magnetic field from the device and returns
// it in nT (nanotesla). 1 G (gauss) = 100_000 nT (nanotesla).
func (d *Device) ReadMagneticField() (x, y, z int32, err error) {
err = d.bus.ReadRegister(uint8(d.MagAddress), OUT_X_L_M, d.dataBufferSix)
data := d.buf[:6]
err = d.bus.ReadRegister(uint8(d.MagAddress), OUT_X_L_M, data)
if err != nil {
return
}
x = int32(int16((int16(d.dataBufferSix[1])<<8)|int16(d.dataBufferSix[0]))) * d.magMultiplier
y = int32(int16((int16(d.dataBufferSix[3])<<8)|int16(d.dataBufferSix[2]))) * d.magMultiplier
z = int32(int16((int16(d.dataBufferSix[5])<<8)|int16(d.dataBufferSix[4]))) * d.magMultiplier
x = int32(int16((int16(data[1])<<8)|int16(data[0]))) * d.magMultiplier
y = int32(int16((int16(data[3])<<8)|int16(data[2]))) * d.magMultiplier
z = int32(int16((int16(data[5])<<8)|int16(data[4]))) * d.magMultiplier
return
}
// ReadTemperature returns the temperature in Celsius milli degrees (°C/1000)
func (d *Device) ReadTemperature() (t int32, err error) {
err = d.bus.ReadRegister(uint8(d.AccelAddress), OUT_TEMP_L, d.dataBufferTwo)
data := d.buf[:2]
err = d.bus.ReadRegister(uint8(d.AccelAddress), OUT_TEMP_L, data)
if err != nil {
return
}
// 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
t = 25000 + (int32(int16((int16(data[1])<<8)|int16(data[0])))*125)/2
return
}
@@ -138,7 +133,7 @@ func (d *Device) ReadTemperature() (t int32, err error) {
func (d *Device) doConfigure(cfg Configuration) (err error) {
// Verify unit communication
if con, err := d.Connected(); !con || err != nil {
if !d.Connected() {
return errNotConnected
}
@@ -172,7 +167,7 @@ func (d *Device) doConfigure(cfg Configuration) (err error) {
d.magMultiplier = 58
}
data := make([]byte, 1)
data := d.buf[:1]
// Configure accelerometer
// Sample rate & measurement range
+1 -1
View File
@@ -19,7 +19,7 @@ func (d *Device) Configure(cfg Configuration) error {
machine.I2C_PULLUP.Configure(machine.PinConfig{Mode: machine.PinOutput})
machine.I2C_PULLUP.High()
// Wait a moment
time.Sleep(10 * time.Millisecond)
time.Sleep(100 * time.Millisecond)
// Common initialisation code
return d.doConfigure(cfg)
}
+46
View File
@@ -0,0 +1,46 @@
package makeybutton
const bufferSize = 6
// Buffer is a buffer to keep track of the most recent readings for a button.
type Buffer struct {
readings [bufferSize]bool
index int
}
// NewBuffer returns a new buffer.
func NewBuffer() *Buffer {
return &Buffer{}
}
// Used returns how many bytes in buffer have been used.
func (b *Buffer) Used() int {
return b.index
}
// Put stores a boolean in the buffer.
func (b *Buffer) Put(val bool) bool {
b.index++
if b.index >= bufferSize {
b.index = 0
}
b.readings[b.index] = val
return true
}
// Avg returns the "average" of all the readings in the buffer, by
// treating a true as 1 and a false as -1.
func (b *Buffer) Avg() int {
avg := 0
for i := 0; i < bufferSize; i++ {
if b.readings[i] {
avg += 1
} else {
avg -= 1
}
}
return avg
}
+91
View File
@@ -0,0 +1,91 @@
// Package makeybutton providers a driver for a button that can be triggered
// by anything that is conductive by using an ultra high value resistor.
//
// Inspired by the amazing MakeyMakey
// https://makeymakey.com/
//
package makeybutton
import (
"machine"
"time"
)
// ButtonState represents the state of a MakeyButton.
type ButtonState int
const (
NeverPressed ButtonState = 0
Press = 1
Release = 2
)
// ButtonEvent represents when the state of a Button changes.
type ButtonEvent int
const (
NotChanged ButtonEvent = 0
Pressed = 1
Released = 2
)
// Button is a "button"-like device that acts like a MakeyMakey.
type Button struct {
pin machine.Pin
state ButtonState
readings *Buffer
HighMeansPressed bool
}
// NewButton creates a new Button.
func NewButton(pin machine.Pin) *Button {
return &Button{
pin: pin,
state: NeverPressed,
readings: NewBuffer(),
HighMeansPressed: false,
}
}
// Configure configures the Makey Button pin to have the correct settings to detect touches.
func (b *Button) Configure() error {
// Note that we have to first turn on the pullup, and then turn off the pullup,
// in order for the pin to be properly floating.
b.pin.Configure(machine.PinConfig{Mode: machine.PinInputPullup})
time.Sleep(10 * time.Millisecond)
b.pin.Configure(machine.PinConfig{Mode: machine.PinInput})
b.pin.Set(false)
return nil
}
// Get returns a ButtonEvent based on the most recent state of the button,
// and if it has changed by being pressed or released.
func (b *Button) Get() ButtonEvent {
// if pin is pulled up, a low value means the key is pressed
pressed := !b.pin.Get()
if b.HighMeansPressed {
// otherwise, a high value means the key is pressed
pressed = !pressed
}
avg := b.readings.Avg()
b.readings.Put(pressed)
switch {
case pressed && avg > -1*bufferSize+2:
if b.state == Press {
return NotChanged
}
b.state = Press
return Pressed
case !pressed:
if b.state == Press {
b.state = Release
return Released
}
}
return NotChanged
}
+10 -1
View File
@@ -31,7 +31,16 @@ func (driver *Device) Configure() {
// SetScanLimit sets the scan limit. Maximum is 8.
// Example: a 4 digit 7SegmentDisplay has a scan limit of 4
func (driver *Device) SetScanLimit(digitNumber uint8) {
driver.WriteCommand(byte(REG_SCANLIMIT), byte(digitNumber-1))
driver.WriteCommand(REG_SCANLIMIT, digitNumber-1)
}
// SetIntensity sets the intensity of the diplays.
// There are 16 possible intensity levels. The valid range is 0x00-0x0F
func (driver *Device) SetIntensity(intensity uint8) {
if intensity > 0x0F {
intensity = 0x0F
}
driver.WriteCommand(REG_INTENSITY, intensity)
}
// SetDecodeMode sets the decode mode for 7 segment displays.
+25 -10
View File
@@ -20,6 +20,11 @@ func SetBuf(b []byte) {
}
func (c *Client) Do(req *Request) (*Response, error) {
if c.Jar != nil {
for _, cookie := range c.Jar.Cookies(req.URL) {
req.AddCookie(cookie)
}
}
switch req.URL.Scheme {
case "http":
return c.doHTTP(req)
@@ -31,15 +36,17 @@ func (c *Client) Do(req *Request) (*Response, error) {
}
func (c *Client) doHTTP(req *Request) (*Response, error) {
if c.Jar != nil {
for _, cookie := range c.Jar.Cookies(req.URL) {
req.AddCookie(cookie)
}
}
// make TCP connection
ip := net.ParseIP(req.URL.Host)
raddr := &net.TCPAddr{IP: ip, Port: 80}
ip := net.ParseIP(req.URL.Hostname())
port := 80
if req.URL.Port() != "" {
p, err := strconv.ParseUint(req.URL.Port(), 0, 64)
if err != nil {
return nil, err
}
port = int(p)
}
raddr := &net.TCPAddr{IP: ip, Port: port}
laddr := &net.TCPAddr{Port: 8080}
conn, err := net.DialTCP("tcp", laddr, raddr)
@@ -178,12 +185,17 @@ func (c *Client) doResp(conn net.Conn, req *Request) (*Response, error) {
if err != nil {
println("Read error: " + err.Error())
} else {
idx := bytes.Index(buf[ofs:ofs+n], []byte("\r\n\r\n"))
// Take care of the case where "\r\n\r\n" is on the boundary of a buffer
start := ofs
if start > 3 {
start -= 3
}
idx := bytes.Index(buf[start:ofs+n], []byte("\r\n\r\n"))
if idx == -1 {
ofs += n
continue
}
idx += ofs + 4
idx += start + 4
scanner = bufio.NewScanner(bytes.NewReader(buf[0 : ofs+n]))
if resp.Status == "" && scanner.Scan() {
@@ -261,6 +273,9 @@ func (c *Client) doResp(conn net.Conn, req *Request) (*Response, error) {
return nil, fmt.Errorf("slice out of range : use http.SetBuf() to change the allocation to %d bytes or more", end)
}
n, err := conn.Read(buf[ofs : ofs+0x400])
if err != nil {
return nil, err
}
if n == 0 {
continue
}
+123 -30
View File
@@ -5,6 +5,7 @@ package mqtt
import (
"errors"
"strings"
"sync"
"time"
"github.com/eclipse/paho.mqtt.golang/packets"
@@ -19,20 +20,32 @@ import (
func NewClient(o *ClientOptions) Client {
c := &mqttclient{opts: o, adaptor: o.Adaptor}
c.msgRouter, c.stopRouter = newRouter()
c.inboundPacketChan = make(chan packets.ControlPacket, 10)
c.stopInbound = make(chan struct{})
c.incomingPubChan = make(chan *packets.PublishPacket, 10)
// this launches a goroutine, so only call once per client:
c.msgRouter.matchAndDispatch(c.incomingPubChan, c.opts.Order, c)
return c
}
type mqttclient struct {
adaptor net.Adapter
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
adaptor net.Adapter
conn net.Conn
connected bool
opts *ClientOptions
mid uint16
inboundPacketChan chan packets.ControlPacket
stopInbound chan struct{}
msgRouter *router
stopRouter chan bool
incomingPubChan chan *packets.PublishPacket
// stats for keepalive
lastReceive time.Time
lastSend time.Time
// keep track of routines and signal a shutdown
workers sync.WaitGroup
shutdown bool
}
// AddRoute allows you to add a handler for messages on a specific topic
@@ -56,6 +69,9 @@ func (c *mqttclient) IsConnectionOpen() bool {
// Connect will create a connection to the message broker.
func (c *mqttclient) Connect() Token {
if c.IsConnected() {
return &mqtttoken{}
}
var err error
// make connection
@@ -77,10 +93,6 @@ func (c *mqttclient) Connect() Token {
}
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)
@@ -98,12 +110,19 @@ func (c *mqttclient) Connect() Token {
connectPkt.ClientIdentifier = c.opts.ClientID
connectPkt.ProtocolVersion = byte(c.opts.ProtocolVersion)
connectPkt.ProtocolName = "MQTT"
connectPkt.Keepalive = 60
connectPkt.Keepalive = uint16(c.opts.KeepAlive)
connectPkt.WillFlag = c.opts.WillEnabled
connectPkt.WillTopic = c.opts.WillTopic
connectPkt.WillMessage = c.opts.WillPayload
connectPkt.WillQos = c.opts.WillQos
connectPkt.WillRetain = c.opts.WillRetained
err = connectPkt.Write(c.conn)
if err != nil {
return &mqtttoken{err: err}
}
c.lastSend = time.Now()
// TODO: handle timeout as ReadPacket blocks until it gets a packet.
// CONNECT response.
@@ -121,20 +140,36 @@ func (c *mqttclient) Connect() Token {
}
}
go readMessages(c)
go processInbound(c)
go readMessages(c)
go keepAlive(c)
return &mqtttoken{}
}
// Disconnect will end the connection with the server, but not before waiting
// the specified number of milliseconds to wait for existing work to be
// completed.
// completed. Blocks until disconnected.
func (c *mqttclient) Disconnect(quiesce uint) {
c.conn.Close()
c.shutdownRoutines()
// block until all done
for c.connected {
time.Sleep(time.Millisecond * 10)
}
return
}
// shutdownRoutines will disconnect and shut down all processes. If you want to trigger a
// disconnect internally, make sure you call this instead of Disconnect() to avoid deadlocks
func (c *mqttclient) shutdownRoutines() {
if c.shutdown {
return
}
c.shutdown = true
c.conn.Close()
c.stopInbound <- struct{}{}
}
// Publish will publish a message with the specified QoS and content
// to the specified topic.
// Returns a token to track delivery of the message to the broker
@@ -146,6 +181,8 @@ func (c *mqttclient) Publish(topic string, qos byte, retained bool, payload inte
pub := packets.NewControlPacket(packets.Publish).(*packets.PublishPacket)
pub.Qos = qos
pub.TopicName = topic
pub.Retain = retained
switch payload.(type) {
case string:
pub.Payload = []byte(payload.(string))
@@ -161,6 +198,8 @@ func (c *mqttclient) Publish(topic string, qos byte, retained bool, payload inte
if err != nil {
return &mqtttoken{err: err}
}
// update this for every control message that is sent successfully, for keepalive
c.lastSend = time.Now()
return &mqtttoken{}
}
@@ -188,6 +227,7 @@ func (c *mqttclient) Subscribe(topic string, qos byte, callback MessageHandler)
if err != nil {
return &mqtttoken{err: err}
}
c.lastSend = time.Now()
return &mqtttoken{}
}
@@ -213,12 +253,13 @@ func (c *mqttclient) OptionsReader() ClientOptionsReader {
}
func processInbound(c *mqttclient) {
PROCESS:
for {
select {
case msg := <-c.inbound:
case msg := <-c.inboundPacketChan:
switch m := msg.(type) {
case *packets.PingrespPacket:
// TODO: handle this
// println("pong")
case *packets.SubackPacket:
// TODO: handle this
case *packets.UnsubackPacket:
@@ -235,33 +276,85 @@ func processInbound(c *mqttclient) {
case *packets.PubcompPacket:
// TODO: handle this
}
case <-c.stop:
return
case <-c.stopInbound:
break PROCESS
}
}
// as this routine could be the last to finish (if a lot of messages are queued in the
// channel), it is the last to turn out the lights
c.workers.Wait()
c.connected = false
c.shutdown = false
}
// readMessages reads incoming messages off the wire.
// incoming messages are then send into inbound channel.
// incoming messages are then send into inbound buffered channel.
func readMessages(c *mqttclient) {
c.workers.Add(1)
defer c.workers.Done()
var err error
var cp packets.ControlPacket
PROCESS:
for {
for !c.shutdown {
if cp, err = c.ReadPacket(); err != nil {
break PROCESS
c.shutdownRoutines()
return
}
if cp != nil {
c.inbound <- cp
// TODO: Notify keepalive logic that we recently received a packet
c.inboundPacketChan <- cp
// notify keepalive logic that we recently received a packet
c.lastReceive = time.Now()
}
time.Sleep(100 * time.Millisecond)
}
}
// TODO: handle if we received an error on read.
// If disconnect is in progress, swallow error and return
// keepAlive is a goroutine to handle sending ping requests according to the MQTT spec. If the keepalive time has
// been reached with no messages being sent, we will send a ping request and check back to see if we've
// had any activity by the timeout. If not, disconnect.
func keepAlive(c *mqttclient) {
c.workers.Add(1)
defer c.workers.Done()
var err error
var ping *packets.PingreqPacket
var timeout, pingsent time.Time
for !c.shutdown {
// As long as we haven't reached the keepalive value...
if time.Since(c.lastSend) < time.Duration(c.opts.KeepAlive)*time.Second {
// ...sleep and check shutdown status again
time.Sleep(time.Millisecond * 100)
continue
}
// value has been reached, so send a ping request
ping = packets.NewControlPacket(packets.Pingreq).(*packets.PingreqPacket)
if err = ping.Write(c.conn); err != nil {
// if connection is lost, report disconnect
c.shutdownRoutines()
return
}
// println("ping")
c.lastSend = time.Now()
pingsent = time.Now()
timeout = pingsent.Add(c.opts.PingTimeout)
// as long as we are still connected and haven't received anything after the ping...
for !c.shutdown && c.lastReceive.Before(pingsent) {
// if the timeout has passed, disconnect
if time.Now().After(timeout) {
c.shutdownRoutines()
return
}
time.Sleep(time.Millisecond * 100)
}
}
}
func (c *mqttclient) ackFunc(packet *packets.PublishPacket) func() {
+18 -1
View File
@@ -210,7 +210,7 @@ type ClientOptions struct {
// NewClientOptions returns a new ClientOptions struct.
func NewClientOptions() *ClientOptions {
return &ClientOptions{Adaptor: net.ActiveDevice, ProtocolVersion: 4}
return &ClientOptions{Adaptor: net.ActiveDevice, ProtocolVersion: 4, KeepAlive: 60, PingTimeout: time.Second * 10}
}
// AddBroker adds a broker URI to the list of brokers to be used. The format should be
@@ -257,6 +257,23 @@ func (o *ClientOptions) SetPassword(p string) *ClientOptions {
return o
}
// SetKeepAlive will set the amount of time (in seconds) that the client
// should wait before sending a PING request to the broker. This will
// allow the client to know that a connection has not been lost with the
// server.
func (o *ClientOptions) SetKeepAlive(k time.Duration) *ClientOptions {
o.KeepAlive = int64(k / time.Second)
return o
}
// SetPingTimeout will set the amount of time (in seconds) that the client
// will wait after sending a PING request to the broker, before deciding
// that the connection has been lost. Default is 10 seconds.
func (o *ClientOptions) SetPingTimeout(k time.Duration) *ClientOptions {
o.PingTimeout = k
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
+3 -7
View File
@@ -89,18 +89,14 @@ func (d Dev) Top() uint32 {
}
// Set sets the `on` value of a PWM channel in the range [0..15].
// Max value `on` can take is 4095.
// Example:
// d.Set(1, d.Top()/4)
// sets the dutycycle of second (LED1) channel to 25%.
func (d Dev) Set(channel uint8, on uint32) {
switch {
case on > maxtop:
panic("pca9685: value must be in range 0..4096")
case on == 0:
d.SetPhased(channel, 0, maxtop)
return
if on > maxtop {
panic("pca9685: value must be in range 0..4095")
}
d.SetPhased(channel, on, 0)
}
+53
View File
@@ -0,0 +1,53 @@
package pca9685
import (
"encoding/binary"
"tinygo.org/x/drivers"
)
// 16 PWM channels, 2 2 byte values each (on, off 16bits)
const buffLen = 16 * 2 * 2
// DevBuffered provides a way of performing one-shot writes
// on all PWM signals. This is useful when working with systems
// which require as little as possible I/O overhead.
type DevBuffered struct {
Dev
// LED buffer, first value is address, following values correspond to LED registers.
// [0]: LEDSTART register address
// [1:5]: LED0 corresponding to PWM channel 0
// [5:9]: LED1 PWM channel 1
// ...
// [1 + N*4 : 1 + N*4 + 4] : channnel N up to channel 15
ledBuf [buffLen + 1]byte
}
// New creates a new instance of a PCA9685 device. It performs
// no IO on the i2c bus.
func NewBuffered(bus drivers.I2C, addr uint8) *DevBuffered {
db := &DevBuffered{
Dev: New(bus, addr),
}
db.ledBuf[0] = LEDSTART
return db
}
// PrepSet prepares a value to be written to the
// channel's PWM register on Update() call.
func (b *DevBuffered) PrepSet(channel uint8, on uint32) {
b.PrepPhasedSet(channel, on, 0)
}
// PrepPhasedSet prepares a phased PWM value to be written to the
// channel's register on Update() call.
func (b *DevBuffered) PrepPhasedSet(channel uint8, on, off uint32) {
onLReg := 1 + channel*4
binary.LittleEndian.PutUint16(b.ledBuf[onLReg:], uint16(on)&maxtop)
binary.LittleEndian.PutUint16(b.ledBuf[onLReg+2:], uint16(off)&maxtop)
}
// Update writes the prepared values to the PWM device registers in one shot.
func (b *DevBuffered) Update() error {
return b.bus.Tx(uint16(b.addr), b.ledBuf[:], nil)
}
+4 -1
View File
@@ -118,6 +118,9 @@ func (r *RTL8720DN) ConnectSSLSocket(addr, port string) error {
if r.debug {
fmt.Printf("ConnectSSLSocket(%q, %q)\r\n", addr, port)
}
if r.root_ca == nil {
return fmt.Errorf("root_ca is not set")
}
client, err := r.Rpc_wifi_ssl_client_create()
if err != nil {
@@ -224,7 +227,7 @@ func (r *RTL8720DN) DisconnectSocket() error {
fmt.Printf("DisconnectSocket()\r\n")
}
switch r.connectionType {
case ConnectionTypeTCP:
case ConnectionTypeTCP, ConnectionTypeUDP:
_, err := r.Rpc_lwip_close(r.socket)
if err != nil {
return err
+25
View File
@@ -0,0 +1,25 @@
package scd4x
const (
// Address is default I2C address.
Address = 0x62
CmdDataReady = 0xE4B8
CmdFactoryReset = 0x3632
CmdForcedRecal = 0x362F
CmdGetAltitude = 0x2322
CmdGetASCE = 0x2313
CmdGetTempOffset = 0x2318
CmdPersistSettings = 0x3615
CmdReadMeasurement = 0xEC05
CmdReinit = 0x3646
CmdSelfTest = 0x3639
CmdSerialNumber = 0x3682
CmdSetAltitude = 0x2427
CmdSetASCE = 0x2416
CmdSetPressure = 0xE000
CmdSetTempOffset = 0x241D
CmdStartLowPowerPeriodicMeasurement = 0x21AC
CmdStartPeriodicMeasurement = 0x21B1
CmdStopPeriodicMeasurement = 0x3F86
)
+180
View File
@@ -0,0 +1,180 @@
// Package scd4x provides a driver for the scd4x I2C envrironment sensor.
//
// Datasheet: https://sensirion.com/media/documents/C4B87CE6/627C2DCD/CD_DS_SCD40_SCD41_Datasheet_D1.pdf
//
// This driver is heavily influenced by the scd4x code from Adafruit for CircuitPython:
// https://github.com/adafruit/Adafruit_CircuitPython_SCD4X
// Thank you!
//
package scd4x // import "tinygo.org/x/drivers/scd4x"
import (
"encoding/binary"
"time"
"tinygo.org/x/drivers"
)
type Device struct {
bus drivers.I2C
tx []byte
rx []byte
Address uint8
// used to cache the most recent readings
co2 uint16
temperature uint16
humidity uint16
}
// New returns SCD4x device for the provided I2C bus using default address of 0x62.
func New(i2c drivers.I2C) *Device {
return &Device{
bus: i2c,
tx: make([]byte, 5),
rx: make([]byte, 18),
Address: Address,
}
}
// Configure the device.
func (d *Device) Configure() (err error) {
if err := d.StopPeriodicMeasurement(); err != nil {
return err
}
time.Sleep(500 * time.Millisecond)
// reset the chip
if err := d.sendCommand(CmdReinit); err != nil {
return err
}
time.Sleep(20 * time.Millisecond)
return
}
// Connected returns whether sensor has been found.
func (d *Device) Connected() bool {
// TODO: something here to check if the sensor is connected
return true
}
// DataReady checks the sensor to see if new data is available.
func (d *Device) DataReady() (bool, error) {
if err := d.sendCommandWithResult(CmdDataReady, d.rx[0:3]); err != nil {
return false, err
}
return !(d.rx[0]&0x07 == 0 && d.rx[1] == 0), nil
}
// StartPeriodicMeasurement puts the sensor into working mode, about 5s per measurement.
func (d *Device) StartPeriodicMeasurement() error {
return d.sendCommand(CmdStartPeriodicMeasurement)
}
// StopPeriodicMeasurement stops the sensor reading data.
func (d *Device) StopPeriodicMeasurement() error {
return d.sendCommand(CmdStopPeriodicMeasurement)
}
// StartLowPowerPeriodicMeasurement puts the sensor into low power working mode,
// about 30s per measurement.
func (d *Device) StartLowPowerPeriodicMeasurement() error {
return d.sendCommand(CmdStartLowPowerPeriodicMeasurement)
}
// ReadData reads the data from the sensor and caches it.
func (d *Device) ReadData() error {
if err := d.sendCommandWithResult(CmdReadMeasurement, d.rx[0:9]); err != nil {
return err
}
d.co2 = binary.BigEndian.Uint16(d.rx[0:2])
d.temperature = binary.BigEndian.Uint16(d.rx[3:5])
d.humidity = binary.BigEndian.Uint16(d.rx[6:8])
return nil
}
// ReadCO2 returns the CO2 concentration in PPM (parts per million).
func (d *Device) ReadCO2() (co2 int32, err error) {
ok, err := d.DataReady()
if err != nil {
return 0, err
}
if ok {
err = d.ReadData()
}
return int32(d.co2), err
}
// ReadTemperature returns the temperature in celsius milli degrees (°C/1000)
func (d *Device) ReadTemperature() (temperature int32, err error) {
ok, err := d.DataReady()
if err != nil {
return 0, err
}
if ok {
err = d.ReadData()
}
// temp = -45 + 175 * value / 2¹⁶
return (-1 * 45000) + (21875 * (int32(d.temperature)) / 8192), err
}
// ReadTempC returns the value in the temperature value in Celsius.
func (d *Device) ReadTempC() float32 {
t, _ := d.ReadTemperature()
return float32(t) / 1000
}
// ReadTempF returns the value in the temperature value in Fahrenheit.
func (d *Device) ReadTempF() float32 {
return d.ReadTempC()*1.8 + 32.0
}
// ReadHumidity returns the current relative humidity in %rH.
func (d *Device) ReadHumidity() (humidity int32, err error) {
ok, err := d.DataReady()
if err != nil {
return 0, err
}
if ok {
err = d.ReadData()
}
// humidity = 100 * value / 2¹⁶
return (25 * int32(d.humidity)) / 16384, err
}
func (d *Device) sendCommand(command uint16) error {
binary.BigEndian.PutUint16(d.tx[0:], command)
return d.bus.Tx(uint16(d.Address), d.tx[0:2], nil)
}
func (d *Device) sendCommandWithValue(command, value uint16) error {
binary.BigEndian.PutUint16(d.tx[0:], command)
binary.BigEndian.PutUint16(d.tx[2:], value)
d.tx[4] = crc8(d.tx[2:4])
return d.bus.Tx(uint16(d.Address), d.tx[0:5], nil)
}
func (d *Device) sendCommandWithResult(command uint16, result []byte) error {
binary.BigEndian.PutUint16(d.tx[0:], command)
if err := d.bus.Tx(uint16(d.Address), d.tx[0:2], nil); err != nil {
return err
}
time.Sleep(time.Millisecond)
return d.bus.Tx(uint16(d.Address), nil, result)
}
func crc8(buf []byte) uint8 {
var crc uint8 = 0xff
for _, b := range buf {
crc ^= b
for i := 0; i < 8; i++ {
if crc&0x80 != 0 {
crc = (crc << 1) ^ 0x31
} else {
crc <<= 1
}
}
}
return crc & 0xff
}
+15
View File
@@ -0,0 +1,15 @@
package scd4x
import (
"testing"
qt "github.com/frankban/quicktest"
"tinygo.org/x/drivers/tester"
)
func TestDefaultI2CAddress(t *testing.T) {
c := qt.New(t)
bus := tester.NewI2CBus(c)
dev := New(bus)
c.Assert(dev.Address, qt.Equals, uint8(Address))
}
+2 -2
View File
@@ -28,7 +28,7 @@ var (
)
type Device struct {
bus machine.SPI
bus *machine.SPI
sck machine.Pin
sdo machine.Pin
sdi machine.Pin
@@ -41,7 +41,7 @@ type Device struct {
CSD *CSD
}
func New(b machine.SPI, sck, sdo, sdi, cs machine.Pin) Device {
func New(b *machine.SPI, sck, sdo, sdi, cs machine.Pin) Device {
return Device{
bus: b,
cs: cs,
+37
View File
@@ -0,0 +1,37 @@
package ssd1289
import "machine"
type pinBus struct {
pins [16]machine.Pin
}
func NewPinBus(pins [16]machine.Pin) pinBus {
for i := 0; i < 16; i++ {
pins[i].Configure(machine.PinConfig{Mode: machine.PinOutput})
}
return pinBus{
pins: pins,
}
}
func (b pinBus) Set(data uint16) {
b.pins[15].Set((data & (1 << 15)) != 0)
b.pins[14].Set((data & (1 << 14)) != 0)
b.pins[13].Set((data & (1 << 13)) != 0)
b.pins[12].Set((data & (1 << 12)) != 0)
b.pins[11].Set((data & (1 << 11)) != 0)
b.pins[10].Set((data & (1 << 10)) != 0)
b.pins[9].Set((data & (1 << 9)) != 0)
b.pins[8].Set((data & (1 << 8)) != 0)
b.pins[7].Set((data & (1 << 7)) != 0)
b.pins[6].Set((data & (1 << 6)) != 0)
b.pins[5].Set((data & (1 << 5)) != 0)
b.pins[4].Set((data & (1 << 4)) != 0)
b.pins[3].Set((data & (1 << 3)) != 0)
b.pins[2].Set((data & (1 << 2)) != 0)
b.pins[1].Set((data & (1 << 1)) != 0)
b.pins[0].Set((data & (1 << 0)) != 0)
}
+23
View File
@@ -0,0 +1,23 @@
package ssd1289
type Command byte
const (
OSCILLATIONSTART Command = 0x00
DRIVEROUTPUTCONTROL = 0x01
POWERCONTROL1 = 0x03
POWERCONTROL2 = 0x0C
POWERCONTROL3 = 0x0D
POWERCONTROL4 = 0x0E
POWERCONTROL5 = 0x1E
DISPLAYCONTROL = 0x07
SLEEPMODE = 0x10
ENTRYMODE = 0x11
LCDDRIVEACCONTROL = 0x02
HORIZONTALRAMADDRESSPOSITION = 0x44
VERTICALRAMADDRESSSTARTPOSITION = 0x45
VERTICALRAMADDRESSENDPOSITION = 0x46
SETGDDRAMYADDRESSCOUNTER = 0x4F
SETGDDRAMXADDRESSCOUNTER = 0x4E
RAMDATAREADWRITE = 0x22
)
+32
View File
@@ -0,0 +1,32 @@
//go:build rp2040
// +build rp2040
package ssd1289
import (
"device/rp"
"machine"
)
type rp2040Bus struct {
firstPin machine.Pin
}
func NewRP2040Bus(firstPin machine.Pin) rp2040Bus {
for i := uint8(0); i < 16; i++ {
pin := machine.Pin(i + uint8(firstPin))
pin.Configure(machine.PinConfig{Mode: machine.PinOutput})
}
return rp2040Bus{
firstPin: firstPin,
}
}
func (b rp2040Bus) Set(data uint16) {
data32 := uint32(data)
rp.SIO.GPIO_OUT_CLR.Set(0xFFFF << b.firstPin)
rp.SIO.GPIO_OUT_SET.Set(data32 << b.firstPin)
}
+200
View File
@@ -0,0 +1,200 @@
// Package ssd1289 implements a driver for the SSD1289 led matrix controller as packaged on the TFT_320QVT board
//
// Datasheet: http://aitendo3.sakura.ne.jp/aitendo_data/product_img/lcd/tft2/M032C1289TP/3.2-SSD1289.pdf
//
package ssd1289
import (
"image/color"
"machine"
"time"
)
type Bus interface {
Set(data uint16)
}
type Device struct {
rs machine.Pin
wr machine.Pin
cs machine.Pin
rst machine.Pin
bus Bus
}
const width = int16(240)
const height = int16(320)
func New(rs machine.Pin, wr machine.Pin, cs machine.Pin, rst machine.Pin, bus Bus) Device {
d := Device{
rs: rs,
wr: wr,
cs: cs,
rst: rst,
bus: bus,
}
rs.Configure(machine.PinConfig{Mode: machine.PinOutput})
wr.Configure(machine.PinConfig{Mode: machine.PinOutput})
cs.Configure(machine.PinConfig{Mode: machine.PinOutput})
rst.Configure(machine.PinConfig{Mode: machine.PinOutput})
cs.High()
rst.High()
wr.High()
return d
}
func (d *Device) lcdWriteCom(cmd Command) {
d.rs.Low()
d.lcdWriteBusInt(uint16(cmd))
}
func (d *Device) lcdWriteDataInt(data uint16) {
d.rs.High()
d.lcdWriteBusInt(data)
}
func (d *Device) lcdWriteComData(cmd Command, data uint16) {
d.lcdWriteCom(cmd)
d.lcdWriteDataInt(data)
}
func (d *Device) tx() {
d.wr.Low()
d.wr.High()
}
func (d *Device) lcdWriteBusInt(data uint16) {
d.bus.Set(data)
d.tx()
}
func (d *Device) Configure() {
d.rst.High()
time.Sleep(time.Millisecond * 5)
d.rst.Low()
time.Sleep(time.Millisecond * 15)
d.rst.High()
time.Sleep(time.Millisecond * 15)
d.cs.Low()
//Power supply setting
d.lcdWriteComData(POWERCONTROL1, 0xA8A4)
d.lcdWriteComData(POWERCONTROL2, 0x0000)
d.lcdWriteComData(POWERCONTROL3, 0x080C)
d.lcdWriteComData(POWERCONTROL4, 0x2B00)
d.lcdWriteComData(POWERCONTROL5, 0x00B7)
//Set R07h at 0021h
d.lcdWriteComData(DISPLAYCONTROL, 0x021)
//Set R00h at 0001h
d.lcdWriteComData(OSCILLATIONSTART, 0x0001)
//Set R07h at 0021h
d.lcdWriteComData(DISPLAYCONTROL, 0x023)
//Set R10h at 0000h, Exit sleep mode
d.lcdWriteComData(SLEEPMODE, 0x0000)
//Wait 30ms
time.Sleep(time.Millisecond * 30)
//Set R07h at 0033h
d.lcdWriteComData(DISPLAYCONTROL, 0x033)
//Entry Mode setting (R11h)
//DFM 11 --> 65k
//TRANS 0
//OEDEF 0
//WMODE 0 --> Normal data bus
//DMODE 00 --> Ram
//TY 01 --> 262k Type A not used as we are in 65k mode.
//ID 11 --> Horizontal & Vertical increment
//AM 0 --> Horizontal
//LG 000 --> No compare register usage
d.lcdWriteComData(ENTRYMODE, 0x6030)
//LCD Driver AC Setting
//I couldn't make sense of the documentation fortunately 0 seems to
//FLD 0 --> Normal driving
//ENWS 0 --> POR mode
//BC 1 --> Less flicker
//EOR 1 --> Less stripey
//WSMD 0 --> not used in POR mode
//NW 0 --> Least flicker
d.lcdWriteComData(LCDDRIVEACCONTROL, 0x0600)
//End of documented init
//RL 0 --> Output shift direction
//REV 1 --> Reverse colors
//CAD 0 --> Cs on common
//BGR 0 --> use RGB color assignment
//SM 0 --> standard gate scan sequence
//TB 1 --> Display is mirrored with 0
//MUX 319 --> Number of lines in display
d.lcdWriteComData(DRIVEROUTPUTCONTROL, 0x233F)
d.cs.High()
}
func (d *Device) setXY(x1 uint16, y1 uint16, x2 uint16, y2 uint16) {
d.lcdWriteComData(HORIZONTALRAMADDRESSPOSITION, (x2<<8)+x1)
d.lcdWriteComData(VERTICALRAMADDRESSSTARTPOSITION, y1)
d.lcdWriteComData(VERTICALRAMADDRESSENDPOSITION, y2)
d.lcdWriteComData(SETGDDRAMXADDRESSCOUNTER, x1)
d.lcdWriteComData(SETGDDRAMYADDRESSCOUNTER, y1)
d.lcdWriteCom(RAMDATAREADWRITE)
}
func (d *Device) ClearDisplay() {
d.FillDisplay(color.RGBA{0, 0, 0, 255})
}
func (d *Device) FillDisplay(c color.RGBA) {
d.FillRect(0, 0, width, height, c)
}
func encodeColor(c color.RGBA) uint16 {
encoded := (uint16(c.B)&248)<<8 | (uint16(c.G)&252)<<3 | (uint16(c.R)&248)>>3
return encoded
}
func (d *Device) SetPixel(x, y int16, c color.RGBA) {
encoded := encodeColor(c)
d.cs.Low()
d.setXY(uint16(x), uint16(y), uint16(x), uint16(y))
d.rs.High()
d.lcdWriteBusInt(encoded)
d.cs.High()
}
func (d *Device) FillRect(x, y, w, h int16, c color.RGBA) {
encoded := encodeColor(c)
d.cs.Low()
d.setXY(uint16(x), uint16(y), uint16(x+(w-1)), uint16(y+(h-1)))
d.rs.High()
d.bus.Set(encoded)
for i := int64(0); i < int64(w)*int64(h); i++ {
d.tx()
}
d.cs.High()
d.rs.Low()
}
func (d *Device) Display() error {
//Not enough memory to store an entire screen on most microcontrollers
return nil
}
func (d *Device) Size() (x, y int16) {
return width, height
}
+1 -1
View File
@@ -103,7 +103,7 @@ func (d *Device) Configure(cfg Config) {
d.Command(SET_MUX_RATIO)
d.Data(0x7F)
d.Command(SET_REMAP_COLORDEPTH)
d.Data(0x72)
d.Data(0x62)
d.Command(SET_COLUMN_ADDRESS)
d.Data(0x00)
d.Data(0x7F)
+153
View File
@@ -0,0 +1,153 @@
package uc8151
// Registers
const (
// Display resolution
EPD_WIDTH = 128
EPD_HEIGHT = 296
PSR = 0x00
PWR = 0x01
POF = 0x02
PFS = 0x03
PON = 0x04
PMES = 0x05
BTST = 0x06
DSLP = 0x07
DTM1 = 0x10
DSP = 0x11
DRF = 0x12
DTM2 = 0x13
LUT_VCOM = 0x20
LUT_WW = 0x21
LUT_BW = 0x22
LUT_WB = 0x23
LUT_BB = 0x24
PLL = 0x30
TSC = 0x40
TSE = 0x41
TSR = 0x43
TSW = 0x42
CDI = 0x50
LPD = 0x51
TCON = 0x60
TRES = 0x61
REV = 0x70
FLG = 0x71
AMV = 0x80
VV = 0x81
VDCS = 0x82
PTL = 0x90
PTIN = 0x91
PTOU = 0x92
PGM = 0xa0
APG = 0xa1
ROTP = 0xa2
CCSET = 0xe0
PWS = 0xe3
TSSET = 0xe5
RES_96x230 = 0b00000000
RES_96x252 = 0b01000000
RES_128x296 = 0b10000000
RES_160x296 = 0b11000000
LUT_OTP = 0b00000000
LUT_REG = 0b00100000
FORMAT_BWR = 0b00000000
FORMAT_BW = 0b00010000
SCAN_DOWN = 0b00000000
SCAN_UP = 0b00001000
SHIFT_LEFT = 0b00000000
SHIFT_RIGHT = 0b00000100
BOOSTER_OFF = 0b00000000
BOOSTER_ON = 0b00000010
RESET_SOFT = 0b00000000
RESET_NONE = 0b00000001
VDS_EXTERNAL = 0b00000000
VDS_INTERNAL = 0b00000010
VDG_EXTERNAL = 0b00000000
VDG_INTERNAL = 0b00000001
VCOM_VD = 0b00000000
VCOM_VG = 0b00000100
VGHL_16V = 0b00000000
VGHL_15V = 0b00000001
VGHL_14V = 0b00000010
VGHL_13V = 0b00000011
START_10MS = 0b00000000
START_20MS = 0b01000000
START_30MS = 0b10000000
START_40MS = 0b11000000
STRENGTH_1 = 0b00000000
STRENGTH_2 = 0b00001000
STRENGTH_3 = 0b00010000
STRENGTH_4 = 0b00011000
STRENGTH_5 = 0b00100000
STRENGTH_6 = 0b00101000
STRENGTH_7 = 0b00110000
STRENGTH_8 = 0b00111000
OFF_0_27US = 0b00000000
OFF_0_34US = 0b00000001
OFF_0_40US = 0b00000010
OFF_0_54US = 0b00000011
OFF_0_80US = 0b00000100
OFF_1_54US = 0b00000101
OFF_3_34US = 0b00000110
OFF_6_58US = 0b00000111
FRAMES_1 = 0b00000000
FRAMES_2 = 0b00010000
FRAMES_3 = 0b00100000
FRAMES_4 = 0b00110000
TEMP_INTERNAL = 0b00000000
TEMP_EXTERNAL = 0b10000000
OFFSET_0 = 0b00000000
OFFSET_1 = 0b00000001
OFFSET_2 = 0b00000010
OFFSET_3 = 0b00000011
OFFSET_4 = 0b00000100
OFFSET_5 = 0b00000101
OFFSET_6 = 0b00000110
OFFSET_7 = 0b00000111
OFFSET_MIN_8 = 0b00001000
OFFSET_MIN_7 = 0b00001001
OFFSET_MIN_6 = 0b00001010
OFFSET_MIN_5 = 0b00001011
OFFSET_MIN_4 = 0b00001100
OFFSET_MIN_3 = 0b00001101
OFFSET_MIN_2 = 0b00001110
OFFSET_MIN_1 = 0b00001111
HZ_29 = 0b00111111
HZ_33 = 0b00111110
HZ_40 = 0b00111101
HZ_50 = 0b00111100
HZ_67 = 0b00111011
HZ_100 = 0b00111010
HZ_200 = 0b00111001
NO_ROTATION Rotation = 0
ROTATION_90 Rotation = 1 // 90 degrees clock-wise rotation
ROTATION_180 Rotation = 2
ROTATION_270 Rotation = 3
DEFAULT Speed = 0
MEDIUM Speed = 1
FAST Speed = 2
TURBO Speed = 3
)
+634
View File
@@ -0,0 +1,634 @@
// Package uc8151 implements a driver for e-ink displays controlled by UC8151
//
// Inspired by https://github.com/pimoroni/pimoroni-pico/blob/main/drivers/uc8151/uc8151.cpp
// Datasheet: https://www.buydisplay.com/download/ic/UC8151C.pdf
//
package uc8151 // import "tinygo.org/x/drivers/uc8151"
import (
"errors"
"image/color"
"machine"
"time"
"tinygo.org/x/drivers"
)
type Config struct {
Width int16
Height int16
Rotation Rotation // Rotation is clock-wise
Speed Speed // Value from DEFAULT, MEDIUM, FAST, TURBO
Blocking bool
}
type Device struct {
bus drivers.SPI
cs machine.Pin
dc machine.Pin
rst machine.Pin
busy machine.Pin
width int16
height int16
buffer []uint8
bufferLength uint32
rotation Rotation
speed Speed
blocking bool
}
type Rotation uint8
type Speed uint8
// New returns a new epd2in13x driver. Pass in a fully configured SPI bus.
func New(bus drivers.SPI, csPin, dcPin, rstPin, busyPin machine.Pin) Device {
csPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
dcPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
rstPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
busyPin.Configure(machine.PinConfig{Mode: machine.PinInput})
return Device{
bus: bus,
cs: csPin,
dc: dcPin,
rst: rstPin,
busy: busyPin,
}
}
// Configure sets up the device.
func (d *Device) Configure(cfg Config) {
if cfg.Width != 0 {
d.width = cfg.Width
} else {
d.width = EPD_WIDTH
}
if cfg.Height != 0 {
d.height = cfg.Height
} else {
d.height = EPD_HEIGHT
}
d.rotation = cfg.Rotation
d.speed = cfg.Speed
d.blocking = cfg.Blocking
d.bufferLength = (uint32(d.width) * uint32(d.height)) / 8
d.buffer = make([]uint8, d.bufferLength)
for i := uint32(0); i < d.bufferLength; i++ {
d.buffer[i] = 0xFF
}
d.Reset()
d.SendCommand(PSR)
if d.speed == 0 {
d.SendData(RES_128x296 | LUT_OTP | FORMAT_BW | SHIFT_RIGHT | BOOSTER_ON | RESET_NONE | SCAN_UP)
} else {
d.SendData(RES_128x296 | LUT_REG | FORMAT_BW | SHIFT_RIGHT | BOOSTER_ON | RESET_NONE | SCAN_UP)
}
d.SetLUT(d.speed)
d.SendCommand(PWR)
d.SendData(VDS_INTERNAL | VDG_INTERNAL)
d.SendData(VCOM_VG | VGHL_16V)
d.SendData(0x2B)
d.SendData(0x2B)
d.SendData(0x2B)
d.SendCommand(PON)
d.WaitUntilIdle()
d.SendCommand(BTST)
d.SendData(START_10MS | STRENGTH_3 | OFF_6_58US)
d.SendData(START_10MS | STRENGTH_3 | OFF_6_58US)
d.SendData(START_10MS | STRENGTH_3 | OFF_6_58US)
d.SendCommand(PFS)
d.SendData(FRAMES_1)
d.SendCommand(TSE)
d.SendData(TEMP_INTERNAL | OFFSET_0)
d.SendCommand(TCON)
d.SendData(0x22)
d.SendCommand(CDI)
d.SendData(0x4C) // 4C //5C
d.SendCommand(PLL)
d.SendData(HZ_100)
d.SendCommand(POF)
d.WaitUntilIdle()
}
// Reset resets the device
func (d *Device) Reset() {
d.rst.Low()
time.Sleep(10 * time.Millisecond)
d.rst.High()
time.Sleep(10 * time.Millisecond)
d.WaitUntilIdle()
}
// PowerOff power off the device
func (d *Device) PowerOff() {
d.SendCommand(POF)
}
// SendCommand sends a command to the display
func (d *Device) SendCommand(command uint8) {
d.sendDataCommand(true, command)
}
// SendData sends a data byte to the display
func (d *Device) SendData(data uint8) {
d.sendDataCommand(false, data)
}
// sendDataCommand sends image data or a command to the screen
func (d *Device) sendDataCommand(isCommand bool, data uint8) {
if isCommand {
d.dc.Low()
} else {
d.dc.High()
}
d.cs.Low()
d.bus.Transfer(data)
d.cs.High()
}
// SetPixel modifies the internal buffer in a single pixel.
// The display have 2 colors: black and white
// We use RGBA(0,0,0, 255) as white (transparent)
// Anything else as black
func (d *Device) SetPixel(x int16, y int16, c color.RGBA) {
x, y = d.xy(x, y)
if x < 0 || x >= d.width || y < 0 || y >= d.height {
return
}
byteIndex := x/8 + y*(d.width/8)
if c.R == 0 && c.G == 0 && c.B == 0 { // TRANSPARENT / WHITE
d.buffer[byteIndex] &^= 0x80 >> uint8(x%8)
} else { // WHITE / EMPTY
d.buffer[byteIndex] |= 0x80 >> uint8(x%8)
}
}
// Display sends the buffer to the screen.
func (d *Device) Display() error {
if d.blocking {
d.WaitUntilIdle()
}
d.SendCommand(PON)
d.SendCommand(PTOU)
d.SendCommand(DTM2)
for i := uint32(0); i < d.bufferLength; i++ {
d.SendData(d.buffer[i])
}
d.SendCommand(DSP)
d.SendCommand(DRF)
if d.blocking {
d.WaitUntilIdle()
d.PowerOff()
}
return nil
}
// DisplayRect sends only an area of the buffer to the screen.
// The rectangle points need to be a multiple of 8 in the screen.
// They might not work as expected if the screen is rotated.
func (d *Device) DisplayRect(x int16, y int16, width int16, height int16) error {
if d.blocking {
d.WaitUntilIdle()
}
x, y = d.xy(x, y)
if x < 0 || y < 0 || x >= d.width || y >= d.height || width < 0 || height < 0 {
return errors.New("wrong rectangle")
}
if d.rotation == ROTATION_90 {
width, height = height, width
x -= width
} else if d.rotation == ROTATION_180 {
x -= width - 1
y -= height - 1
} else if d.rotation == ROTATION_270 {
width, height = height, width
y -= height
}
x &= 0xF8
width &= 0xF8
width = x + width // reuse variables
if width >= d.width {
width = d.width
}
height = y + height
if height > d.height {
height = d.height
}
d.SendCommand(PON)
d.SendCommand(PTIN)
d.SendCommand(PTL)
d.SendData(uint8(x))
d.SendData(uint8(x+width-1) | 0x07)
d.SendData(uint8(y >> 8))
d.SendData(uint8(y))
d.SendData(uint8((y + height - 1) >> 8))
d.SendData(uint8(y + height - 1))
d.SendData(0x01)
d.SendCommand(DTM2)
x = x / 8
width = width / 8
for ; y < height; y++ {
for i := x; i < width; i++ {
d.SendData(d.buffer[i+y*(d.width/8)])
}
}
d.SendCommand(DSP)
d.SendCommand(DRF)
if d.blocking {
d.WaitUntilIdle()
d.PowerOff()
}
return nil
}
// ClearDisplay erases the device SRAM
func (d *Device) ClearDisplay() {
d.ClearBuffer()
d.Display()
}
// WaitUntilIdle waits until the display is ready
func (d *Device) WaitUntilIdle() {
for !d.busy.Get() {
time.Sleep(100 * time.Millisecond)
}
}
// IsBusy returns the busy status of the display
func (d *Device) IsBusy() bool {
return d.busy.Get()
}
// ClearBuffer sets the buffer to 0xFF (white)
func (d *Device) ClearBuffer() {
for i := uint32(0); i < d.bufferLength; i++ {
d.buffer[i] = 0x00
}
}
// Size returns the current size of the display.
func (d *Device) Size() (w, h int16) {
if d.rotation == ROTATION_90 || d.rotation == ROTATION_270 {
return d.height, d.width
}
return d.width, d.height
}
// SetRotation changes the rotation (clock-wise) of the device
func (d *Device) SetRotation(rotation Rotation) {
d.rotation = rotation
}
// SetBlocking changes the blocking flag of the device
func (d *Device) SetBlocking(blocking bool) {
d.blocking = blocking
}
// xy chages the coordinates according to the rotation
func (d *Device) xy(x, y int16) (int16, int16) {
switch d.rotation {
case NO_ROTATION:
return x, y
case ROTATION_90:
return d.width - y - 1, x
case ROTATION_180:
return d.width - x - 1, d.height - y - 1
case ROTATION_270:
return y, d.height - x - 1
}
return x, y
}
// SetSpeed changes the refresh speed of the device (the display needs to re-configure)
func (d *Device) SetSpeed(speed Speed) {
d.Configure(Config{
Width: d.width,
Height: d.height,
Rotation: d.rotation,
Speed: speed,
Blocking: d.blocking,
})
}
// Invert sets the display' invert mode
func (d *Device) Invert(invert bool) {
if invert {
d.SendData(0x5C)
} else {
d.SendData(0x4C)
}
}
// SetLUT sets the look up tables for full or partial updates
func (d *Device) SetLUT(speed Speed) {
switch speed {
case MEDIUM:
var lut = [44]uint8{
0x00, 0x16, 0x16, 0x0d, 0x00, 0x01,
0x00, 0x23, 0x23, 0x00, 0x00, 0x02,
0x00, 0x16, 0x16, 0x0d, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00,
}
d.SendCommand(LUT_VCOM)
for i := 0; i < 44; i++ {
d.SendData(lut[i])
}
lut = [44]uint8{
0x54, 0x16, 0x16, 0x0d, 0x00, 0x01,
0x60, 0x23, 0x23, 0x00, 0x00, 0x02,
0xa8, 0x16, 0x16, 0x0d, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
d.SendCommand(LUT_WW)
// do not send last two bytes
for i := 0; i < 42; i++ {
d.SendData(lut[i])
}
lut = [44]uint8{
0x54, 0x16, 0x16, 0x0d, 0x00, 0x01,
0x60, 0x23, 0x23, 0x00, 0x00, 0x02,
0xa8, 0x16, 0x16, 0x0d, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
d.SendCommand(LUT_BW)
// do not send last two bytes
for i := 0; i < 42; i++ {
d.SendData(lut[i])
}
lut = [44]uint8{
0xa8, 0x16, 0x16, 0x0d, 0x00, 0x01,
0x60, 0x23, 0x23, 0x00, 0x00, 0x02,
0x54, 0x16, 0x16, 0x0d, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
d.SendCommand(LUT_WB)
// do not send last two bytes
for i := 0; i < 42; i++ {
d.SendData(lut[i])
}
lut = [44]uint8{
0xa8, 0x16, 0x16, 0x0d, 0x00, 0x01,
0x60, 0x23, 0x23, 0x00, 0x00, 0x02,
0x54, 0x16, 0x16, 0x0d, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
d.SendCommand(LUT_BB)
// do not send last two bytes
for i := 0; i < 42; i++ {
d.SendData(lut[i])
}
break
case FAST:
var lut = [44]uint8{
0x00, 0x04, 0x04, 0x07, 0x00, 0x01,
0x00, 0x0c, 0x0c, 0x00, 0x00, 0x02,
0x00, 0x04, 0x04, 0x07, 0x00, 0x02,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00,
}
d.SendCommand(LUT_VCOM)
for i := 0; i < 44; i++ {
d.SendData(lut[i])
}
lut = [44]uint8{
0x54, 0x04, 0x04, 0x07, 0x00, 0x01,
0x60, 0x0c, 0x0c, 0x00, 0x00, 0x02,
0xa8, 0x04, 0x04, 0x07, 0x00, 0x02,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
d.SendCommand(LUT_WW)
// do not send last two bytes
for i := 0; i < 42; i++ {
d.SendData(lut[i])
}
lut = [44]uint8{
0x54, 0x04, 0x04, 0x07, 0x00, 0x01,
0x60, 0x0c, 0x0c, 0x00, 0x00, 0x02,
0xa8, 0x04, 0x04, 0x07, 0x00, 0x02,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
d.SendCommand(LUT_BW)
// do not send last two bytes
for i := 0; i < 42; i++ {
d.SendData(lut[i])
}
lut = [44]uint8{
0xa8, 0x04, 0x04, 0x07, 0x00, 0x01,
0x60, 0x0c, 0x0c, 0x00, 0x00, 0x02,
0x54, 0x04, 0x04, 0x07, 0x00, 0x02,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
d.SendCommand(LUT_WB)
// do not send last two bytes
for i := 0; i < 42; i++ {
d.SendData(lut[i])
}
lut = [44]uint8{
0xa8, 0x04, 0x04, 0x07, 0x00, 0x01,
0x60, 0x0c, 0x0c, 0x00, 0x00, 0x02,
0x54, 0x04, 0x04, 0x07, 0x00, 0x02,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
d.SendCommand(LUT_BB)
// do not send last two bytes
for i := 0; i < 42; i++ {
d.SendData(lut[i])
}
break
case TURBO:
var lut = [44]uint8{
0x00, 0x01, 0x01, 0x02, 0x00, 0x01,
0x00, 0x02, 0x02, 0x00, 0x00, 0x02,
0x00, 0x02, 0x02, 0x03, 0x00, 0x02,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00,
}
d.SendCommand(LUT_VCOM)
for i := 0; i < 44; i++ {
d.SendData(lut[i])
}
lut = [44]uint8{
0x54, 0x01, 0x01, 0x02, 0x00, 0x01,
0x60, 0x02, 0x02, 0x00, 0x00, 0x02,
0xa8, 0x02, 0x02, 0x03, 0x00, 0x02,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
d.SendCommand(LUT_WW)
// do not send last two bytes
for i := 0; i < 42; i++ {
d.SendData(lut[i])
}
lut = [44]uint8{
0x54, 0x01, 0x01, 0x02, 0x00, 0x01,
0x60, 0x02, 0x02, 0x00, 0x00, 0x02,
0xa8, 0x02, 0x02, 0x03, 0x00, 0x02,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
d.SendCommand(LUT_BW)
// do not send last two bytes
for i := 0; i < 42; i++ {
d.SendData(lut[i])
}
lut = [44]uint8{
0xa8, 0x01, 0x01, 0x02, 0x00, 0x01,
0x60, 0x02, 0x02, 0x00, 0x00, 0x02,
0x54, 0x02, 0x02, 0x03, 0x00, 0x02,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
d.SendCommand(LUT_WB)
// do not send last two bytes
for i := 0; i < 42; i++ {
d.SendData(lut[i])
}
lut = [44]uint8{
0xa8, 0x01, 0x01, 0x02, 0x00, 0x01,
0x60, 0x02, 0x02, 0x00, 0x00, 0x02,
0x54, 0x02, 0x02, 0x03, 0x00, 0x02,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
d.SendCommand(LUT_BB)
// do not send last two bytes
for i := 0; i < 42; i++ {
d.SendData(lut[i])
}
break
default:
var lut = [44]uint8{
0x00, 0x64, 0x64, 0x37, 0x00, 0x01,
0x00, 0x8c, 0x8c, 0x00, 0x00, 0x04,
0x00, 0x64, 0x64, 0x37, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00,
}
d.SendCommand(LUT_VCOM)
for i := 0; i < 44; i++ {
d.SendData(lut[i])
}
lut = [44]uint8{
0x54, 0x64, 0x64, 0x37, 0x00, 0x01,
0x60, 0x8c, 0x8c, 0x00, 0x00, 0x04,
0xa8, 0x64, 0x64, 0x37, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
d.SendCommand(LUT_WW)
// do not send last two bytes
for i := 0; i < 42; i++ {
d.SendData(lut[i])
}
lut = [44]uint8{
0x54, 0x64, 0x64, 0x37, 0x00, 0x01,
0x60, 0x8c, 0x8c, 0x00, 0x00, 0x04,
0xa8, 0x64, 0x64, 0x37, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
d.SendCommand(LUT_BW)
// do not send last two bytes
for i := 0; i < 42; i++ {
d.SendData(lut[i])
}
lut = [44]uint8{
0xa8, 0x64, 0x64, 0x37, 0x00, 0x01,
0x60, 0x8c, 0x8c, 0x00, 0x00, 0x04,
0x54, 0x64, 0x64, 0x37, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
d.SendCommand(LUT_WB)
// do not send last two bytes
for i := 0; i < 42; i++ {
d.SendData(lut[i])
}
lut = [44]uint8{
0xa8, 0x64, 0x64, 0x37, 0x00, 0x01,
0x60, 0x8c, 0x8c, 0x00, 0x00, 0x04,
0x54, 0x64, 0x64, 0x37, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
d.SendCommand(LUT_BB)
// do not send last two bytes
for i := 0; i < 42; i++ {
d.SendData(lut[i])
}
break
}
}
+1 -1
View File
@@ -2,4 +2,4 @@ package drivers
// Version returns a user-readable string showing the version of the drivers package for support purposes.
// Update this value before release of new version of software.
const Version = "0.19.0"
const Version = "0.22.0"

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