Compare commits

..

70 Commits

Author SHA1 Message Date
Ron Evans e4f6fbcb52 release: update changelog for v0.2.0
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-06-04 07:47:44 +02:00
Ron Evans 7487c6b3a2 docs: correct badge and link in README for godocs to use custom domain
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-06-03 19:45:18 +02:00
Ron Evans d1553458f5 bme280: add package docs, and add bme280 to README
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-06-03 19:39:30 +02:00
Daniel Esteban 79d3609f76 Support for AT24C32/64 2-wire serial EEPROM 2019-06-03 19:38:04 +02:00
Ayke van Laethem 00a9b9db77 ws2812: better support the nrf52832
This has cost me _hours_ to find. There are many clones of the ws2812
and they have slightly different timing characteristics. Some don't work
so well when you get close to the minimum T1H time: a 1-bit may be
interpreted as a 0-bit.
2019-06-03 19:25:24 +02:00
Martin Treml f68388702d Implementation of the BME280 Sensor (#38)
* Driver for bme280
2019-06-03 19:03:53 +02:00
Ron Evans 0722fe7161 docs: add CHANGELOG for first official release of drivers
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-05-29 16:28:08 +02:00
Ron Evans c09f0a8075 all: switch to using custom domain for all drivers
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-05-29 13:33:50 +02:00
Ron Evans 7757122598 docs: add contribution guidelines link to README and also show dev branch status in CI build
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-05-27 08:58:41 +02:00
Ron Evans 93849555f3 docs: add contribution guidelines to repo
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-05-27 08:54:36 +02:00
Ron Evans 3f946868e1 all: refactor all current drivers to use new machine.Pin interface
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-05-26 21:46:33 +02:00
Daniel Esteban 2c0bde0dad microbitmatrix: fix for dev branch / volatile package/API (#63)
* microbitmatrix: fix for dev branch / volatile package/API
2019-05-25 18:02:47 +02:00
Ron Evans 848385d625 docs: just to make clear what the driver is for
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-05-24 15:52:47 +02:00
k-brk dce063e0c2 Support for DS1307 (#58)
* Support for DS1307
2019-05-24 12:56:26 +02:00
Daniel Esteban 30b118cf63 support for Waveshare EPD 2.13" display (black&white only) (#61)
* support for Waveshare EPD 2.13" display (black&white only)
2019-05-24 12:37:02 +02:00
Ron Evans 9127719933 docs: add GPS to README
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-05-23 15:36:00 +02:00
Anthony Elder a24657d1eb GPS driver (#55)
* u-blox GPS driver for both I2C and UART
2019-05-23 15:29:29 +02:00
Ron Evans c259da1f17 build: use Makefile test command for CI tests
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-05-17 21:16:13 +02:00
Daniel Esteban 374d3f6bec Support for Waveshare 2.13" (B & C versions) e-paper display (#56)
* Support for Waveshare 2.13" (B) e-paper display
2019-05-17 07:55:57 +02:00
Anthony Elder 5930c149d9 SHT3x driver (#53)
* SHT3x driver
2019-05-12 16:11:15 +02:00
Daniel Esteban d22cf7a3e1 Driver for BBC micro:bit on-board LED matrix (#48) 2019-05-05 16:51:04 +02:00
Ron Evans a9e5c8f710 build: use tinygo-dev docker container for CI
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-05-05 16:28:38 +02:00
Daniel Esteban ad2b76be8b support for SSD1306 displays using I2C or SPI, several screen sizes (#49)
supported too
2019-05-05 11:45:26 +02:00
k-brk ba4de1efb8 SetPixel removed, formatting 2019-05-05 11:42:38 +02:00
k-brk 4560c61c61 GPIO implements io readwriter interface, simplified display, smoke tests 2019-05-05 11:42:38 +02:00
k-brk 715829cdbd Support for HD44780 in 4 and 8 bit mode 2019-05-05 11:42:38 +02:00
Ron Evans 8ac1fa2b22 adxl345: update ADXL345 driver and example to make uniform with other accelerometers
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-05-03 20:30:03 +02:00
Ron Evans b7fe897b8c mag3110: change method name/signature for obtaining temperature to be uniform with other similar drivers
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-05-03 20:26:43 +02:00
Daniel Esteban 35a7590c2d clean commit, added smoke tests 2019-05-03 11:44:56 +02:00
Ron Evans de22b8839e ci: ensure all examples are included in smoke tests
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-04-26 09:28:16 +02:00
Daniel Esteban 38e0d39f48 Initial support for RGB led matrix, usually called hub75 2019-04-26 09:24:08 +02:00
Ron Evans 59db5fef76 thermistor: add support for thermistors such as the NTC 3950
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-04-25 11:30:10 +02:00
Ron Evans c0cadf8d82 lis3dh: implement accelerometer functionality
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-04-24 20:10:57 +02:00
Ron Evans 720097ebad ci: perform go fmt test on all files
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-04-24 19:07:41 +02:00
Daniel Esteban 772a1044c4 Define common display interface (#31)
* issue #27 definition of Displayer interface
2019-04-24 18:56:49 +02:00
Ron Evans cd992bd7d6 bmp180: change method names/signature for obtaining temperature and pressure uniform with other similar drivers
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-04-24 15:17:26 +02:00
Ron Evans f6b9080f9e ci: show CircleCI badge in README
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-04-22 22:21:30 +02:00
Ron Evans 137e667d5a ci: setup CircleCI to build an example per driver as a smoke test suite
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-04-22 22:16:47 +02:00
Ron Evans cc391102ff ci: put CircleCI config file under correct name
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-04-22 19:01:31 +02:00
Ron Evans df3b3aa1ca ci: first start at setting up CircleCI for smoke tests
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-04-22 19:00:40 +02:00
Ron Evans 84b1cd0bfb docs: improve godocs formatting
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-04-22 11:12:28 +02:00
Ron Evans 8e384a00f1 docs: improve godocs formatting
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-04-22 11:11:28 +02:00
Ron Evans 7006a675ad docs: improve godocs formatting
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-04-22 11:10:11 +02:00
Ron Evans 1895c84912 docs: improve godocs info used by main package to explain what the package is and how to use it properly
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-04-22 11:07:43 +02:00
Ron Evans 3c68510cbc docs: improve godocs info used by main package to explain what the package is and how to use it
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-04-22 10:56:22 +02:00
Daniel Esteban a332118087 added DS3231 driver to README.md 2019-04-08 08:18:42 +02:00
Daniel Esteban ed2d334d55 Driver for ADXL345 3-axis digital accelerometer (#33)
* Driver for ADXL345 3-axis digital accelerometer
2019-04-08 08:18:03 +02:00
Daniel Esteban f6bdc9734f ds3231: basic support for DS3231 RTC device 2019-04-07 16:55:46 +02:00
Ron Evans d9b426b90b i2c: refactor all i2c drivers to use Address property of Driver. Allows variations to override the default address as discussed in #14 (#30)
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-04-07 10:50:34 +02:00
Ron Evans b8e278e08f examples: add example code for BlinkM
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-03-14 16:34:11 +01:00
Ayke van Laethem b0c4f3e2ce mma8653: return acceleration in a standard unit: micro-G. 2019-03-11 17:14:04 +01:00
Ron Evans c83475d09d docs: add VL53L1X to list of supported devices
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-03-06 17:49:37 +01:00
Daniel Esteban f144bfed79 Basic support for VL53L1X time-of-flight distance sensor (#20)
* Basic support for VL53L1X time-of-flight distance sensor
2019-03-06 17:42:17 +01:00
Ron Evans 8e1df6d63a examples: add basic usage example for mma8653
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-03-04 21:42:02 +01:00
Ron Evans 239c10a6ab examples: add example for mag3110 magnetometer
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-03-04 21:33:32 +01:00
Ron Evans 55d12ce0a1 examples: add basic usage example for ws2812
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-02-28 14:22:14 +01:00
Ayke van Laethem 7116778235 ws2812: add support for M0 at 48MHz: most SAMD21 boards 2019-02-24 23:10:10 +01:00
Ron Evans 0f7b458d9c docs: update README for current list of drivers
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-02-24 13:33:40 +01:00
Ron Evans 0c2924cd77 espat: add support for esp8266/esp32 Wifi adaptor AT command set with net-compatible UDP support
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-02-24 12:54:11 +01:00
Daniel Esteban ae53ea7d81 easystepper: very simple 4-wire stepper driver 2019-02-23 13:41:52 +01:00
Ayke van Laethem 4ff437e5d4 ws2812: add nrf52 support, update timings for m0 and avr
This commit adds support for the nrf52, which is a Cortex-M4 running at
64MHz.
While implementing this, I discovered that the timings for AVR/16MHz and
Cortex-M0 at 16MHz weren't quite right. They do work, but were slightly
out of spec. So I fixed this as well.
2019-02-22 07:10:22 +01:00
Ron Evans 1475a63798 docs: add BH1750 to README
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-02-08 09:10:02 +01:00
Daniel Esteban 693e7a1db7 Added driver for BH1750 digital ambient light sensor (#15)
* Added driver for BH1750 digital ambient light sensor
2019-02-08 09:04:58 +01:00
Ron Evans 31bc74101d examples: add example for apa102 (#17)
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-02-07 14:52:05 +01:00
Ron Evans 7dfd72c1b1 examples: add example for mpu6050 (#16)
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-02-07 14:51:45 +01:00
Ron Evans bb56d49ce3 rename: make sure package name is consistent in all places after move to organization
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-02-07 09:41:24 +01:00
Ron Evans 96b5daee8e license: add BSD-3 license for the TinyGo Authors
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-02-07 09:18:27 +01:00
Ron Evans be048a35bf docs: commit initial version of README file
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-02-07 08:35:07 +01:00
Daniel Esteban 3af2f18437 Initial support for BMP180 digital pressure sensor (#11)
* Initial support for BMP180 digital pressure sensor
Note that Pressure & Temperature return int32 and millis unit (millidegrees
and mPa)
2019-02-06 08:50:40 +01:00
Ron Evans 773b01e911 apa102: add support for APA102 SPI controlled LEDs
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2018-12-22 19:14:02 +01:00
102 changed files with 8068 additions and 72 deletions
+18
View File
@@ -0,0 +1,18 @@
# 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
working_directory: /usr/local/go/src/tinygo.org/x/drivers
steps:
- checkout
- run: tinygo version
- run:
name: "Enforce Go Formatted Code"
command: make fmt-check
- run:
name: "Run build and smoke tests"
command: make smoke-test
+1
View File
@@ -0,0 +1 @@
build
+36
View File
@@ -0,0 +1,36 @@
0.2.0
---
- **new devices**
- AT24C32/64 2-wire serial EEPROM
- BME280 humidity/pressure sensor
- **bugfixes**
- ws2812: better support for nrf52832
0.1.0
---
- **first release**
- This is the first official release of the TinyGo drivers repo, matching TinyGo 0.6.0. The following devices are supported:
- ADXL345
- APA102
- BH1750
- BlinkM
- BMP180
- DS1307
- DS3231
- Easystepper
- ESP8266/ESP32
- GPS
- HUB75
- LIS3DH
- MAG3110
- microbit LED matrix
- MMA8653
- MPU6050
- PCD8544
- SHT3x
- SSD1306
- Thermistor
- VL53L1X
- Waveshare 2.13"
- Waveshare 2.13" (B & C)
- WS2812
+38
View File
@@ -0,0 +1,38 @@
# How to contribute
Thank you for your interest in improving the TinyGo drivers.
We would like your help to make this project better, so we appreciate any contributions. See if one of the following descriptions matches your situation:
### New to TinyGo
We'd love to get your feedback on getting started with TinyGo. Run into any difficulty, confusion, or anything else? You are not alone. We want to know about your experience, so we can help the next people. Please open a Github issue with your questions, or you can also get in touch directly with us on our Slack channel at [https://gophers.slack.com/messages/CDJD3SUP6](https://gophers.slack.com/messages/CDJD3SUP6).
### One of the TinyGo drivers is not working as you expect
Please open a Github issue with your problem, and we will be happy to assist.
### Some specific hardware you want to use does not appear to be in the TinyGo drivers
We probably have not implemented it yet. Your contribution adding the hardware support to TinyGo would be greatly appreciated.
## How to use our Github repository
The `master` branch of this repo will always have the latest released version of the TinyGo drivers. All of the active development work for the next release will take place in the `dev` branch. The TinyGo drivers will use semantic versioning and will create a tag/release for each release.
Here is how to contribute back some code or documentation:
- Fork repo
- Create a feature branch off of the `dev` branch
- Make some useful change
- Make sure the tests still pass
- Submit a pull request against the `dev` branch.
- Be kind
## How to run tests
To run the tests:
```
make test
```
+27
View File
@@ -0,0 +1,27 @@
Copyright (c) 2018-2019 The TinyGo Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+47
View File
@@ -0,0 +1,47 @@
clean:
@rm -rf build
FMT_PATHS = ./*.go ./examples/**/*.go
fmt-check:
@unformatted=$$(gofmt -l $(FMT_PATHS)); [ -z "$$unformatted" ] && exit 0; echo "Unformatted:"; for fn in $$unformatted; do echo " $$fn"; done; exit 1
smoke-test:
@mkdir -p build
tinygo build -size short -o ./build/test.elf -target=itsybitsy-m0 ./examples/adxl345/main.go
tinygo build -size short -o ./build/test.elf -target=itsybitsy-m0 ./examples/apa102/main.go
tinygo build -size short -o ./build/test.elf -target=microbit ./examples/at24cx/main.go
tinygo build -size short -o ./build/test.elf -target=itsybitsy-m0 ./examples/bh1750/main.go
tinygo build -size short -o ./build/test.elf -target=itsybitsy-m0 ./examples/blinkm/main.go
tinygo build -size short -o ./build/test.elf -target=itsybitsy-m0 ./examples/bmp180/main.go
tinygo build -size short -o ./build/test.elf -target=bluepill ./examples/ds1307/sram/main.go
tinygo build -size short -o ./build/test.elf -target=bluepill ./examples/ds1307/time/main.go
tinygo build -size short -o ./build/test.elf -target=itsybitsy-m0 ./examples/ds3231/main.go
tinygo build -size short -o ./build/test.elf -target=microbit ./examples/easystepper/main.go
tinygo build -size short -o ./build/test.elf -target=itsybitsy-m0 ./examples/espat/espconsole/main.go
tinygo build -size short -o ./build/test.elf -target=itsybitsy-m0 ./examples/espat/esphub/main.go
tinygo build -size short -o ./build/test.elf -target=itsybitsy-m0 ./examples/espat/espstation/main.go
tinygo build -size short -o ./build/test.elf -target=feather-m0 ./examples/gps/i2c/main.go
tinygo build -size short -o ./build/test.elf -target=feather-m0 ./examples/gps/uart/main.go
tinygo build -size short -o ./build/test.elf -target=microbit ./examples/hd44780/customchar/main.go
tinygo build -size short -o ./build/test.elf -target=microbit ./examples/hd44780/text/main.go
tinygo build -size short -o ./build/test.elf -target=microbit ./examples/hub75/main.go
tinygo build -size short -o ./build/test.elf -target=circuitplay-express ./examples/lis3dh/main.go
tinygo build -size short -o ./build/test.elf -target=itsybitsy-m0 ./examples/mag3110/main.go
tinygo build -size short -o ./build/test.elf -target=microbit ./examples/microbitmatrix/main.go
tinygo build -size short -o ./build/test.elf -target=itsybitsy-m0 ./examples/mma8653/main.go
tinygo build -size short -o ./build/test.elf -target=itsybitsy-m0 ./examples/mpu6050/main.go
tinygo build -size short -o ./build/test.elf -target=microbit ./examples/pcd8544/setbuffer/main.go
tinygo build -size short -o ./build/test.elf -target=microbit ./examples/pcd8544/setpixel/main.go
tinygo build -size short -o ./build/test.elf -target=microbit ./examples/sht3x/main.go
tinygo build -size short -o ./build/test.elf -target=microbit ./examples/ssd1306/i2c_128x32/main.go
tinygo build -size short -o ./build/test.elf -target=microbit ./examples/ssd1306/spi_128x64/main.go
tinygo build -size short -o ./build/test.elf -target=circuitplay-express ./examples/thermistor/main.go
tinygo build -size short -o ./build/test.elf -target=itsybitsy-m0 ./examples/vl53l1x/main.go
tinygo build -size short -o ./build/test.elf -target=microbit ./examples/waveshare-epd/epd2in13/main.go
tinygo build -size short -o ./build/test.elf -target=microbit ./examples/waveshare-epd/epd2in13x/main.go
tinygo build -size short -o ./build/test.elf -target=circuitplay-express ./examples/ws2812/main.go
tinygo build -size short -o ./build/test.elf -target=trinket-m0 ./examples/bme280/main.go
test: clean fmt-check smoke-test
+92
View File
@@ -0,0 +1,92 @@
# TinyGo Drivers
[![GoDoc](https://godoc.org/tinygo.org/x/drivers?status.svg)](https://godoc.org/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)
This package provides a collection of hardware drivers for devices that can be used together with [TinyGo](https://tinygo.org).
## Installing
```shell
go get tinygo.org/x/drivers
```
## How to use
Here is an example in TinyGo that uses the BMP180 digital barometer:
```go
package main
import (
"time"
"machine"
"tinygo.org/x/drivers/bmp180"
)
func main() {
machine.I2C0.Configure(machine.I2CConfig{})
sensor := bmp180.New(machine.I2C0)
sensor.Configure()
connected := sensor.Connected()
if !connected {
println("BMP180 not detected")
return
}
println("BMP180 detected")
for {
temp, _ := sensor.ReadTemperature()
println("Temperature:", float32(temp)/1000, "ºC")
pressure, _ := sensor.ReadPressure()
println("Pressure", float32(pressure)/100000, "hPa")
time.Sleep(2 * time.Second)
}
}
```
## Currently supported devices
| Device Name | Interface Type |
|----------|-------------|
| [ADXL345 accelerometer](http://www.analog.com/media/en/technical-documentation/data-sheets/ADXL345.pdf) | I2C |
| [APA102 RGB LED](https://cdn-shop.adafruit.com/product-files/2343/APA102C.pdf) | SPI |
| [AT24CX 2-wire serial EEPROM](https://www.openimpulse.com/blog/wp-content/uploads/wpsc/downloadables/24C32-Datasheet.pdf) | I2C |
| [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 |
| [BMP180 barometer](https://cdn-shop.adafruit.com/datasheets/BST-BMP180-DS000-09.pdf) | I2C |
| [DS1307 real time clock](https://datasheets.maximintegrated.com/en/ds/DS1307.pdf) | I2C |
| [DS3231 real time clock](https://datasheets.maximintegrated.com/en/ds/DS3231.pdf) | I2C |
| ["Easystepper" stepper motor controller](https://en.wikipedia.org/wiki/Stepper_motor) | GPIO |
| [ESP8266/ESP32 AT Command set for WiFi/TCP/UDP](https://github.com/espressif/esp32-at) | UART |
| [GPS module](https://www.u-blox.com/en/product/neo-6-series) | I2C/UART |
| [HUB75 RGB led matrix](https://cdn-learn.adafruit.com/downloads/pdf/32x16-32x32-rgb-led-matrix.pdf) | SPI |
| [LIS3DH accelerometer](https://www.st.com/resource/en/datasheet/lis3dh.pdf) | I2C |
| [MAG3110 magnetometer](https://www.nxp.com/docs/en/data-sheet/MAG3110.pdf) | I2C |
| [BBC micro:bit LED matrix](https://github.com/bbcmicrobit/hardware/blob/master/SCH_BBC-Microbit_V1.3B.pdf) | GPIO |
| [MMA8653 accelerometer](https://www.nxp.com/docs/en/data-sheet/MMA8653FC.pdf) | I2C |
| [MPU6050 accelerometer/gyroscope](https://store.invensense.com/datasheets/invensense/MPU-6050_DataSheet_V3%204.pdf) | I2C |
| [PCD8544 display](http://eia.udg.edu/~forest/PCD8544_1.pdf) | SPI |
| [SHT3x Digital Humidity Sensor](https://www.sensirion.com/fileadmin/user_upload/customers/sensirion/Dokumente/0_Datasheets/Humidity/Sensirion_Humidity_Sensors_SHT3x_Datasheet_digital.pdf) | I2C |
| [SSD1306 OLED display](https://cdn-shop.adafruit.com/datasheets/SSD1306.pdf) | I2C / SPI |
| [Thermistor](https://www.farnell.com/datasheets/33552.pdf) | ADC |
| [VL53L1X time-of-flight distance sensor](https://www.st.com/resource/en/datasheet/vl53l1x.pdf) | I2C |
| [Waveshare 2.13" e-paper display](https://www.waveshare.com/w/upload/e/e6/2.13inch_e-Paper_Datasheet.pdf) | SPI |
| [Waveshare 2.13" (B & C) e-paper display](https://www.waveshare.com/w/upload/d/d3/2.13inch-e-paper-b-Specification.pdf) | SPI |
| [WS2812 RGB LED](https://cdn-shop.adafruit.com/datasheets/WS2812.pdf) | GPIO |
## Contributing
Your contributions are welcome!
Please take a look at our [CONTRIBUTING.md](./CONTRIBUTING.md) document for details.
## License
This project is licensed under the BSD 3-clause license, just like the [Go project](https://golang.org/LICENSE) itself.
+195
View File
@@ -0,0 +1,195 @@
// Package adxl345 provides a driver for the ADXL345 digital accelerometer.
//
// Datasheet EN: http://www.analog.com/media/en/technical-documentation/data-sheets/ADXL345.pdf
//
// Datasheet JP: http://www.analog.com/media/jp/technical-documentation/data-sheets/ADXL345_jp.pdf
//
package adxl345 // import "tinygo.org/x/drivers/adxl345"
import (
"machine"
)
type Range uint8
type Rate uint8
// Internal structure for the power configuration
type powerCtl struct {
link uint8
autoSleep uint8
measure uint8
sleep uint8
wakeUp uint8
}
// Internal structure for the sensor's data format configuration
type dataFormat struct {
selfTest uint8
spi uint8
intInvert uint8
fullRes uint8
justify uint8
sensorRange Range
}
// Internal structure for the sampling rate configuration
type bwRate struct {
lowPower uint8
rate Rate
}
// Device wraps an I2C connection to a ADXL345 device.
type Device struct {
bus machine.I2C
Address uint16
powerCtl powerCtl
dataFormat dataFormat
bwRate bwRate
}
// New creates a new ADXL345 connection. The I2C bus must already be
// configured.
//
// This function only creates the Device object, it does not init the device.
// To do that you must call the Configure() method on the Device before using it.
func New(bus machine.I2C) Device {
return Device{
bus: bus,
powerCtl: powerCtl{
measure: 1,
},
dataFormat: dataFormat{
sensorRange: RANGE_2G,
},
bwRate: bwRate{
lowPower: 1,
rate: RATE_100HZ,
},
Address: AddressLow,
}
}
// Configure sets up the device for communication
func (d *Device) Configure() {
d.bus.WriteRegister(uint8(d.Address), REG_BW_RATE, []byte{d.bwRate.toByte()})
d.bus.WriteRegister(uint8(d.Address), REG_POWER_CTL, []byte{d.powerCtl.toByte()})
d.bus.WriteRegister(uint8(d.Address), REG_DATA_FORMAT, []byte{d.dataFormat.toByte()})
}
// Halt stops the sensor, values will not updated
func (d *Device) Halt() {
d.powerCtl.measure = 0
d.bus.WriteRegister(uint8(d.Address), REG_POWER_CTL, []byte{d.powerCtl.toByte()})
}
// Restart makes reading the sensor working again after a halt
func (d *Device) Restart() {
d.powerCtl.measure = 1
d.bus.WriteRegister(uint8(d.Address), REG_POWER_CTL, []byte{d.powerCtl.toByte()})
}
// 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, err error) {
rx, ry, rz := d.ReadRawAcceleration()
x = d.dataFormat.convertToIS(rx)
y = d.dataFormat.convertToIS(ry)
z = d.dataFormat.convertToIS(rz)
return
}
// ReadRawAcceleration reads the sensor values and returns the raw x, y and z axis
// from the adxl345.
func (d *Device) ReadRawAcceleration() (x int32, y int32, z int32) {
data := []byte{0, 0, 0, 0, 0, 0}
d.bus.ReadRegister(uint8(d.Address), REG_DATAX0, data)
x = readIntLE(data[0], data[1])
y = readIntLE(data[2], data[3])
z = readIntLE(data[4], data[5])
return
}
// UseLowPower sets the ADXL345 to use the low power mode.
func (d *Device) UseLowPower(power bool) {
if power {
d.bwRate.lowPower = 1
} else {
d.bwRate.lowPower = 0
}
d.bus.WriteRegister(uint8(d.Address), REG_BW_RATE, []byte{d.bwRate.toByte()})
}
// SetRate change the current rate of the sensor
func (d *Device) SetRate(rate Rate) bool {
d.bwRate.rate = rate & 0x0F
d.bus.WriteRegister(uint8(d.Address), REG_BW_RATE, []byte{d.bwRate.toByte()})
return true
}
// SetRange change the current range of the sensor
func (d *Device) SetRange(sensorRange Range) bool {
d.dataFormat.sensorRange = sensorRange & 0x03
d.bus.WriteRegister(uint8(d.Address), REG_DATA_FORMAT, []byte{d.dataFormat.toByte()})
return true
}
// convertToIS adjusts the raw values from the adxl345 with the range configuration
func (d *dataFormat) convertToIS(rawValue int32) int32 {
switch d.sensorRange {
case RANGE_2G:
return rawValue * 4 // rawValue * 2 * 1000 / 512
case RANGE_4G:
return rawValue * 8 // rawValue * 4 * 1000 / 512
case RANGE_8G:
return rawValue * 16 // rawValue * 8 * 1000 / 512
case RANGE_16G:
return rawValue * 32 // rawValue * 16 * 1000 / 512
default:
return 0
}
}
// toByte returns a byte from the powerCtl configuration
func (p *powerCtl) toByte() (bits uint8) {
bits = 0x00
bits = bits | (p.link << 5)
bits = bits | (p.autoSleep << 4)
bits = bits | (p.measure << 3)
bits = bits | (p.sleep << 2)
bits = bits | p.wakeUp
return bits
}
// toByte returns a byte from the dataFormat configuration
func (d *dataFormat) toByte() (bits uint8) {
bits = 0x00
bits = bits | (d.selfTest << 7)
bits = bits | (d.spi << 6)
bits = bits | (d.intInvert << 5)
bits = bits | (d.fullRes << 3)
bits = bits | (d.justify << 2)
bits = bits | uint8(d.sensorRange)
return bits
}
// toByte returns a byte from the bwRate configuration
func (b *bwRate) toByte() (bits uint8) {
bits = 0x00
bits = bits | (b.lowPower << 4)
bits = bits | uint8(b.rate)
return bits
}
// readInt converts two bytes to int16
func readIntLE(msb byte, lsb byte) int32 {
return int32(uint16(msb) | uint16(lsb)<<8)
}
+61
View File
@@ -0,0 +1,61 @@
package adxl345
const AddressLow = 0x53
const AddressHigh = 0x1D
const (
// Data rate
RATE_3200HZ Rate = 0x0F // 3200 Hz
RATE_1600HZ Rate = 0x0E // 1600 Hz
RATE_800HZ Rate = 0x0D // 800 Hz
RATE_400HZ Rate = 0x0C // 400 Hz
RATE_200HZ Rate = 0x0B // 200 Hz
RATE_100HZ Rate = 0x0A // 100 Hz
RATE_50HZ Rate = 0x09 // 50 Hz
RATE_25HZ Rate = 0x08 // 25 Hz
RATE_12_5HZ Rate = 0x07 // 12.5 Hz
RATE_6_25HZ Rate = 0x06 // 6.25 Hz
RATE_3_13HZ Rate = 0x05 // 3.13 Hz
RATE_1_56HZ Rate = 0x04 // 1.56 Hz
RATE_0_78HZ Rate = 0x03 // 0.78 Hz
RATE_0_39HZ Rate = 0x02 // 0.39 Hz
RATE_0_20HZ Rate = 0x01 // 0.20 Hz
RATE_0_10HZ Rate = 0x00 // 0.10 Hz
// Data range
RANGE_2G Range = 0x00 // +-2 g
RANGE_4G Range = 0x01 // +-4 g
RANGE_8G Range = 0x02 // +-8 g
RANGE_16G Range = 0x03 // +-16 g)
REG_DEVID = 0x00 // R, 11100101, Device ID
REG_THRESH_TAP = 0x1D // R/W, 00000000, Tap threshold
REG_OFSX = 0x1E // R/W, 00000000, X-axis offset
REG_OFSY = 0x1F // R/W, 00000000, Y-axis offset
REG_OFSZ = 0x20 // R/W, 00000000, Z-axis offset
REG_DUR = 0x21 // R/W, 00000000, Tap duration
REG_LATENT = 0x22 // R/W, 00000000, Tap latency
REG_WINDOW = 0x23 // R/W, 00000000, Tap window
REG_THRESH_ACT = 0x24 // R/W, 00000000, Activity threshold
REG_THRESH_INACT = 0x25 // R/W, 00000000, Inactivity threshold
REG_TIME_INACT = 0x26 // R/W, 00000000, Inactivity time
REG_ACT_INACT_CTL = 0x27 // R/W, 00000000, Axis enable control for activity and inactiv ity detection
REG_THRESH_FF = 0x28 // R/W, 00000000, Free-fall threshold
REG_TIME_FF = 0x29 // R/W, 00000000, Free-fall time
REG_TAP_AXES = 0x2A // R/W, 00000000, Axis control for single tap/double tap
REG_ACT_TAP_STATUS = 0x2B // R, 00000000, Source of single tap/double tap
REG_BW_RATE = 0x2C // R/W, 00001010, Data rate and power mode control
REG_POWER_CTL = 0x2D // R/W, 00000000, Power-saving features control
REG_INT_ENABLE = 0x2E // R/W, 00000000, Interrupt enable control
REG_INT_MAP = 0x2F // R/W, 00000000, Interrupt mapping control
REG_INT_SOUCE = 0x30 // R, 00000010, Source of interrupts
REG_DATA_FORMAT = 0x31 // R/W, 00000000, Data format control
REG_DATAX0 = 0x32 // R, 00000000, X-Axis Data 0
REG_DATAX1 = 0x33 // R, 00000000, X-Axis Data 1
REG_DATAY0 = 0x34 // R, 00000000, Y-Axis Data 0
REG_DATAY1 = 0x35 // R, 00000000, Y-Axis Data 1
REG_DATAZ0 = 0x36 // R, 00000000, Z-Axis Data 0
REG_DATAZ1 = 0x37 // R, 00000000, Z-Axis Data 1
REG_FIFO_CTL = 0x38 // R/W, 00000000, FIFO control
REG_FIFO_STATUS = 0x39 // R, 00000000, FIFO status
)
+86
View File
@@ -0,0 +1,86 @@
// Package apa102 implements a driver for the APA102 SPI LED.
//
// Datasheet: https://cdn-shop.adafruit.com/product-files/2343/APA102C.pdf
package apa102 // import "tinygo.org/x/drivers/apa102"
import (
"image/color"
"machine"
)
const (
// BGR aka "Blue Green Red" is the current APA102 LED color order.
BGR = iota
// BRG aka "Blue Red Green" is the typical APA102 color order from 2015-2017.
BRG
// GRB aka "Green Red Blue" is the typical APA102 color order from pre-2015.
GRB
)
// Device wraps APA102 SPI LEDs.
type Device struct {
bus machine.SPI
Order int
}
// New returns a new APA102 driver. Pass in a fully configured SPI bus.
func New(b machine.SPI) Device {
return Device{bus: b, Order: BGR}
}
// WriteColors writes the given RGBA color slice out using the APA102 protocol.
// The A value (Alpha channel) is used for brightness, set to 0xff (255) for maximum.
func (d Device) WriteColors(cs []color.RGBA) (n int, err error) {
d.startFrame()
// write data
for _, c := range cs {
// brightness is scaled to 5 bit value
d.bus.Tx([]byte{0xe0 | (c.A >> 3)}, nil)
// set the colors
switch d.Order {
case BRG:
d.bus.Tx([]byte{c.B}, nil)
d.bus.Tx([]byte{c.R}, nil)
d.bus.Tx([]byte{c.G}, nil)
case GRB:
d.bus.Tx([]byte{c.G}, nil)
d.bus.Tx([]byte{c.R}, nil)
d.bus.Tx([]byte{c.B}, nil)
case BGR:
d.bus.Tx([]byte{c.B}, nil)
d.bus.Tx([]byte{c.G}, nil)
d.bus.Tx([]byte{c.R}, nil)
}
}
d.endFrame(len(cs))
return len(cs), nil
}
// Write the raw bytes using the APA102 protocol.
func (d Device) Write(buf []byte) (n int, err error) {
d.startFrame()
d.bus.Tx(buf, nil)
d.endFrame(len(buf) / 4)
return len(buf), nil
}
// startFrame sends the start bytes for a strand of LEDs.
func (d Device) startFrame() {
d.bus.Tx([]byte{0x00, 0x00, 0x00, 0x00}, nil)
}
// endFrame sends the end frame marker with one extra bit per LED so
// long strands of LEDs receive the necessary termination for updates.
// See https://cpldcpu.wordpress.com/2014/11/30/understanding-the-apa102-superled/
func (d Device) endFrame(count int) {
for i := 0; i < count/16; i++ {
d.bus.Tx([]byte{0xff}, nil)
}
}
+171
View File
@@ -0,0 +1,171 @@
// Package at24cx provides a driver for the AT24C32/64/128/256/512 2-wire serial EEPROM
//
// Datasheet:
// https://www.openimpulse.com/blog/wp-content/uploads/wpsc/downloadables/24C32-Datasheet.pdf
package at24cx // import "tinygo.org/x/drivers/at24cx"
import (
"errors"
"machine"
"time"
)
// Device wraps an I2C connection to a DS3231 device.
type Device struct {
bus machine.I2C
Address uint16
pageSize uint16
currentRAMAddress uint16
startRAMAddress uint16
endRAMAddress uint16
}
type Config struct {
PageSize uint16
StartRAMAddress uint16
EndRAMAddress uint16
}
// New creates a new AT24C32/64 connection. The I2C bus must already be
// configured.
//
// This function only creates the Device object, it does not touch the device.
func New(bus machine.I2C) Device {
return Device{
bus: bus,
Address: Address,
}
}
// Configure sets up the device for communication
func (d *Device) Configure(cfg Config) {
if cfg.PageSize == 0 {
d.pageSize = 32
} else {
d.pageSize = cfg.PageSize
}
if cfg.EndRAMAddress == 0 {
d.endRAMAddress = 4096
} else {
d.endRAMAddress = cfg.EndRAMAddress
}
d.startRAMAddress = cfg.StartRAMAddress
}
// WriteByte writes a byte at the specified address
func (d *Device) WriteByte(eepromAddress uint16, value uint8) error {
address := []uint8{
uint8((eepromAddress >> 8) & 0xFF),
uint8(eepromAddress & 0xFF),
value,
}
return d.bus.Tx(d.Address, address, nil)
}
// ReadByte reads the byte at the specified address
func (d *Device) ReadByte(eepromAddress uint16) (uint8, error) {
address := []uint8{
uint8(eepromAddress >> 8),
uint8(eepromAddress & 0xFF),
}
data := make([]uint8, 1)
err := d.bus.Tx(d.Address, address, data)
return data[0], err
}
// WriteAt writes a byte array at the specified address
func (d *Device) WriteAt(data []byte, offset int64) (n int, err error) {
return d.writeAt(data, uint16(offset))
}
// writeAt writes a byte array at the specified address
func (d *Device) writeAt(data []byte, offset uint16) (n int, err error) {
values := make([]uint8, 32)
dataLeft := uint16(len(data))
d.currentRAMAddress = offset
offset = 0
var offsetPage uint16
var chunkLength uint16
for dataLeft > 0 {
offsetPage = d.currentRAMAddress % d.pageSize
if dataLeft < 30 { // The 32K/64K EEPROM is capable of 32-byte page writes and we're using 2 for the address
chunkLength = dataLeft
} else {
chunkLength = 30
}
if (d.pageSize - offsetPage) < chunkLength {
chunkLength = d.pageSize - offsetPage
}
for i := uint16(0); i < chunkLength; i++ {
values[2+i] = data[offset+i]
}
values[0] = uint8(d.currentRAMAddress >> 8)
values[1] = uint8(d.currentRAMAddress & 0xFF)
err := d.bus.Tx(d.Address, values[:chunkLength+2], nil)
if err != nil {
return 0, err
}
dataLeft -= chunkLength
offset += chunkLength
if d.endRAMAddress-chunkLength < d.currentRAMAddress {
d.currentRAMAddress = d.startRAMAddress + (d.currentRAMAddress+uint16(len(data)))%d.endRAMAddress
} else {
d.currentRAMAddress += chunkLength
}
time.Sleep(2 * time.Millisecond) // writing again too soon will block the device
}
return len(data), nil
}
// ReadAt reads the bytes at the specified address
func (d *Device) ReadAt(data []byte, offset int64) (n int, err error) {
return d.readAt(data, uint16(offset))
}
// readAt reads the bytes at the specified address
func (d *Device) readAt(data []byte, offset uint16) (n int, err error) {
address := []uint8{
uint8((offset >> 8) & 0xFF),
uint8(offset & 0xFF),
}
err = d.bus.Tx(d.Address, address, data)
if d.endRAMAddress-uint16(len(data)) < offset {
d.currentRAMAddress = d.startRAMAddress + (offset+uint16(len(data)))%d.endRAMAddress
} else {
d.currentRAMAddress = offset + uint16(len(data))
}
return len(data), err
}
// Seek sets the offset for the next Read or Write on SRAM to offset, interpreted
// according to whence: 0 means relative to the origin of the SRAM, 1 means
// relative to the current offset, and 2 means relative to the end.
// returns new offset and error, if any
func (d *Device) Seek(offset int64, whence int) (int64, error) {
w := uint16(0)
switch whence {
case 0:
w = d.startRAMAddress
case 1:
w = d.currentRAMAddress
case 2:
w = d.endRAMAddress
default:
return 0, errors.New("invalid whence")
}
d.currentRAMAddress = w + uint16(offset)
return int64(d.currentRAMAddress), nil
}
// Write writes len(data) bytes to SRAM
// returns number of bytes written and error, if any
func (d *Device) Write(data []byte) (n int, err error) {
return d.writeAt(data, d.currentRAMAddress)
}
// Read reads len(data) from SRAM
// returns number of bytes written and error, if any
func (d *Device) Read(data []uint8) (n int, err error) {
return d.readAt(data, d.currentRAMAddress)
}
+4
View File
@@ -0,0 +1,4 @@
package at24cx
// The I2C address which this device listens to.
const Address = 0x57
+72
View File
@@ -0,0 +1,72 @@
// Package bh1750 provides a driver for the BH1750 digital Ambient Light
//
// Datasheet:
// https://www.mouser.com/ds/2/348/bh1750fvi-e-186247.pdf
//
package bh1750 // import "tinygo.org/x/drivers/bh1750"
import (
"time"
"machine"
)
// SamplingMode is the sampling's resolution of the measurement
type SamplingMode byte
// Device wraps an I2C connection to a bh1750 device.
type Device struct {
bus machine.I2C
Address uint16
mode SamplingMode
}
// New creates a new bh1750 connection. The I2C bus must already be
// configured.
//
// This function only creates the Device object, it does not touch the device.
func New(bus machine.I2C) Device {
return Device{
bus: bus,
Address: Address,
mode: CONTINUOUS_HIGH_RES_MODE,
}
}
// Configure sets up the device for communication
func (d *Device) Configure() {
d.bus.Tx(d.Address, []byte{POWER_ON}, nil)
d.SetMode(d.mode)
}
// RawSensorData returns the raw value from the bh1750
func (d *Device) RawSensorData() uint16 {
buf := []byte{1, 0}
d.bus.Tx(d.Address, nil, buf)
return (uint16(buf[0]) << 8) | uint16(buf[1])
}
// Illuminance returns the adjusted value in mlx (milliLux)
func (d *Device) Illuminance() int32 {
lux := uint32(d.RawSensorData())
var coef uint32
if d.mode == CONTINUOUS_HIGH_RES_MODE || d.mode == ONE_TIME_HIGH_RES_MODE {
coef = HIGH_RES
} else if d.mode == CONTINUOUS_HIGH_RES_MODE_2 || d.mode == ONE_TIME_HIGH_RES_MODE_2 {
coef = HIGH_RES2
} else {
coef = LOW_RES
}
// 100 * coef * lux * (5/6)
// 5/6 = measurement accuracy as per the datasheet
return int32(250 * coef * lux / 3)
}
// SetMode changes the reading mode for the sensor
func (d *Device) SetMode(mode SamplingMode) {
d.mode = mode
d.bus.Tx(d.Address, []byte{byte(d.mode)}, nil)
time.Sleep(10 * time.Millisecond)
}
+24
View File
@@ -0,0 +1,24 @@
package bh1750
// Constants/addresses used for I2C.
// The I2C address which this device listens to.
const Address = 0x23
// Registers. Names, addresses and comments copied from the datasheet.
const (
POWER_DOWN = 0x00
POWER_ON = 0x01
RESET = 0x07
CONTINUOUS_HIGH_RES_MODE SamplingMode = 0x10
CONTINUOUS_HIGH_RES_MODE_2 SamplingMode = 0x11
CONTINUOUS_LOW_RES_MODE SamplingMode = 0x13
ONE_TIME_HIGH_RES_MODE SamplingMode = 0x20
ONE_TIME_HIGH_RES_MODE_2 SamplingMode = 0x21
ONE_TIME_LOW_RES_MODE SamplingMode = 0x23
// resolution in 10*lx
HIGH_RES = 10
HIGH_RES2 = 5
LOW_RES = 40
)
+14 -8
View File
@@ -1,7 +1,7 @@
// Package blinkm implements a driver for the BlinkM I2C RGB LED.
//
// Datasheet: http://thingm.com/fileadmin/thingm/downloads/BlinkM_datasheet.pdf
package blinkm
package blinkm // import "tinygo.org/x/drivers/blinkm"
import (
"machine"
@@ -9,7 +9,8 @@ import (
// Device wraps an I2C connection to a BlinkM device.
type Device struct {
bus machine.I2C
bus machine.I2C
Address uint16
}
// New creates a new BlinkM connection. The I2C bus must already be
@@ -17,38 +18,43 @@ type Device struct {
//
// This function only creates the Device object, it does not touch the device.
func New(bus machine.I2C) Device {
return Device{bus}
return Device{bus, Address}
}
// Configure sets up the device for communication
func (d *Device) Configure() {
d.bus.Tx(d.Address, []byte{'o'}, nil)
}
// Version returns the version of firmware on the BlinkM.
func (d Device) Version() (major, minor byte, err error) {
version := []byte{0, 0}
d.bus.ReadRegister(Address, GET_FIRMWARE, version)
d.bus.Tx(d.Address, []byte{GET_FIRMWARE}, version)
return version[0], version[1], nil
}
// SetRGB sets the RGB color on the BlinkM.
func (d Device) SetRGB(r, g, b byte) error {
d.bus.WriteRegister(Address, TO_RGB, []byte{r, g, b})
d.bus.Tx(d.Address, []byte{TO_RGB, r, g, b}, nil)
return nil
}
// GetRGB gets the current RGB color on the BlinkM.
func (d Device) GetRGB() (r, g, b byte, err error) {
color := []byte{0, 0, 0}
d.bus.ReadRegister(Address, GET_RGB, color)
d.bus.Tx(d.Address, []byte{GET_RGB}, color)
return color[0], color[1], color[2], nil
}
// FadeToRGB sets the RGB color on the BlinkM by fading from the current color
// to the new color.
func (d Device) FadeToRGB(r, g, b byte) error {
d.bus.WriteRegister(Address, FADE_TO_RGB, []byte{r, g, b})
d.bus.Tx(d.Address, []byte{FADE_TO_RGB, r, g, b}, nil)
return nil
}
// StopScript stops whatever script is currently running on the BlinkM.
func (d Device) StopScript() error {
d.bus.WriteRegister(Address, STOP_SCRIPT, nil)
d.bus.Tx(d.Address, []byte{STOP_SCRIPT}, nil)
return nil
}
+257
View File
@@ -0,0 +1,257 @@
// Package bme280 provides a driver for the BME280 digital combined
// humidity and pressure sensor by Bosch.
//
// Datasheet:
// https://cdn-shop.adafruit.com/datasheets/BST-BME280_DS001-10.pdf
//
package bme280
import (
"machine"
"math"
)
// calibrationCoefficients reads at startup and stores the calibration coefficients
type calibrationCoefficients struct {
t1 uint16
t2 int16
t3 int16
p1 uint16
p2 int16
p3 int16
p4 int16
p5 int16
p6 int16
p7 int16
p8 int16
p9 int16
h1 uint8
h2 int16
h3 uint8
h4 int16
h5 int16
h6 int8
}
// Device wraps an I2C connection to a BME280 device.
type Device struct {
bus machine.I2C
Address uint16
calibrationCoefficients calibrationCoefficients
}
// New creates a new BME280 connection. The I2C bus must already be
// configured.
//
// This function only creates the Device object, it does not touch the device.
func New(bus machine.I2C) Device {
return Device{
bus: bus,
Address: Address,
}
}
// Configure sets up the device for communication and
// read the calibration coefficientes.
func (d *Device) Configure() {
var data [24]byte
err := d.bus.ReadRegister(uint8(d.Address), REG_CALIBRATION, data[:])
if err != nil {
return
}
var h1 [1]byte
err = d.bus.ReadRegister(uint8(d.Address), REG_CALIBRATION_H1, h1[:])
if err != nil {
return
}
var h2lsb [7]byte
err = d.bus.ReadRegister(uint8(d.Address), REG_CALIBRATION_H2LSB, h2lsb[:])
if err != nil {
return
}
d.calibrationCoefficients.t1 = readUintLE(data[0], data[1])
d.calibrationCoefficients.t2 = readIntLE(data[2], data[3])
d.calibrationCoefficients.t3 = readIntLE(data[4], data[5])
d.calibrationCoefficients.p1 = readUintLE(data[6], data[7])
d.calibrationCoefficients.p2 = readIntLE(data[8], data[9])
d.calibrationCoefficients.p3 = readIntLE(data[10], data[11])
d.calibrationCoefficients.p4 = readIntLE(data[12], data[13])
d.calibrationCoefficients.p5 = readIntLE(data[14], data[15])
d.calibrationCoefficients.p6 = readIntLE(data[16], data[17])
d.calibrationCoefficients.p7 = readIntLE(data[18], data[19])
d.calibrationCoefficients.p8 = readIntLE(data[20], data[21])
d.calibrationCoefficients.p9 = readIntLE(data[22], data[23])
d.calibrationCoefficients.h1 = h1[0]
d.calibrationCoefficients.h2 = readIntLE(h2lsb[0], h2lsb[1])
d.calibrationCoefficients.h3 = h2lsb[2]
d.calibrationCoefficients.h6 = int8(h2lsb[6])
d.calibrationCoefficients.h4 = 0 + (int16(h2lsb[3]) << 4) | (int16(h2lsb[4] & 0x0F))
d.calibrationCoefficients.h5 = 0 + (int16(h2lsb[5]) << 4) | (int16(h2lsb[4]) >> 4)
d.bus.WriteRegister(uint8(d.Address), CTRL_HUMIDITY_ADDR, []byte{0x3f})
d.bus.WriteRegister(uint8(d.Address), CTRL_MEAS_ADDR, []byte{0xB7})
d.bus.WriteRegister(uint8(d.Address), CTRL_CONFIG, []byte{0x00})
}
// Connected returns whether a BME280 has been found.
// It does a "who am I" request and checks the response.
func (d *Device) Connected() bool {
data := []byte{0}
d.bus.ReadRegister(uint8(d.Address), WHO_AM_I, data)
return data[0] == CHIP_ID
}
// Reset the device
func (d *Device) Reset() {
d.bus.WriteRegister(uint8(d.Address), CMD_RESET, []byte{0xB6})
}
// ReadTemperature returns the temperature in celsius milli degrees (ºC/1000)
func (d *Device) ReadTemperature() (int32, error) {
data, err := d.readData()
if err != nil {
return 0, err
}
temp, _ := d.calculateTemp(data)
return temp, nil
}
// ReadPressure returns the pressure in milli pascals mPa
func (d *Device) ReadPressure() (int32, error) {
data, err := d.readData()
if err != nil {
return 0, err
}
_, tFine := d.calculateTemp(data)
pressure := d.calculatePressure(data, tFine)
return pressure, nil
}
// ReadHumidity returns the relative humidity in hundredths of a percent
func (d *Device) ReadHumidity() (int32, error) {
data, err := d.readData()
if err != nil {
return 0, err
}
_, tFine := d.calculateTemp(data)
humidity := d.calculateHumidity(data, tFine)
return humidity, nil
}
// ReadAltitude returns the current altitude in meters based on the
// current barometric pressure and estimated pressure at sea level.
// Calculation is based on code from Adafruit BME280 library
// https://github.com/adafruit/Adafruit_BME280_Library
func (d *Device) ReadAltitude() (alt int32, err error) {
mPa, _ := d.ReadPressure()
atmP := float32(mPa) / 100000
alt = int32(44330.0 * (1.0 - math.Pow(float64(atmP/SEALEVEL_PRESSURE), 0.1903)))
return
}
// convert2Bytes converts two bytes to int32
func convert2Bytes(msb byte, lsb byte) int32 {
return int32(readUint(msb, lsb))
}
// convert3Bytes converts three bytes to int32
func convert3Bytes(msb byte, b1 byte, lsb byte) int32 {
return int32(((((uint32(msb) << 8) | uint32(b1)) << 8) | uint32(lsb)) >> 4)
}
// readUint converts two bytes to uint16
func readUint(msb byte, lsb byte) uint16 {
return (uint16(msb) << 8) | uint16(lsb)
}
// readUintLE converts two little endian bytes to uint16
func readUintLE(msb byte, lsb byte) uint16 {
temp := readUint(msb, lsb)
return (temp >> 8) | (temp << 8)
}
// readIntLE converts two little endian bytes to int16
func readIntLE(msb byte, lsb byte) int16 {
return int16(readUintLE(msb, lsb))
}
// readData does a burst read from 0xF7 to 0xF0 according to the datasheet
// resulting in an slice with 8 bytes 0-2 = pressure / 3-5 = temperature / 6-7 = humidity
func (d *Device) readData() (data [8]byte, err error) {
err = d.bus.ReadRegister(uint8(d.Address), REG_PRESSURE, data[:])
if err != nil {
println(err)
return
}
return
}
// calculateTemp uses the data slice and applies calibrations values on it to convert the value to milli degrees
// it also calculates the variable tFine which is used by the pressure and humidity calculation
func (d *Device) calculateTemp(data [8]byte) (int32, int32) {
rawTemp := convert3Bytes(data[3], data[4], data[5])
var1 := (((rawTemp >> 3) - (int32(d.calibrationCoefficients.t1) << 1)) * int32(d.calibrationCoefficients.t2)) >> 11
var2 := (((((rawTemp >> 4) - int32(d.calibrationCoefficients.t1)) * ((rawTemp >> 4) - int32(d.calibrationCoefficients.t1))) >> 12) * int32(d.calibrationCoefficients.t3)) >> 14
tFine := var1 + var2
T := (tFine*5 + 128) >> 8
return (10 * T), tFine
}
// calculatePressure uses the data slice and applies calibrations values on it to convert the value to milli pascals mPa
func (d *Device) calculatePressure(data [8]byte, tFine int32) int32 {
rawPressure := convert3Bytes(data[0], data[1], data[2])
var1 := int64(tFine) - 128000
var2 := var1 * var1 * int64(d.calibrationCoefficients.p6)
var2 = var2 + ((var1 * int64(d.calibrationCoefficients.p5)) << 17)
var2 = var2 + (int64(d.calibrationCoefficients.p4) << 35)
var1 = ((var1 * var1 * int64(d.calibrationCoefficients.p3)) >> 8) + ((var1 * int64(d.calibrationCoefficients.p2)) << 12)
var1 = ((int64(1) << 47) + var1) * int64(d.calibrationCoefficients.p1) >> 33
if var1 == 0 {
return 0 // avoid exception caused by division by zero
}
p := int64(1048576 - rawPressure)
p = (((p << 31) - var2) * 3125) / var1
var1 = (int64(d.calibrationCoefficients.p9) * (p >> 13) * (p >> 13)) >> 25
var2 = (int64(d.calibrationCoefficients.p8) * p) >> 19
p = ((p + var1 + var2) >> 8) + (int64(d.calibrationCoefficients.p7) << 4)
p = (p / 256)
return int32(1000 * p)
}
// calculateHumidity uses the data slice and applies calibrations values on it to convert the value to relative humidity in hundredths of a percent
func (d *Device) calculateHumidity(data [8]byte, tFine int32) int32 {
rawHumidity := convert2Bytes(data[6], data[7])
h := float32(tFine) - 76800
if h == 0 {
println("invalid value")
}
var1 := float32(rawHumidity) - (float32(d.calibrationCoefficients.h4)*64.0 +
(float32(d.calibrationCoefficients.h5) / 16384.0 * h))
var2 := float32(d.calibrationCoefficients.h2) / 65536.0 *
(1.0 + float32(d.calibrationCoefficients.h6)/67108864.0*h*
(1.0+float32(d.calibrationCoefficients.h3)/67108864.0*h))
h = var1 * var2
h = h * (1 - float32(d.calibrationCoefficients.h1)*h/524288)
return int32(100 * h)
}
+25
View File
@@ -0,0 +1,25 @@
package bme280
// Constants/addresses used for I2C.
// The I2C address which this device listens to.
const Address = 0x76
// Registers. Names, addresses and comments copied from the datasheet.
const (
CTRL_MEAS_ADDR = 0xF4
CTRL_HUMIDITY_ADDR = 0xF2
CTRL_CONFIG = 0xF5
REG_PRESSURE = 0xF7
REG_CALIBRATION = 0x88
REG_CALIBRATION_H1 = 0xA1
REG_CALIBRATION_H2LSB = 0xE1
CMD_RESET = 0xE0
WHO_AM_I = 0xD0
CHIP_ID = 0x60
)
const (
SEALEVEL_PRESSURE float32 = 1013.25 // in hPa
)
+183
View File
@@ -0,0 +1,183 @@
// Package bmp180 provides a driver for the BMP180 digital pressure sensor
// by Bosch.
//
// Datasheet:
// https://cdn-shop.adafruit.com/datasheets/BST-BMP180-DS000-09.pdf
//
package bmp180 // import "tinygo.org/x/drivers/bmp180"
import (
"time"
"machine"
)
// OversamplingMode is the oversampling ratio of the pressure measurement.
type OversamplingMode uint
// calibrationCoefficients reads at startup and stores the calibration coefficients
type calibrationCoefficients struct {
ac1 int16
ac2 int16
ac3 int16
ac4 uint16
ac5 uint16
ac6 uint16
b1 int16
b2 int16
mb int16
mc int16
md int16
}
// Device wraps an I2C connection to a BMP180 device.
type Device struct {
bus machine.I2C
Address uint16
mode OversamplingMode
calibrationCoefficients calibrationCoefficients
}
// New creates a new BMP180 connection. The I2C bus must already be
// configured.
//
// This function only creates the Device object, it does not initialize the device.
// You must call Configure() first in order to use the device itself.
func New(bus machine.I2C) Device {
return Device{
bus: bus,
Address: Address,
mode: ULTRAHIGHRESOLUTION,
}
}
// Connected returns whether a BMP180 has been found.
// It does a "who am I" request and checks the response.
func (d *Device) Connected() bool {
data := []byte{0}
d.bus.ReadRegister(uint8(d.Address), WHO_AM_I, data)
return data[0] == CHIP_ID
}
// Configure sets up the device for communication and
// read the calibration coefficients.
func (d *Device) Configure() {
data := make([]byte, 22)
err := d.bus.ReadRegister(uint8(d.Address), AC1_MSB, data)
if err != nil {
return
}
d.calibrationCoefficients.ac1 = readInt(data[0], data[1])
d.calibrationCoefficients.ac2 = readInt(data[2], data[3])
d.calibrationCoefficients.ac3 = readInt(data[4], data[5])
d.calibrationCoefficients.ac4 = readUint(data[6], data[7])
d.calibrationCoefficients.ac5 = readUint(data[8], data[9])
d.calibrationCoefficients.ac6 = readUint(data[10], data[11])
d.calibrationCoefficients.b1 = readInt(data[12], data[13])
d.calibrationCoefficients.b2 = readInt(data[14], data[15])
d.calibrationCoefficients.mb = readInt(data[16], data[17])
d.calibrationCoefficients.mc = readInt(data[18], data[19])
d.calibrationCoefficients.md = readInt(data[20], data[21])
}
// ReadTemperature returns the temperature in celsius milli degrees (ºC/1000).
func (d *Device) ReadTemperature() (temperature int32, err error) {
rawTemp, err := d.rawTemp()
if err != nil {
return
}
b5 := d.calculateB5(rawTemp)
t := (b5 + 8) >> 4
return 100 * t, nil
}
// ReadPressure returns the pressure in milli pascals (mPa).
func (d *Device) ReadPressure() (pressure int32, err error) {
rawTemp, err := d.rawTemp()
if err != nil {
return
}
rawPressure, err := d.rawPressure(d.mode)
if err != nil {
return
}
b5 := d.calculateB5(rawTemp)
b6 := b5 - 4000
x1 := (int32(d.calibrationCoefficients.b2) * (b6 * b6 >> 12)) >> 11
x2 := (int32(d.calibrationCoefficients.ac2) * b6) >> 11
x3 := x1 + x2
b3 := (((int32(d.calibrationCoefficients.ac1)*4 + x3) << uint(d.mode)) + 2) >> 2
x1 = (int32(d.calibrationCoefficients.ac3) * b6) >> 13
x2 = (int32(d.calibrationCoefficients.b1) * ((b6 * b6) >> 12)) >> 16
x3 = ((x1 + x2) + 2) >> 2
b4 := (uint32(d.calibrationCoefficients.ac4) * uint32(x3+32768)) >> 15
b7 := uint32(rawPressure-b3) * (50000 >> uint(d.mode))
var p int32
if b7 < 0x80000000 {
p = int32((b7 << 1) / b4)
} else {
p = int32((b7 / b4) << 1)
}
x1 = (p >> 8) * (p >> 8)
x1 = (x1 * 3038) >> 16
x2 = (-7357 * p) >> 16
return 1000 * (p + ((x1 + x2 + 3791) >> 4)), nil
}
// rawTemp returns the sensor's raw values of the temperature
func (d *Device) rawTemp() (int16, error) {
d.bus.WriteRegister(uint8(d.Address), REG_CTRL, []byte{CMD_TEMP})
time.Sleep(5 * time.Millisecond)
data := make([]byte, 2)
err := d.bus.ReadRegister(uint8(d.Address), REG_TEMP_MSB, data)
if err != nil {
return 0, err
}
return readInt(data[0], data[1]), nil
}
// calculateB5 calculates intermediate value B5 as per page 15 of datasheet
func (d *Device) calculateB5(rawTemp int16) int32 {
x1 := (int32(rawTemp) - int32(d.calibrationCoefficients.ac6)) * int32(d.calibrationCoefficients.ac5) >> 15
x2 := int32(d.calibrationCoefficients.mc) << 11 / (x1 + int32(d.calibrationCoefficients.md))
return x1 + x2
}
// rawPressure returns the sensor's raw values of the pressure
func (d *Device) rawPressure(mode OversamplingMode) (int32, error) {
d.bus.WriteRegister(uint8(d.Address), REG_CTRL, []byte{CMD_PRESSURE + byte(mode<<6)})
time.Sleep(pauseForReading(mode))
data := make([]byte, 3)
err := d.bus.ReadRegister(uint8(d.Address), REG_PRESSURE_MSB, data)
if err != nil {
return 0, err
}
rawPressure := int32((uint32(data[0])<<16 + uint32(data[1])<<8 + uint32(data[2])) >> (8 - uint(mode)))
return rawPressure, nil
}
// pauseForReading returns the pause duration depending on the sampling mode
func pauseForReading(mode OversamplingMode) time.Duration {
var d time.Duration
switch mode {
case ULTRALOWPOWER:
d = 5 * time.Millisecond
case STANDARD:
d = 8 * time.Millisecond
case HIGHRESOLUTION:
d = 14 * time.Millisecond
case ULTRAHIGHRESOLUTION:
d = 26 * time.Millisecond
}
return d
}
// readInt converts two bytes to int16
func readInt(msb byte, lsb byte) int16 {
return int16(uint16(msb)<<8 | uint16(lsb))
}
// readUint converts two bytes to uint16
func readUint(msb byte, lsb byte) uint16 {
return (uint16(msb) << 8) | uint16(lsb)
}
+30
View File
@@ -0,0 +1,30 @@
package bmp180
// Constants/addresses used for I2C.
// The I2C address which this device listens to.
const Address = 0x77
// Registers. Names, addresses and comments copied from the datasheet.
const (
AC1_MSB = 0xAA // Calibration coefficients start at 0xAA ends at 0xBF
CMD_TEMP = 0x2E
CMD_PRESSURE = 0x34
REG_CTRL = 0xF4
REG_TEMP_MSB = 0xF6
REG_PRESSURE_MSB = 0xF6
WHO_AM_I = 0xD0
CHIP_ID = 0x55
)
const (
// ULTRALOWPOWER is the lowest oversampling mode of the pressure measurement.
ULTRALOWPOWER OversamplingMode = iota
// BSTANDARD is the standard oversampling mode of the pressure measurement.
STANDARD
// HIGHRESOLUTION is a high oversampling mode of the pressure measurement.
HIGHRESOLUTION
// ULTRAHIGHRESOLUTION is the highest oversampling mode of the pressure measurement.
ULTRAHIGHRESOLUTION
)
+14
View File
@@ -0,0 +1,14 @@
package drivers
import "image/color"
type Displayer interface {
// Size returns the current size of the display.
Size() (x, y int16)
// SetPizel modifies the internal buffer.
SetPixel(x, y int16, c color.RGBA)
// Display sends the buffer (if any) to the screen.
Display() error
}
+42 -4
View File
@@ -1,4 +1,42 @@
// Package drivers is just a placeholder for the sub-packages.
// It is here just to be able to install the package without errors using
// go get -d github.com/ayke/tinygo-drivers
package drivers
// Package drivers provides a collection of hardware drivers for devices that
// can be used together with TinyGo (https://tinygo.org).
//
// Here is an example in TinyGo that uses the BMP180 digital barometer:
//
// package main
//
// import (
// "time"
// "machine"
//
// "github.com/tinygo-org/drivers/bmp180"
// )
//
// func main() {
// machine.I2C0.Configure(machine.I2CConfig{})
// sensor := bmp180.New(machine.I2C0)
// sensor.Configure()
//
// connected := sensor.Connected()
// if !connected {
// println("BMP180 not detected")
// return
// }
// println("BMP180 detected")
//
// for {
// temp, _ := sensor.ReadTemperature()
// println("Temperature:", float32(temp)/1000, "ºC")
//
// pressure, _ := sensor.ReadPressure()
// println("Pressure", float32(pressure)/100000, "hPa")
//
// time.Sleep(2 * time.Second)
// }
// }
//
// Each individual driver is contained within its own sub-package within this package and
// there are no interdependencies in order to minimize the final size of compiled code that
// uses any of these drivers.
//
package drivers // import "tinygo.org/x/drivers"
+173
View File
@@ -0,0 +1,173 @@
// Package ds1307 provides a driver for the DS1307 RTC
//
// Datasheet:
// https://datasheets.maximintegrated.com/en/ds/DS1307.pdf
//
package ds1307 // import "tinygo.org/x/drivers/ds1307"
import (
"errors"
"time"
"machine"
)
// Device wraps an I2C connection to a DS1307 device.
type Device struct {
bus machine.I2C
Address uint8
AddressSRAM uint8
}
// New creates a new DS1307 connection. I2C bus must be already configured.
func New(bus machine.I2C) Device {
return Device{bus: bus,
Address: uint8(I2CAddress),
AddressSRAM: SRAMBeginAddres,
}
}
// SetTime sets the time and date
func (d *Device) SetTime(t time.Time) error {
data := make([]byte, 8)
data[0] = uint8(TimeDate)
data[1] = decToBcd(t.Second())
data[2] = decToBcd(t.Minute())
data[3] = decToBcd(t.Hour())
data[4] = decToBcd(int(t.Weekday() + 1))
data[5] = decToBcd(t.Day())
data[6] = decToBcd(int(t.Month()))
data[7] = decToBcd(t.Year() - 2000)
err := d.bus.Tx(uint16(d.Address), data, nil)
return err
}
// Time returns the time and date
func (d *Device) Time() (time.Time, error) {
data := make([]byte, 8)
err := d.bus.ReadRegister(d.Address, uint8(TimeDate), data)
if err != nil {
return time.Time{}, err
}
seconds := bcdToDec(data[0] & 0x7F)
minute := bcdToDec(data[1])
hour := hoursBCDToInt(data[2])
day := bcdToDec(data[4])
month := time.Month(bcdToDec(data[5]))
year := bcdToDec(data[6])
year += 2000
t := time.Date(year, month, day, hour, minute, seconds, 0, time.UTC)
return t, nil
}
// Seek sets the offset for the next Read or Write on SRAM to offset, interpreted
// according to whence: 0 means relative to the origin of the SRAM, 1 means
// relative to the current offset, and 2 means relative to the end.
// returns new offset and error, if any
func (d *Device) Seek(offset int64, whence int) (int64, error) {
switch whence {
case 0:
whence = SRAMBeginAddres
case 1:
whence = int(d.AddressSRAM)
case 2:
whence = SRAMEndAddress
default:
return 0, errors.New("Invalid starting point")
}
d.AddressSRAM = uint8(whence) + uint8(offset)
if d.AddressSRAM > SRAMEndAddress {
return 0, errors.New("EOF")
}
return int64(d.AddressSRAM), nil
}
// Write writes len(data) bytes to SRAM
// returns number of bytes written and error, if any
func (d *Device) Write(data []byte) (n int, err error) {
if int(d.AddressSRAM)+len(data)-1 > SRAMEndAddress {
return 0, errors.New("Writing outside of SRAM")
}
buffer := make([]byte, len(data)+1)
buffer[0] = d.AddressSRAM
copy(buffer[1:], data)
err = d.bus.Tx(uint16(d.Address), buffer, nil)
if err != nil {
return 0, err
}
d.Seek(int64(len(data)), 1)
return len(data), nil
}
// Read reads len(data) from SRAM
// returns number of bytes written and error, if any
func (d *Device) Read(data []uint8) (n int, err error) {
if int(d.AddressSRAM)+len(data)-1 > SRAMEndAddress {
return 0, errors.New("EOF")
}
err = d.bus.ReadRegister(d.Address, d.AddressSRAM, data)
if err != nil {
return 0, err
}
d.Seek(int64(len(data)), 1)
return len(data), nil
}
// SetOscillatorFrequency sets output oscillator frequency
// Available modes: SQW_OFF, SQW_1HZ, SQW_4KHZ, SQW_8KHZ, SQW_32KHZ
func (d *Device) SetOscillatorFrequency(sqw uint8) error {
data := []byte{uint8(Control), sqw}
err := d.bus.Tx(uint16(d.Address), data, nil)
return err
}
// IsOscillatorRunning returns if the oscillator is running
func (d *Device) IsOscillatorRunning() bool {
data := []byte{0}
err := d.bus.ReadRegister(d.Address, uint8(TimeDate), data)
if err != nil {
return false
}
return (data[0] & (1 << CH)) == 0
}
// SetOscillatorRunning starts/stops internal oscillator by toggling halt bit
func (d *Device) SetOscillatorRunning(running bool) error {
data := make([]byte, 3)
err := d.bus.ReadRegister(d.Address, uint8(TimeDate), data)
if err != nil {
return err
}
if running {
data[0] &^= (1 << CH)
} else {
data[0] |= (1 << CH)
}
data[1], data[0] = data[0], uint8(TimeDate)
err = d.bus.Tx(uint16(d.Address), data[:2], nil)
return err
}
// decToBcd converts int to BCD
func decToBcd(dec int) uint8 {
return uint8(dec + 6*(dec/10))
}
// bcdToDec converts BCD to int
func bcdToDec(bcd uint8) int {
return int(bcd - 6*(bcd>>4))
}
// hoursBCDToInt converts the BCD hours to int
func hoursBCDToInt(value uint8) (hour int) {
if value&0x40 != 0x00 {
hour = bcdToDec(value & 0x1F)
if (value & 0x20) != 0x00 {
hour += 12
}
} else {
hour = bcdToDec(value)
}
return
}
+19
View File
@@ -0,0 +1,19 @@
package ds1307
const (
I2CAddress = 0x68
TimeDate = 0x00
Control = 0x7
//CH is oscillator halt bit
CH = 0x7
SRAMBeginAddres = 0x8
SRAMEndAddress = 0x3F
)
const (
SQW_OFF = 0x0
SQW_1HZ = 0x10
SQW_4KHZ = 0x11
SQW_8KHZ = 0x12
SQW_32KHZ = 0x13
)
+166
View File
@@ -0,0 +1,166 @@
// Package ds3231 provides a driver for the DS3231 RTC
//
// Datasheet:
// https://datasheets.maximintegrated.com/en/ds/DS3231.pdf
package ds3231 // import "tinygo.org/x/drivers/ds3231"
import (
"machine"
"time"
)
type Mode uint8
// Device wraps an I2C connection to a DS3231 device.
type Device struct {
bus machine.I2C
Address uint16
}
// New creates a new DS3231 connection. The I2C bus must already be
// configured.
//
// This function only creates the Device object, it does not touch the device.
func New(bus machine.I2C) Device {
return Device{
bus: bus,
Address: Address,
}
}
// Configure sets up the device for communication
func (d *Device) Configure() bool {
return true
}
// IsTimeValid return true/false is the time in the device is valid
func (d *Device) IsTimeValid() bool {
data := []byte{0}
err := d.bus.ReadRegister(uint8(d.Address), REG_STATUS, data)
if err != nil {
return false
}
return (data[0] & (1 << OSF)) == 0x00
}
// IsRunning returns if the oscillator is running
func (d *Device) IsRunning() bool {
data := []uint8{0}
err := d.bus.ReadRegister(uint8(d.Address), REG_CONTROL, data)
if err != nil {
return false
}
return (data[0] & (1 << EOSC)) == 0x00
}
// SetRunning starts the internal oscillator
func (d *Device) SetRunning(isRunning bool) error {
data := []uint8{0}
err := d.bus.ReadRegister(uint8(d.Address), REG_CONTROL, data)
if err != nil {
return err
}
if isRunning {
data[0] &^= uint8(1 << EOSC)
} else {
data[0] |= 1 << EOSC
}
err = d.bus.WriteRegister(uint8(d.Address), REG_CONTROL, data)
if err != nil {
return err
}
return nil
}
// SetTime sets the date and time in the DS3231
func (d *Device) SetTime(dt time.Time) error {
data := []byte{0}
err := d.bus.ReadRegister(uint8(d.Address), REG_STATUS, data)
if err != nil {
return err
}
data[0] &^= 1 << OSF
err = d.bus.WriteRegister(uint8(d.Address), REG_STATUS, data)
if err != nil {
return err
}
data = make([]uint8, 7)
data[0] = uint8ToBCD(uint8(dt.Second()))
data[1] = uint8ToBCD(uint8(dt.Minute()))
data[2] = uint8ToBCD(uint8(dt.Hour()))
year := uint8(dt.Year() - 2000)
centuryFlag := uint8(0)
if year >= 100 {
year -= 100
centuryFlag = 1 << 7
}
data[3] = uint8ToBCD(uint8(dt.Weekday()))
data[4] = uint8ToBCD(uint8(dt.Day()))
data[5] = uint8ToBCD(uint8(dt.Month()) | centuryFlag)
data[6] = uint8ToBCD(year)
err = d.bus.WriteRegister(uint8(d.Address), REG_TIMEDATE, data)
if err != nil {
return err
}
return nil
}
// ReadTime returns the date and time
func (d *Device) ReadTime() (dt time.Time, err error) {
data := make([]uint8, 7)
err = d.bus.ReadRegister(uint8(d.Address), REG_TIMEDATE, data)
if err != nil {
return
}
second := bcdToInt(data[0] & 0x7F)
minute := bcdToInt(data[1])
hour := hoursBCDToInt(data[2])
day := bcdToInt(data[4])
monthRaw := data[5]
year := bcdToInt(data[6]) + 2000
if monthRaw&(1<<7) != 0x00 {
year += 100
}
month := time.Month(bcdToInt(monthRaw & 0x7F))
dt = time.Date(year, month, day, hour, minute, second, 0, time.UTC)
return
}
// ReadTemperature returns the temperature in millicelsius (mC)
func (d *Device) ReadTemperature() (int32, error) {
data := make([]uint8, 2)
err := d.bus.ReadRegister(uint8(d.Address), REG_TEMP, data)
if err != nil {
return 0, err
}
return int32(data[0])*1000 + int32((data[1]>>6)*25)*10, nil
}
// uint8ToBCD converts a byte to BCD for the DS3231
func uint8ToBCD(value uint8) uint8 {
return value + 6*(value/10)
}
// bcdToInt converts BCD from the DS3231 to int
func bcdToInt(value uint8) int {
return int(value - 6*(value>>4))
}
// hoursBCDToInt converts the BCD hours to int
func hoursBCDToInt(value uint8) (hour int) {
if value&0x40 != 0x00 {
hour = bcdToInt(value & 0x1F)
if (value & 0x20) != 0x00 {
hour += 12
}
} else {
hour = bcdToInt(value)
}
return
}
+48
View File
@@ -0,0 +1,48 @@
package ds3231
// The I2C address which this device listens to.
const Address = 0x68
// Registers
const (
REG_TIMEDATE = 0x00
REG_ALARMONE = 0x07
REG_ALARMTWO = 0x0B
REG_CONTROL = 0x0E
REG_STATUS = 0x0F
REG_AGING = 0x10
REG_TEMP = 0x11
REG_ALARMONE_SIZE = 4
REG_ALARMTWO_SIZE = 3
// DS3231 Control Register Bits
A1IE = 0
A2IE = 1
INTCN = 2
RS1 = 3
RS2 = 4
CONV = 5
BBSQW = 6
EOSC = 7
// DS3231 Status Register Bits
A1F = 0
A2F = 1
BSY = 2
EN32KHZ = 3
OSF = 7
AlarmFlag_Alarm1 = 0x01
AlarmFlag_Alarm2 = 0x02
AlarmFlag_AlarmBoth = 0x03
None Mode = 0
BatteryBackup Mode = 1
Clock Mode = 2
AlarmOne Mode = 3
AlarmTwo Mode = 4
ModeAlarmBoth Mode = 5
)
+80
View File
@@ -0,0 +1,80 @@
// Simple driver to rotate a 4-wire stepper motor
package easystepper // import "tinygo.org/x/drivers/easystepper"
import (
"machine"
"time"
)
// Device holds the pins and the delay between steps
type Device struct {
pins [4]machine.Pin
stepDelay int32
stepNumber int32
}
// New returns a new easystepper driver given 4 pins numbers (not pin object),
// number of steps and rpm
func New(pin1, pin2, pin3, pin4 machine.Pin, steps int32, rpm int32) Device {
pin1.Configure(machine.PinConfig{Mode: machine.PinOutput})
pin2.Configure(machine.PinConfig{Mode: machine.PinOutput})
pin3.Configure(machine.PinConfig{Mode: machine.PinOutput})
pin4.Configure(machine.PinConfig{Mode: machine.PinOutput})
return Device{
pins: [4]machine.Pin{pin1, pin2, pin3, pin4},
stepDelay: 60000000 / (steps * rpm),
}
}
// Move rotates the motor the number of given steps
// (negative steps will rotate it the opposite direction)
func (d *Device) Move(steps int32) {
direction := steps > 0
if steps < 0 {
steps = -steps - d.stepNumber
} else {
steps += d.stepNumber
}
var stepN int8
var s int32
for s = d.stepNumber; s < steps; s++ {
time.Sleep(time.Duration(d.stepDelay) * time.Microsecond)
if direction {
stepN = int8(s % 4)
} else {
stepN = int8((s + 2*(s%2)) % 4)
}
d.stepMotor(stepN)
}
d.stepNumber = int32(stepN)
}
// stepMotor changes the pins' state to the correct step
func (d *Device) stepMotor(step int8) {
switch step {
case 0:
d.pins[0].High()
d.pins[1].Low()
d.pins[2].High()
d.pins[3].Low()
break
case 1:
d.pins[0].Low()
d.pins[1].High()
d.pins[2].High()
d.pins[3].Low()
break
case 2:
d.pins[0].Low()
d.pins[1].High()
d.pins[2].Low()
d.pins[3].High()
break
case 3:
d.pins[0].High()
d.pins[1].Low()
d.pins[2].Low()
d.pins[3].High()
break
}
}
+104
View File
@@ -0,0 +1,104 @@
package espat
// Basic AT commands
const (
// Test that the device is working.
Test = ""
// Restart module
Restart = "+RST"
// Version show info about the current software version.
Version = "+GMR"
// Enter deep-sleep mode
Sleep = "+GSLP"
// Configure echo.
EchoConfig = "E"
// EchoConfigOn
EchoConfigOn = EchoConfig + "1"
// EchoConfigOff
EchoConfigOff = EchoConfig + "0"
// Configure UART
UARTConfig = "+UART"
)
// WiFi commands.
const (
// WiFi mode (sta/AP/sta+AP)
WifiMode = "+CWMODE"
// Connect to an access point.
ConnectAP = "+CWJAP"
// List available AP's
ListAP = "+CWLAP"
// Disconnect from the current AP
Disconnect = "+CWQAP"
// Set softAP configuration. This also activates the ESP8266/ESP32 to act as an access point.
// The settings will not be saved in flash memory, so they will be forgotten on next reset.
SoftAPConfigCurrent = "+CWSAP_CUR"
// Set softAP configuration as saved in flash. This also activates the ESP8266/ESP32 to act as an
// access point. The settings will be saved in flash memory, so they will be used on next reset.
SoftAPConfigFlash = "+CWSAP_DEF"
// List station IP's connected to softAP
ListConnectedIP = "+CWLIF"
// Enable/disable DHCP
DHCPConfig = "+CWDHCP"
// Set MAC address of station
SetStationMACAddress = "+CIPSTAMAC"
// Set MAC address of softAP
SetAPMACAddress = "+CIPAPMAC"
// Set IP address of ESP8266/ESP32 station
SetStationIP = "+CIPSTA"
// Set IP address of ESP8266/ESP32 when acting as access point.
// The IP address will not be saved in flash memory, so it will be forgotten on next reset.
SetSoftAPIPCurrent = "+CIPAP_CUR"
// Set IP address of ESP8266/ESP32 when acting as access point.
// The IP address will be saved in flash memory, so they will be used on next reset.
SetSoftAPIPFlash = "+CIPAP_DEF"
)
// TCP/IP commands
const (
// Get connection status
TCPStatus = "+CIPSTATUS"
// Establish TCP connection or register UDP port
TCPConnect = "+CIPSTART"
// Send Data
TCPSend = "+CIPSEND"
// Close TCP/UDP connection
TCPClose = "+CIPCLOSE"
// Get local IP address
GetLocalIP = "+CIFSR"
// Set multiple connections mode
TCPMultiple = "+CIPMUX"
// Configure as server
ServerConfig = "+CIPSERVER"
// Set transmission mode
TransmissionMode = "+CIPMODE"
// Set timeout when ESP8266/ESP32 runs as TCP server
SetServerTimeout = "+CIPSTO"
)
+252
View File
@@ -0,0 +1,252 @@
// Package espat implements TCP/UDP wireless communication over serial
// with a separate ESP8266 or ESP32 board using the Espressif AT command set
// across a UART interface.
//
// In order to use this driver, the ESP8266/ESP32 must be flashed with firmware
// supporting the AT command set. Many ESP8266/ESP32 chips already have this firmware
// installed by default. You will need to install this firmware if you have an
// ESP8266 that has been flashed with NodeMCU (Lua) or Arduino firmware.
//
// AT Command Core repository:
// https://github.com/espressif/esp32-at
//
// Datasheet:
// https://www.espressif.com/sites/default/files/documentation/0a-esp8266ex_datasheet_en.pdf
//
// AT command set:
// https://www.espressif.com/sites/default/files/documentation/4a-esp8266_at_instruction_set_en.pdf
//
package espat // import "tinygo.org/x/drivers/espat"
import (
"machine"
"strconv"
"strings"
"time"
)
// Device wraps UART connection to the ESP8266/ESP32.
type Device struct {
bus machine.UART
// command responses that come back from the ESP8266/ESP32
response []byte
// data received from a TCP/UDP connection forwarded by the ESP8266/ESP32
socketdata []byte
}
// New returns a new espat driver. Pass in a fully configured UART bus.
func New(b machine.UART) *Device {
return &Device{bus: b, response: make([]byte, 512), socketdata: make([]byte, 0, 1024)}
}
// Configure sets up the device for communication.
func (d Device) Configure() {
}
// Connected checks if there is communication with the ESP8266/ESP32.
func (d *Device) Connected() bool {
d.Execute(Test)
// handle response here, should include "OK"
r := d.Response()
if strings.Contains(string(r), "OK") {
return true
}
return false
}
// Write raw bytes to the UART.
func (d *Device) Write(b []byte) (n int, err error) {
return d.bus.Write(b)
}
// Read raw bytes from the UART.
func (d *Device) Read(b []byte) (n int, err error) {
return d.bus.Read(b)
}
// how long in milliseconds to pause after sending AT commands
const pause = 100
// Execute sends an AT command to the ESP8266/ESP32.
func (d Device) Execute(cmd string) error {
_, err := d.Write([]byte("AT" + cmd + "\r\n"))
return err
}
// Query sends an AT command to the ESP8266/ESP32 that returns the
// current value for some configuration parameter.
func (d Device) Query(cmd string) (string, error) {
_, err := d.Write([]byte("AT" + cmd + "?\r\n"))
return "", err
}
// Set sends an AT command with params to the ESP8266/ESP32 for a
// configuration value to be set.
func (d Device) Set(cmd, params string) error {
_, err := d.Write([]byte("AT" + cmd + "=" + params + "\r\n"))
return err
}
// Version returns the ESP8266/ESP32 firmware version info.
func (d Device) Version() []byte {
d.Execute(Version)
return d.Response()
}
// Echo sets the ESP8266/ESP32 echo setting.
func (d Device) Echo(set bool) {
if set {
d.Execute(EchoConfigOn)
} else {
d.Execute(EchoConfigOff)
}
// TODO: check for success
d.Response()
}
// Reset restarts the ESP8266/ESP32 firmware. Due to how the baud rate changes,
// this messes up communication with the ESP8266/ESP32 module. So make sure you know
// what you are doing when you call this.
func (d Device) Reset() {
d.Execute(Restart)
d.Response()
}
// ReadSocket returns the data that has already been read in from the responses.
func (d *Device) ReadSocket(b []byte) (n int, err error) {
// make sure no data in buffer
d.Response()
count := len(b)
if len(b) >= len(d.socketdata) {
// copy it all, then clear socket data
count = len(d.socketdata)
copy(b, d.socketdata[:count])
d.socketdata = d.socketdata[:0]
} else {
// copy all we can, then keep the remaining socket data around
copy(b, d.socketdata[:count])
copy(d.socketdata, d.socketdata[count:])
d.socketdata = d.socketdata[:len(d.socketdata)-count]
}
return count, nil
}
// Response gets the next response bytes from the ESP8266/ESP32.
func (d *Device) Response() []byte {
var i, retries int
header := make([]byte, 2)
for {
for d.bus.Buffered() > 0 {
// get the first 2 bytes
header[0], _ = d.bus.ReadByte()
header[1], _ = d.bus.ReadByte()
if d.isLeadingCRLF(header) {
// skip it
header[0], _ = d.bus.ReadByte()
header[1], _ = d.bus.ReadByte()
}
if d.isIPD(header) {
// is socket data packet
d.parseIPD()
} else {
// no, so put into response
d.response[i] = header[0]
i++
d.response[i] = header[1]
i++
}
// read the rest of normal command response
for d.bus.Buffered() > 0 {
data, err := d.bus.ReadByte()
if err != nil {
return nil
}
d.response[i] = data
i++
}
}
retries++
if retries > 2 {
break
}
// pause to make sure is no more data to be read
time.Sleep(10 * time.Millisecond)
}
return d.response[:i]
}
func (d *Device) isLeadingCRLF(b []byte) bool {
if len(b) < 2 {
return false
}
if b[0] == 13 && b[1] == 10 {
return true
}
return false
}
func (d *Device) isIPD(b []byte) bool {
if len(b) < 2 {
return false
}
if b[0] == '+' && b[1] == 'I' {
return true
}
return false
}
func (d *Device) parseIPD() bool {
data, _ := d.bus.ReadByte()
if data != 'P' {
// error
return false
}
data, _ = d.bus.ReadByte()
if data != 'D' {
// error
return false
}
data, _ = d.bus.ReadByte()
if data != ',' {
// error
return false
}
// get the expected data length
// skip remaining header up to the ":"
buf := []byte{}
data, _ = d.bus.ReadByte()
for data != ':' {
// put into the buffer with int value here
buf = append(buf, data)
// read next value
data, _ = d.bus.ReadByte()
}
val := string(buf)
count, err := strconv.Atoi(val)
if err != nil {
// not expected data here. what to do?
return false
}
// load up the socket data
// only read the expected amount of data
for m := 0; m < count; m++ {
data, _ = d.bus.ReadByte()
d.socketdata = append(d.socketdata, data)
}
return true
}
+143
View File
@@ -0,0 +1,143 @@
package espat
import (
"strconv"
"time"
)
// DialUDP makes a UDP network connection. raadr is the port that the messages will
// be sent to, and laddr is the port that will be listened to in order to
// receive incoming messages.
func (d Device) DialUDP(network string, laddr, raddr *UDPAddr) (*SerialConn, error) {
addr := raddr.IP.String()
sendport := strconv.Itoa(raddr.Port)
listenport := strconv.Itoa(laddr.Port)
// disconnect any old socket
d.DisconnectSocket()
// connect new socket
d.ConnectUDPSocket(addr, sendport, listenport)
return &SerialConn{Adaptor: &d, laddr: laddr, raddr: raddr}, nil
}
// ListenUDP listens for UDP connections on the port listed in laddr.
func (d Device) ListenUDP(network string, laddr *UDPAddr) (*SerialConn, error) {
addr := "0"
sendport := "0"
listenport := strconv.Itoa(laddr.Port)
// disconnect any old socket
d.DisconnectSocket()
// connect new socket
d.ConnectUDPSocket(addr, sendport, listenport)
return &SerialConn{Adaptor: &d, laddr: laddr}, nil
}
// SerialConn is a loosely net.Conn compatible intended to support
// TCP/UDP over serial.
type SerialConn struct {
Adaptor *Device
laddr *UDPAddr
raddr *UDPAddr
}
// Read reads data from the connection.
// TODO: implement the full method functionality:
// Read can be made to time out and return an Error with Timeout() == true
// after a fixed time limit; see SetDeadline and SetReadDeadline.
func (c *SerialConn) Read(b []byte) (n int, err error) {
// read only the data that has been received via "+IPD" socket
return c.Adaptor.ReadSocket(b)
}
// Write writes data to the connection.
// TODO: implement the full method functionality for timeouts.
// Write can be made to time out and return an Error with Timeout() == true
// after a fixed time limit; see SetDeadline and SetWriteDeadline.
func (c *SerialConn) Write(b []byte) (n int, err error) {
// specify that is a data transfer to the
// currently open socket, not commands to the ESP8266/ESP32.
c.Adaptor.StartSocketSend(len(b))
return c.Adaptor.Write(b)
}
// Close closes the connection.
// Currently only supports a single Read or Write operations without blocking.
func (c *SerialConn) Close() error {
c.Adaptor.DisconnectSocket()
return nil
}
// LocalAddr returns the local network address.
func (c *SerialConn) LocalAddr() UDPAddr {
return *c.laddr
}
// RemoteAddr returns the remote network address.
func (c *SerialConn) RemoteAddr() UDPAddr {
return *c.laddr
}
// SetDeadline sets the read and write deadlines associated
// with the connection. It is equivalent to calling both
// SetReadDeadline and SetWriteDeadline.
//
// A deadline is an absolute time after which I/O operations
// fail with a timeout (see type Error) instead of
// blocking. The deadline applies to all future and pending
// I/O, not just the immediately following call to Read or
// Write. After a deadline has been exceeded, the connection
// can be refreshed by setting a deadline in the future.
//
// An idle timeout can be implemented by repeatedly extending
// the deadline after successful Read or Write calls.
//
// A zero value for t means I/O operations will not time out.
func (c *SerialConn) SetDeadline(t time.Time) error {
return nil
}
// SetReadDeadline sets the deadline for future Read calls
// and any currently-blocked Read call.
// A zero value for t means Read will not time out.
func (c *SerialConn) SetReadDeadline(t time.Time) error {
return nil
}
// SetWriteDeadline sets the deadline for future Write calls
// and any currently-blocked Write call.
// Even if write times out, it may return n > 0, indicating that
// some of the data was successfully written.
// A zero value for t means Write will not time out.
func (c *SerialConn) SetWriteDeadline(t time.Time) error {
return nil
}
// The following definitions are here to support a Golang standard package
// net-compatible interface for IP until TinyGo can compile the net package.
// IP is an IP address. Unlike the standard implementation, it is only
// a buffer of bytes that contains the string form of the IP address, not the
// full byte format used by the Go standard .
type IP []byte
// UDPAddr here to serve as compatible type. until TinyGo can compile the net package.
type UDPAddr struct {
IP IP
Port int
Zone string // IPv6 scoped addressing zone; added in Go 1.1
}
// ParseIP parses s as an IP address, returning the result.
func ParseIP(s string) IP {
return IP([]byte(s))
}
// String returns the string form of the IP address ip.
func (ip IP) String() string {
return string(ip)
}
+97
View File
@@ -0,0 +1,97 @@
package espat
import (
"strconv"
"time"
)
const (
TCPMuxSingle = 0
TCPMuxMultiple = 1
TCPTransferModeNormal = 0
TCPTransferModeUnvarnished = 1
)
// ConnectTCPSocket creates a new TCP socket connection for the ESP8266/ESP32.
// Currently only supports single connection mode.
func (d *Device) ConnectTCPSocket(addr, port string) error {
protocol := "TCP"
val := "\"" + protocol + "\",\"" + addr + "\"," + port
d.Set(TCPConnect, val)
time.Sleep(100 * time.Millisecond)
d.Response()
return nil
}
// ConnectUDPSocket creates a new UDP connection for the ESP8266/ESP32.
func (d *Device) ConnectUDPSocket(addr, sendport, listenport string) error {
protocol := "UDP"
val := "\"" + protocol + "\",\"" + addr + "\"," + sendport + "," + listenport + ",2"
d.Set(TCPConnect, val)
time.Sleep(pause * time.Millisecond)
d.Response()
return nil
}
// DisconnectSocket disconnects the ESP8266/ESP32 from the current TCP/UDP connection.
func (d *Device) DisconnectSocket() error {
d.Execute(TCPClose)
time.Sleep(pause * time.Millisecond)
d.Response()
return nil
}
// SetMux sets the ESP8266/ESP32 current client TCP/UDP configuration for concurrent connections
// either single TCPMuxSingle or multiple TCPMuxMultiple (up to 4).
func (d *Device) SetMux(mode int) error {
val := strconv.Itoa(mode)
d.Set(TCPMultiple, val)
time.Sleep(pause * time.Millisecond)
d.Response()
return nil
}
// GetMux returns the ESP8266/ESP32 current client TCP/UDP configuration for concurrent connections.
func (d *Device) GetMux() ([]byte, error) {
d.Query(TCPMultiple)
return d.Response(), nil
}
// SetTCPTransferMode sets the ESP8266/ESP32 current client TCP/UDP transfer mode.
// Either TCPTransferModeNormal or TCPTransferModeUnvarnished.
func (d *Device) SetTCPTransferMode(mode int) error {
val := strconv.Itoa(mode)
d.Set(TransmissionMode, val)
time.Sleep(pause * time.Millisecond)
d.Response()
return nil
}
// GetTCPTransferMode returns the ESP8266/ESP32 current client TCP/UDP transfer mode.
func (d *Device) GetTCPTransferMode() []byte {
d.Query(TransmissionMode)
return d.Response()
}
// StartSocketSend gets the ESP8266/ESP32 ready to receive TCP/UDP socket data.
func (d *Device) StartSocketSend(size int) error {
val := strconv.Itoa(size)
d.Set(TCPSend, val)
// TODO: wait until ">" is received, which indicates
// ready to receive data
d.Response()
return nil
}
// EndSocketSend tell the ESP8266/ESP32 the TCP/UDP socket data sending is complete,
// and to return to command mode. This is only used in "unvarnished" raw mode.
func (d *Device) EndSocketSend() error {
d.Write([]byte("+++"))
// TODO: wait until ">" is received, which indicates
// ready to receive data
d.Response()
return nil
}
+154
View File
@@ -0,0 +1,154 @@
package espat
import (
"strconv"
"time"
)
const (
WifiModeClient = 1
WifiModeAP = 2
WifiModeDual = 3
WifiAPSecurityOpen = 1
WifiAPSecurityWPA_PSK = 2
WifiAPSecurityWPA2_PSK = 3
WifiAPSecurityWPA_WPA2_PSK = 4
)
// GetWifiMode returns the ESP8266/ESP32 wifi mode.
func (d *Device) GetWifiMode() []byte {
d.Query(WifiMode)
return d.Response()
}
// SetWifiMode sets the ESP8266/ESP32 wifi mode.
func (d *Device) SetWifiMode(mode int) error {
val := strconv.Itoa(mode)
d.Set(WifiMode, val)
time.Sleep(pause * time.Millisecond)
d.Response()
return nil
}
// Wifi Client
// GetConnectedAP returns the ESP8266/ESP32 is currently connected to as a client.
func (d *Device) GetConnectedAP() []byte {
d.Query(ConnectAP)
return d.Response()
}
// ConnectToAP connects the ESP8266/ESP32 to an access point.
// ws is the number of seconds to wait for connection.
func (d *Device) ConnectToAP(ssid, pwd string, ws int) error {
val := "\"" + ssid + "\",\"" + pwd + "\""
d.Set(ConnectAP, val)
// TODO: a better way to wait for connect and check for up to ws seconds.
time.Sleep(time.Duration(ws) * time.Second)
d.Response()
return nil
}
// DisconnectFromAP disconnects the ESP8266/ESP32 from the current access point.
func (d *Device) DisconnectFromAP() error {
d.Execute(Disconnect)
time.Sleep(1000 * time.Millisecond)
d.Response()
return nil
}
// GetClientIP returns the ESP8266/ESP32 current client IP addess when connected to an Access Point.
func (d *Device) GetClientIP() string {
d.Query(SetStationIP)
return string(d.Response())
}
// SetClientIP sets the ESP8266/ESP32 current client IP addess when connected to an Access Point.
func (d *Device) SetClientIP(ipaddr string) []byte {
val := "\"" + ipaddr + "\""
d.Set(ConnectAP, val)
time.Sleep(500 * time.Millisecond)
d.Response()
return nil
}
// Access Point
// GetAPConfig returns the ESP8266/ESP32 current configuration when acting as an Access Point.
func (d *Device) GetAPConfig() string {
d.Query(SoftAPConfigCurrent)
return string(d.Response())
}
// SetAPConfig sets the ESP8266/ESP32 current configuration when acting as an Access Point.
// ch indicates which radiochannel to use. security should be one of the const values
// such as WifiAPSecurityOpen etc.
func (d *Device) SetAPConfig(ssid, pwd string, ch, security int) error {
chval := strconv.Itoa(ch)
ecnval := strconv.Itoa(security)
val := "\"" + ssid + "\",\"" + pwd + "\"," + chval + "," + ecnval
d.Set(SoftAPConfigCurrent, val)
time.Sleep(1000 * time.Millisecond)
d.Response()
return nil
}
// GetAPClients returns the ESP8266/ESP32 current clients when acting as an Access Point.
func (d *Device) GetAPClients() string {
d.Query(ListConnectedIP)
return string(d.Response())
}
// GetAPIP returns the ESP8266/ESP32 current IP addess when configured as an Access Point.
func (d *Device) GetAPIP() string {
d.Query(SetSoftAPIPCurrent)
return string(d.Response())
}
// SetAPIP sets the ESP8266/ESP32 current IP addess when configured as an Access Point.
func (d *Device) SetAPIP(ipaddr string) error {
val := "\"" + ipaddr + "\""
d.Set(SetSoftAPIPCurrent, val)
time.Sleep(500 * time.Millisecond)
d.Response()
return nil
}
// GetAPConfigFlash returns the ESP8266/ESP32 current configuration acting as an Access Point
// from flash storage. These settings are those used after a reset.
func (d *Device) GetAPConfigFlash() string {
d.Query(SoftAPConfigFlash)
return string(d.Response())
}
// SetAPConfigFlash sets the ESP8266/ESP32 current configuration acting as an Access Point,
// and saves them to flash storage. These settings will be used after a reset.
// ch indicates which radiochannel to use. security should be one of the const values
// such as WifiAPSecurityOpen etc.
func (d *Device) SetAPConfigFlash(ssid, pwd string, ch, security int) error {
chval := strconv.Itoa(ch)
ecnval := strconv.Itoa(security)
val := "\"" + ssid + "\",\"" + pwd + "\"," + chval + "," + ecnval
d.Set(SoftAPConfigFlash, val)
time.Sleep(1000 * time.Millisecond)
d.Response()
return nil
}
// GetAPIPFlash returns the ESP8266/ESP32 IP address as saved to flash storage.
// This is the IP address that will be used after a reset.
func (d *Device) GetAPIPFlash() string {
d.Query(SetSoftAPIPFlash)
return string(d.Response())
}
// SetAPIPFlash sets the ESP8266/ESP32 current IP addess when configured as an Access Point.
// The IP will be saved to flash storage, and will be used after a reset.
func (d *Device) SetAPIPFlash(ipaddr string) error {
val := "\"" + ipaddr + "\""
d.Set(SetSoftAPIPFlash, val)
time.Sleep(500 * time.Millisecond)
d.Response()
return nil
}
+25
View File
@@ -0,0 +1,25 @@
package main
import (
"machine"
"time"
"tinygo.org/x/drivers/adxl345"
)
func main() {
machine.I2C0.Configure(machine.I2CConfig{})
sensor := adxl345.New(machine.I2C0)
sensor.Configure()
println("ADXL345 starts")
for {
x, y, z, _ := sensor.ReadAcceleration()
println("X:", x, "Y:", y, "Z:", z)
rx, ry, rz := sensor.ReadRawAcceleration()
println("X (raw):", rx, "Y (raw):", ry, "Z (raw):", rz)
time.Sleep(100 * time.Millisecond)
}
}
+35
View File
@@ -0,0 +1,35 @@
// Connects to an APA102 SPI RGB LED strip with 30 LEDS.
package main
import (
"image/color"
"machine"
"time"
"tinygo.org/x/drivers/apa102"
)
func main() {
machine.SPI0.Configure(machine.SPIConfig{
Frequency: 500000,
Mode: 0})
a := apa102.New(machine.SPI0)
leds := make([]color.RGBA, 30)
rg := false
for {
rg = !rg
for i := range leds {
rg = !rg
if rg {
leds[i] = color.RGBA{R: 0xff, G: 0x00, B: 0x00, A: 0x77}
} else {
leds[i] = color.RGBA{R: 0x00, G: 0xff, B: 0x00, A: 0x77}
}
}
a.WriteColors(leds)
time.Sleep(100 * time.Millisecond)
}
}
+122
View File
@@ -0,0 +1,122 @@
package main
import (
"machine"
"time"
"tinygo.org/x/drivers/at24cx"
)
func main() {
machine.I2C0.Configure(machine.I2CConfig{})
eeprom := at24cx.New(machine.I2C0)
eeprom.Configure(at24cx.Config{})
values := make([]uint8, 100)
for i := uint16(0); i < 100; i++ {
values[i] = uint8(65 + i%26)
}
_, err := eeprom.WriteAt(values, 0)
if err != nil {
println("There was an error in WriteAt:", err)
return
}
for i := uint16(0); i < 26; i++ {
err = eeprom.WriteByte(100+i, uint8(90-i))
if err != nil {
println("There was an error in WriteByte:", i, err)
return
}
time.Sleep(2 * time.Millisecond)
}
println("\n\r\n\rRead 26 bytes one by one from address 0")
println("Expected: ABCDEFGHIJKLMNOPQRSTUVWXYZ")
print("Real: ")
for i := uint16(0); i < 26; i++ {
char, err := eeprom.ReadByte(i)
print(string(char))
if err != nil {
println("There was an error in ReadByte:", i, err)
return
}
}
println("")
println("\n\r\n\rRead 100 bytes from address 26")
println("Expected: ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVZYXWVUTSRQPONMLKJIHGFEDCBA")
print("Real: ")
data := make([]byte, 100)
_, err = eeprom.ReadAt(data, 26)
if err != nil {
println("There was an error in ReadAt:", err)
return
}
for i := 0; i < 100; i++ {
print(string(data[i]))
}
println("")
// Move to the beginning of memory
eeprom.Seek(0, 0)
_, err = eeprom.Write([]uint8{88, 88, 88})
if err != nil {
println("There was an error in Write:", err)
return
}
println("\n\r\n\rRead 3 bytes")
println("Expected: DEF")
print("Real: ")
data = make([]byte, 3)
_, err = eeprom.Read(data)
if err != nil {
println("There was an error in Read:", err)
return
}
for _, char := range data {
print(string(char))
}
println("")
println("\n\r\n\rRead another 3 bytes (from the beginning this time)")
eeprom.Seek(-6, 1)
println("Expected: XXX")
print("Real: ")
data = make([]byte, 3)
_, err = eeprom.Read(data)
if err != nil {
println("There was an error in Read:", err)
return
}
for _, char := range data {
print(string(char))
}
println("")
// Move to the end of memory
eeprom.Seek(-4, 2)
_, err = eeprom.Write([]uint8{89, 90, 89, 90})
if err != nil {
println("There was an error in Write:", err)
return
}
println("\n\r\n\rRead the last 4 bytes of the memory and the 3 of the beginning")
eeprom.Seek(-4, 1)
println("Expected: YZYZXXX")
print("Real: ")
data = make([]byte, 7)
_, err = eeprom.Read(data)
if err != nil {
println("There was an error in Read:", err)
return
}
for _, char := range data {
print(string(char))
}
println("")
}
+22
View File
@@ -0,0 +1,22 @@
package main
import (
"time"
"machine"
"tinygo.org/x/drivers/bh1750"
)
func main() {
machine.I2C0.Configure(machine.I2CConfig{})
sensor := bh1750.New(machine.I2C0)
sensor.Configure()
for {
lux := sensor.Illuminance()
println("Illuminance:", lux, "lx")
time.Sleep(500 * time.Millisecond)
}
}
+41
View File
@@ -0,0 +1,41 @@
// Connects to an BlinkM I2C RGB LED.
// http://thingm.com/fileadmin/thingm/downloads/BlinkM_datasheet.pdf
package main
import (
"machine"
"time"
"tinygo.org/x/drivers/blinkm"
)
func main() {
machine.I2C0.Configure(machine.I2CConfig{})
bm := blinkm.New(machine.I2C0)
bm.Configure()
maj, min, _ := bm.Version()
println("Firmware version:", string(maj), string(min))
count := 0
for {
switch count {
case 0:
// Crimson
bm.SetRGB(0xdc, 0x14, 0x3c)
count = 1
case 1:
// MediumPurple
bm.SetRGB(0x93, 0x70, 0xdb)
count = 2
case 2:
// MediumSeaGreen
bm.SetRGB(0x3c, 0xb3, 0x71)
count = 0
}
time.Sleep(100 * time.Millisecond)
}
}
+35
View File
@@ -0,0 +1,35 @@
package main
import (
"machine"
"strconv"
"time"
"tinygo.org/x/drivers/bme280"
)
func main() {
machine.I2C0.Configure(machine.I2CConfig{})
sensor := bme280.New(machine.I2C0)
sensor.Configure()
connected := sensor.Connected()
if !connected {
println("BME280 not detected")
}
println("BME280 detected")
for {
temp, _ := sensor.ReadTemperature()
println("Temperature:", strconv.FormatFloat(float64(temp)/1000, 'f', 2, 64), "ºC")
press, _ := sensor.ReadPressure()
println("Pressure:", strconv.FormatFloat(float64(press)/100000, 'f', 2, 64), "hPa")
hum, _ := sensor.ReadHumidity()
println("Humidity:", strconv.FormatFloat(float64(hum)/100, 'f', 2, 64), "%")
alt, _ := sensor.ReadAltitude()
println("Altitude:", alt, "m")
time.Sleep(2 * time.Second)
}
}
+32
View File
@@ -0,0 +1,32 @@
package main
import (
"time"
"machine"
"tinygo.org/x/drivers/bmp180"
)
func main() {
machine.I2C0.Configure(machine.I2CConfig{})
sensor := bmp180.New(machine.I2C0)
sensor.Configure()
connected := sensor.Connected()
if !connected {
println("BMP180 not detected")
return
}
println("BMP180 detected")
for {
temp, _ := sensor.ReadTemperature()
println("Temperature:", float32(temp)/1000, "ºC")
pressure, _ := sensor.ReadPressure()
println("Pressure", float32(pressure)/100000, "hPa")
time.Sleep(2 * time.Second)
}
}
+32
View File
@@ -0,0 +1,32 @@
package main
import (
"machine"
"tinygo.org/x/drivers/ds1307"
)
func main() {
machine.I2C0.Configure(machine.I2CConfig{})
rtc := ds1307.New(machine.I2C0)
read := make([]byte, 5)
for {
rtc.Seek(0, 0)
_, err := rtc.Write([]byte{1, 2, 3, 4, 5})
if err != nil {
println("Error while writing data:", err)
break
}
rtc.Seek(0, 0)
_, err = rtc.Read(read)
if err != nil {
println("Error while reading data:", err)
break
}
for data := range read {
println(data, " ")
}
}
}
+25
View File
@@ -0,0 +1,25 @@
package main
import (
"machine"
"time"
"tinygo.org/x/drivers/ds1307"
)
func main() {
machine.I2C0.Configure(machine.I2CConfig{})
rtc := ds1307.New(machine.I2C0)
rtc.SetTime(time.Date(2019, 5, 15, 20, 34, 12, 0, time.UTC))
for {
t, err := rtc.Time()
if err != nil {
println("Error reading date:", err)
break
}
println(t.Hour(), ":", t.Minute(), ":", t.Second(), " ", t.Day(), "/", t.Month(), "/", t.Year())
}
}
+45
View File
@@ -0,0 +1,45 @@
// Connects to an MAG3110 I2C magnetometer.
package main
import (
"machine"
"time"
"fmt"
"tinygo.org/x/drivers/ds3231"
)
func main() {
machine.I2C0.Configure(machine.I2CConfig{})
rtc := ds3231.New(machine.I2C0)
rtc.Configure()
valid := rtc.IsTimeValid()
if !valid {
date := time.Date(2019, 12, 05, 20, 34, 12, 0, time.UTC)
rtc.SetTime(date)
}
running := rtc.IsRunning()
if !running {
err := rtc.SetRunning(true)
if err != nil {
fmt.Println("Error configuring RTC")
}
}
for {
dt, err := rtc.ReadTime()
if err != nil {
fmt.Println("Error reading date:", err)
} else {
fmt.Printf("Date: %d/%s/%02d %02d:%02d:%02d \r\n", dt.Year(), dt.Month(), dt.Day(), dt.Hour(), dt.Minute(), dt.Second())
}
temp, _ := rtc.ReadTemperature()
fmt.Printf("Temperature: %.2f ºC \r\n", float32(temp)/1000)
time.Sleep(time.Second * 1)
}
}
+22
View File
@@ -0,0 +1,22 @@
package main
import (
"machine"
"time"
"tinygo.org/x/drivers/easystepper"
)
func main() {
motor := easystepper.New(machine.P13, machine.P15, machine.P14, machine.P16, 200, 75)
for {
println("CLOCKWISE")
motor.Move(2050)
time.Sleep(time.Millisecond * 1000)
println("COUNTERCLOCKWISE")
motor.Move(-2050)
time.Sleep(time.Millisecond * 1000)
}
}
+129
View File
@@ -0,0 +1,129 @@
// This is a console to a ESP8266/ESP32 running on the device UART1.
// Allows you to type AT commands from your computer via the microcontroller.
//
// In other words:
// Your computer <--> UART0 <--> MCU <--> UART1 <--> ESP8266 <--> INTERNET
//
// More information on the Espressif AT command set at:
// https://www.espressif.com/sites/default/files/documentation/4a-esp8266_at_instruction_set_en.pdf
//
package main
import (
"machine"
"time"
"tinygo.org/x/drivers/espat"
)
// change actAsAP to true to act as an access point instead of connecting to one.
const actAsAP = false
// access point info
const ssid = "YOURSSID"
const pass = "YOURPASS"
// change these to connect to a different UART or pins for the ESP8266/ESP32
var (
uart = machine.UART1
tx = machine.D10
rx = machine.D11
console = machine.UART0
adaptor *espat.Device
)
func main() {
uart.Configure(machine.UARTConfig{TX: tx, RX: rx})
// Init esp8266
adaptor = espat.New(uart)
adaptor.Configure()
// first check if connected
if adaptor.Connected() {
adaptor.Echo(false)
console.Write([]byte("\r\n"))
console.Write([]byte("ESP-AT console enabled.\r\n"))
console.Write([]byte("Firmware version:\r\n"))
console.Write(adaptor.Version())
console.Write([]byte("\r\n"))
if actAsAP {
provideAP()
} else {
connectToAP()
}
console.Write([]byte("Type an AT command then press enter:\r\n"))
prompt()
} else {
console.Write([]byte("\r\n"))
console.Write([]byte("Unable to connect to wifi adaptor.\r\n"))
return
}
input := make([]byte, 64)
i := 0
for {
if console.Buffered() > 0 {
data, _ := console.ReadByte()
switch data {
case 13:
// return key
console.Write([]byte("\r\n"))
// send command to ESP8266
input[i] = byte('\r')
input[i+1] = byte('\n')
adaptor.Write(input[:i+2])
// give the ESP8266 a chance to respond.
time.Sleep(10 * time.Millisecond)
// display response
console.Write(adaptor.Response())
// prompt
prompt()
i = 0
continue
default:
// just echo the character
console.WriteByte(data)
input[i] = data
i++
}
}
time.Sleep(10 * time.Millisecond)
}
}
func prompt() {
console.Write([]byte("ESPAT>"))
}
// connect to access point
func connectToAP() {
console.Write([]byte("Connecting to wifi network...\r\n"))
adaptor.SetWifiMode(espat.WifiModeClient)
adaptor.ConnectToAP(ssid, pass, 10)
console.Write([]byte("Connected.\r\n"))
console.Write([]byte(adaptor.GetClientIP()))
console.Write([]byte("\r\n"))
}
// provide access point
func provideAP() {
console.Write([]byte("Starting wifi network as access point '"))
console.Write([]byte(ssid))
console.Write([]byte("'...\r\n"))
adaptor.SetWifiMode(espat.WifiModeAP)
adaptor.SetAPConfig(ssid, pass, 7, espat.WifiAPSecurityWPA2_PSK)
console.Write([]byte("Ready.\r\n"))
console.Write([]byte(adaptor.GetAPIP()))
console.Write([]byte("\r\n"))
}
+111
View File
@@ -0,0 +1,111 @@
// This is a sensor hub that uses a ESP8266/ESP32 running on the device UART1.
// It creates a UDP "server" you can use to get info to/from your computer via the microcontroller.
//
// In other words:
// Your computer <--> UART0 <--> MCU <--> UART1 <--> ESP8266 <--> INTERNET
//
package main
import (
"machine"
"time"
"tinygo.org/x/drivers/espat"
)
// change actAsAP to true to act as an access point instead of connecting to one.
const actAsAP = false
// access point info
const ssid = "YOURSSID"
const pass = "YOURPASS"
// change these to connect to a different UART or pins for the ESP8266/ESP32
var (
uart = machine.UART1
tx = machine.D10
rx = machine.D11
console = machine.UART0
adaptor *espat.Device
)
func main() {
uart.Configure(machine.UARTConfig{TX: tx, RX: rx})
// Init esp8266
adaptor = espat.New(uart)
adaptor.Configure()
readyled := machine.LED
readyled.Configure(machine.PinConfig{Mode: machine.PinOutput})
readyled.High()
// first check if connected
if adaptor.Connected() {
console.Write([]byte("Connected to wifi adaptor.\r\n"))
adaptor.Echo(false)
if actAsAP {
provideAP()
} else {
connectToAP()
}
} else {
console.Write([]byte("\r\n"))
console.Write([]byte("Unable to connect to wifi adaptor.\r\n"))
return
}
// now make UDP connection
laddr := &espat.UDPAddr{Port: 2222}
console.Write([]byte("Loading UDP listener...\r\n"))
conn, _ := adaptor.ListenUDP("UDP", laddr)
console.Write([]byte("Waiting for data...\r\n"))
data := make([]byte, 50)
blink := true
for {
n, _ := conn.Read(data)
if n > 0 {
console.Write(data[:n])
console.Write([]byte("\r\n"))
conn.Write([]byte("hello back\r\n"))
}
blink = !blink
if blink {
readyled.High()
} else {
readyled.Low()
}
time.Sleep(500 * time.Millisecond)
}
// Right now this code is never reached. Need a way to trigger it...
console.Write([]byte("Disconnecting UDP...\r\n"))
conn.Close()
console.Write([]byte("Done.\r\n"))
}
// connect to access point
func connectToAP() {
console.Write([]byte("Connecting to wifi network...\r\n"))
adaptor.SetWifiMode(espat.WifiModeClient)
adaptor.ConnectToAP(ssid, pass, 10)
console.Write([]byte("Connected.\r\n"))
console.Write([]byte(adaptor.GetClientIP()))
console.Write([]byte("\r\n"))
}
// provide access point
func provideAP() {
console.Write([]byte("Starting wifi network as access point '"))
console.Write([]byte(ssid))
console.Write([]byte("'...\r\n"))
adaptor.SetWifiMode(espat.WifiModeAP)
adaptor.SetAPConfig(ssid, pass, 7, espat.WifiAPSecurityWPA2_PSK)
console.Write([]byte("Ready.\r\n"))
console.Write([]byte(adaptor.GetAPIP()))
console.Write([]byte("\r\n"))
}
+83
View File
@@ -0,0 +1,83 @@
// This is a sensor station that uses a ESP8266 or ESP32 running on the device UART1.
// It creates a UDP connection you can use to get info to/from your computer via the microcontroller.
//
// In other words:
// Your computer <--> UART0 <--> MCU <--> UART1 <--> ESP8266
//
package main
import (
"machine"
"time"
"tinygo.org/x/drivers/espat"
)
// access point info
const ssid = "YOURSSID"
const pass = "YOURPASS"
// IP address of the listener aka "hub". Replace with your own info.
const hubIP = "0.0.0.0"
// change these to connect to a different UART or pins for the ESP8266/ESP32
var (
uart = machine.UART1
tx = machine.D10
rx = machine.D11
console = machine.UART0
adaptor *espat.Device
)
func main() {
uart.Configure(machine.UARTConfig{TX: tx, RX: rx})
// Init esp8266/esp32
adaptor = espat.New(uart)
adaptor.Configure()
// first check if connected
if adaptor.Connected() {
console.Write([]byte("Connected to wifi adaptor.\r\n"))
adaptor.Echo(false)
connectToAP()
} else {
console.Write([]byte("\r\n"))
console.Write([]byte("Unable to connect to wifi adaptor.\r\n"))
return
}
// now make UDP connection
ip := espat.ParseIP(hubIP)
raddr := &espat.UDPAddr{IP: ip, Port: 2222}
laddr := &espat.UDPAddr{Port: 2222}
console.Write([]byte("Dialing UDP connection...\r\n"))
conn, _ := adaptor.DialUDP("udp", laddr, raddr)
for {
// send data
console.Write([]byte("Sending data...\r\n"))
conn.Write([]byte("hello\r\n"))
time.Sleep(1000 * time.Millisecond)
}
// Right now this code is never reached. Need a way to trigger it...
console.Write([]byte("Disconnecting UDP...\r\n"))
conn.Close()
console.Write([]byte("Done.\r\n"))
}
// connect to access point
func connectToAP() {
console.Write([]byte("Connecting to wifi network...\r\n"))
adaptor.SetWifiMode(espat.WifiModeClient)
adaptor.ConnectToAP(ssid, pass, 10)
console.Write([]byte("Connected.\r\n"))
console.Write([]byte(adaptor.GetClientIP()))
console.Write([]byte("\r\n"))
}
+29
View File
@@ -0,0 +1,29 @@
package main
import (
"fmt"
"machine"
"tinygo.org/x/drivers/gps"
)
func main() {
println("GPS I2C Example")
machine.I2C0.Configure(machine.I2CConfig{})
ublox := gps.NewI2C(&machine.I2C0)
parser := gps.Parser(ublox)
var fix gps.Fix
for {
fix = parser.NextFix()
if fix.Valid {
print(fix.Time.Format("15:04:05"))
print(", lat=", fmt.Sprintf("%f", fix.Latitude))
print(", long=", fmt.Sprintf("%f", fix.Longitude))
print(", altitude:=", fix.Altitude)
print(", satellites=", fix.Satellites)
println()
} else {
println("No fix")
}
}
}
+29
View File
@@ -0,0 +1,29 @@
package main
import (
"fmt"
"machine"
"tinygo.org/x/drivers/gps"
)
func main() {
println("GPS UART Example")
machine.UART1.Configure(machine.UARTConfig{BaudRate: 9600})
ublox := gps.NewUART(&machine.UART1)
parser := gps.Parser(ublox)
var fix gps.Fix
for {
fix = parser.NextFix()
if fix.Valid {
print(fix.Time.Format("15:04:05"))
print(", lat=", fmt.Sprintf("%f", fix.Latitude))
print(", long=", fmt.Sprintf("%f", fix.Longitude))
print(", altitude:=", fix.Altitude)
print(", satellites=", fix.Satellites)
println()
} else {
println("No fix")
}
}
}
+31
View File
@@ -0,0 +1,31 @@
package main
import (
"machine"
"tinygo.org/x/drivers/hd44780"
)
func main() {
lcd, _ := hd44780.NewGPIO4Bit(
[]machine.Pin{machine.P0, machine.P1, machine.P2, machine.P3},
machine.P4,
machine.P5,
machine.P6,
)
lcd.Configure(hd44780.Config{
Width: 16,
Height: 2,
CursorOnOff: true,
CursorBlink: true,
})
lcd.CreateCharacter(0x0, []byte{0x04, 0x0E, 0x0E, 0x0E, 0x0E, 0x1F, 0x04, 0x0})
lcd.Write([]byte{0x0})
lcd.Display()
for {
}
}
+31
View File
@@ -0,0 +1,31 @@
package main
import (
"machine"
"tinygo.org/x/drivers/hd44780"
)
func main() {
lcd, _ := hd44780.NewGPIO4Bit(
[]machine.Pin{machine.P0, machine.P1, machine.P2, machine.P3},
machine.P4,
machine.P5,
machine.P6,
)
lcd.Configure(hd44780.Config{
Width: 16,
Height: 2,
CursorOnOff: true,
CursorBlink: true,
})
lcd.Write([]byte("This is a long line"))
lcd.Display()
for {
}
}
+142
View File
@@ -0,0 +1,142 @@
package gopherimg
import "image/color"
var ImageArray = []uint32{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6015176, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6015176, 6015176, 6015176, 6015176, 6015176, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176,
6015176, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6015176, 6015176, 6015176,
6015176, 6015176, 6015176, 6015176, 6015176, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176,
6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176,
6015176, 6015176, 0, 0, 0, 0, 0, 0, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176,
6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176,
6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 0, 0, 0, 0, 0, 0, 6015176, 6015176, 6015176, 6015176,
6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176,
6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 0, 0, 0, 0, 0, 0,
6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176,
6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176,
6015176, 6015176, 0, 0, 0, 0, 0, 0, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176,
6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176,
6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 0, 0, 0, 0, 0, 0, 6015176, 6015176, 6015176, 6015176,
6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176,
6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 0, 0, 0, 0, 0, 0,
6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176,
6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176,
6015176, 6015176, 0, 0, 0, 0, 0, 6015176, 6015176, 6015176, 6015176, 6015176, 16777215, 16777215, 16777215,
16777215, 16777215, 16777215, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176,
6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 0, 0, 0, 0, 0, 6015176, 6015176, 6015176, 6015176,
16777215, 16777215, 0, 0, 0, 0, 16777215, 16777215, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176,
6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 0, 0, 0, 0, 6015176, 6015176, 6015176,
6015176, 16777215, 16777215, 16777215, 0, 0, 0, 0, 0, 16777215, 16777215, 6015176, 6015176, 6015176, 6015176,
6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 0, 0, 0, 0, 6015176,
6015176, 6015176, 16777215, 16777215, 16777215, 16777215, 0, 0, 0, 0, 0, 16777215, 16777215, 6015176, 6015176,
6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 0, 0,
0, 6015176, 6015176, 6015176, 6015176, 16777215, 16777215, 16777215, 16777215, 0, 0, 0, 0, 0, 16777215, 16777215,
16777215, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176,
6015176, 6015176, 0, 0, 0, 6015176, 6015176, 6015176, 6015176, 16777215, 16777215, 16777215, 16777215, 16777215, 0,
0, 0, 16777215, 16777215, 16777215, 16777215, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176,
6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 0, 0, 6015176, 6015176, 6015176, 6015176, 6015176, 16777215,
16777215, 16777215, 16777215, 16777215, 16777215, 16777215, 16777215, 16777215, 16777215, 16777215, 16777215,
6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176,
6015176, 0, 0, 6015176, 6015176, 6015176, 6015176, 6015176, 16777215, 16777215, 16777215, 16777215, 16777215,
16777215, 16777215, 16777215, 16777215, 16777215, 16777215, 16777215, 6015176, 6015176, 6015176, 6015176, 6015176,
6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 0, 0, 6015176, 6015176, 6015176, 6015176,
6015176, 16777215, 16777215, 16777215, 16777215, 16777215, 16777215, 16777215, 16777215, 16777215, 16777215,
16777215, 16777215, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176,
6015176, 6015176, 6015176, 0, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 16777215, 16777215, 16777215,
16777215, 16777215, 16777215, 16777215, 16777215, 16777215, 16777215, 16777215, 6015176, 6015176, 6015176, 6015176,
6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 0, 6015176, 6015176,
6015176, 6015176, 6015176, 6015176, 6015176, 16777215, 16777215, 16777215, 16777215, 16777215, 16777215, 16777215,
16777215, 16777215, 6015176, 6015176, 6015176, 6015176, 8145949, 8145949, 6015176, 6015176, 6015176, 6015176,
6015176, 6015176, 6015176, 6015176, 6015176, 0, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176,
6015176, 16777215, 16777215, 16777215, 16777215, 16777215, 16777215, 16777215, 16777215, 6015176, 6015176, 6015176,
8145949, 8145949, 8145949, 8145949, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 0,
6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 16777215, 16777215,
16777215, 16777215, 6015176, 6015176, 6015176, 6015176, 8145949, 8145949, 8145949, 8145949, 8145949, 16777215,
16777215, 16777215, 6015176, 6015176, 6015176, 6015176, 6015176, 0, 6015176, 6015176, 6015176, 6015176, 6015176,
6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176,
6015176, 8145949, 8145949, 8145949, 8145949, 8145949, 15267831, 16645886, 16777215, 16777215, 6015176, 6015176,
6015176, 6015176, 0, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176,
6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 0, 0, 8145949, 8145949, 8145949, 8145949, 16777215,
16777215, 16777215, 16777215, 6015176, 6015176, 6015176, 6015176, 0, 6015176, 6015176, 6015176, 6015176, 6015176,
6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 0, 0,
8145949, 8145949, 8145949, 8145949, 6868429, 7786961, 7327695, 6015176, 6015176, 6015176, 6015176, 6015176, 0,
6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176,
6015176, 6015176, 6015176, 6015176, 6015176, 0, 0, 8145949, 8145949, 8145949, 8145949, 16777215, 16777215,
16777215, 16777215, 6015176, 6015176, 6015176, 6015176, 0, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176,
6015176, 6015176, 6015176, 16777215, 16777215, 16777215, 16777215, 16777215, 6015176, 6015176, 6015176, 0, 0,
8145949, 8145949, 8145949, 8145949, 15530489, 16121084, 16777215, 16777215, 6015176, 6015176, 6015176, 6015176, 0,
6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 16777215, 16777215, 0, 0, 0, 0, 16777215,
6015176, 6015176, 6015176, 8145949, 8145949, 8145949, 8145949, 8145949, 16777215, 16777215, 16777215, 6015176,
6015176, 6015176, 6015176, 6015176, 0, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 16777215,
16777215, 0, 0, 0, 0, 0, 16777215, 16777215, 6015176, 6015176, 6015176, 8145949, 8145949, 8145949, 8145949,
6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 0, 6015176, 6015176, 6015176, 6015176,
6015176, 6015176, 16777215, 16777215, 16777215, 0, 0, 0, 0, 0, 16777215, 16777215, 6015176, 6015176, 6015176,
6015176, 8145949, 8145949, 8145949, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 0,
6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 16777215, 16777215, 16777215, 0, 0, 0, 0, 0, 16777215,
16777215, 16777215, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176,
6015176, 6015176, 6015176, 6015176, 0, 6015176, 6015176, 6015176, 6015176, 6015176, 16777215, 16777215, 16777215,
16777215, 16777215, 16777215, 0, 0, 16777215, 16777215, 16777215, 16777215, 6015176, 6015176, 6015176, 6015176,
6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 0, 6015176, 6015176,
6015176, 6015176, 6015176, 16777215, 16777215, 16777215, 16777215, 16777215, 16777215, 16777215, 16777215,
16777215, 16777215, 16777215, 16777215, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176,
6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 0, 0, 6015176, 6015176, 6015176, 6015176, 16777215, 16777215,
16777215, 16777215, 16777215, 16777215, 16777215, 16777215, 16777215, 16777215, 16777215, 16777215, 6015176,
6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176,
6015176, 0, 0, 6015176, 6015176, 6015176, 6015176, 16777215, 16777215, 16777215, 16777215, 16777215, 16777215,
16777215, 16777215, 16777215, 16777215, 16777215, 16777215, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176,
6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 0, 0, 6015176, 6015176, 6015176, 6015176,
6015176, 16777215, 16777215, 16777215, 16777215, 16777215, 16777215, 16777215, 16777215, 16777215, 16777215,
6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176,
6015176, 6015176, 6015176, 0, 0, 0, 6015176, 6015176, 6015176, 6015176, 16777215, 16777215, 16777215, 16777215,
16777215, 16777215, 16777215, 16777215, 16777215, 16777215, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176,
6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 0, 0, 0, 6015176, 6015176,
6015176, 6015176, 6015176, 16777215, 16777215, 16777215, 16777215, 16777215, 16777215, 16777215, 16777215, 6015176,
6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176,
6015176, 6015176, 6015176, 0, 0, 0, 0, 6015176, 6015176, 6015176, 6015176, 6015176, 16777215, 16777215, 16777215,
16777215, 16777215, 16777215, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176,
6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 0, 0, 0, 0, 0, 6015176, 6015176, 6015176,
6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176,
6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 0, 0,
0, 0, 0, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176,
6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176,
6015176, 6015176, 6015176, 6015176, 0, 0, 0, 0, 0, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176,
6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176,
6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 0, 0, 0, 0, 0, 6015176, 6015176, 6015176,
6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176,
6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 0, 0,
0, 0, 0, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176,
6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176,
6015176, 6015176, 6015176, 6015176, 0, 0, 0, 0, 0, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176,
6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176,
6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 0, 0, 0, 0, 0, 6015176, 6015176, 6015176,
6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176,
6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 0, 0,
0, 0, 0, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 0, 6015176,
6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176,
6015176, 6015176, 6015176, 0, 0, 0, 0, 0, 0, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176,
6015176, 6015176, 0, 0, 0, 0, 0, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176,
6015176, 6015176, 6015176, 0, 0, 0, 0, 0, 0, 0, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 6015176, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6015176, 6015176, 6015176, 6015176,
6015176, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
}
func Int2Color(intColor uint32) (col color.RGBA) {
blue := uint8(intColor & 0xFF)
green := uint8((intColor >> 8) & 0xFF)
red := uint8((intColor >> 16) & 0xFF)
return color.RGBA{R: red, G: green, B: blue}
}
+103
View File
@@ -0,0 +1,103 @@
package main
import (
"machine"
"image/color"
"time"
"tinygo.org/x/drivers/examples/hub75/gopherimg"
"tinygo.org/x/drivers/hub75"
)
var display hub75.Device
func main() {
machine.SPI0.Configure(machine.SPIConfig{
Frequency: 8000000,
Mode: 0},
)
display = hub75.New(machine.SPI0, 11, 12, 6, 10, 18, 20)
display.Configure(hub75.Config{
Width: 64,
Height: 32,
RowPattern: 16,
ColorDepth: 6,
})
colors := []color.RGBA{
{255, 0, 0, 255},
{255, 255, 0, 255},
{0, 255, 0, 255},
{0, 255, 255, 255},
{0, 0, 255, 255},
{255, 0, 255, 255},
{255, 255, 255, 255},
}
display.ClearDisplay()
display.SetBrightness(100)
step := 0
then := time.Now()
size := int16(8)
x := int16(0)
y := int16(0)
dx := int16(1)
dy := int16(1)
c := 0
for {
if time.Since(then).Nanoseconds() > 800000000 {
then = time.Now()
step++
if step < 23 {
showRect(size, x*size, y*size, colors[c])
c = (c + 1) % 7
x += dx
y += dy
if x >= (64 / size) {
dx = -1
x += 2 * dx
}
if y >= (32 / size) {
dy = -1
y += 2 * dy
}
if x < 0 {
dx = 1
x += 2 * dx
}
if y < 0 {
dy = 1
y += 2 * dy
}
} else if step == 23 {
showGopher()
} else if step == 30 {
display.ClearDisplay()
step = 0
x = 0
y = 0
}
}
display.Display()
}
}
func showGopher() {
for i := int16(0); i < 64; i++ {
for j := int16(0); j < 32; j++ {
display.SetPixel(i, j, gopherimg.Int2Color(gopherimg.ImageArray[32*i+j]))
}
}
}
func showRect(size int16, x int16, y int16, c color.RGBA) {
for i := x; i < x+size; i++ {
for j := y; j < y+size; j++ {
display.SetPixel(i, j, c)
}
}
}
+32
View File
@@ -0,0 +1,32 @@
// Connects to a LIS3DH I2C accelerometer on the Adafruit Circuit Playground Express.
package main
import (
"machine"
"time"
"tinygo.org/x/drivers/lis3dh"
)
var i2c = machine.I2C1
func main() {
i2c.Configure(machine.I2CConfig{})
accel := lis3dh.New(i2c)
accel.Address = lis3dh.Address1 // address on the Circuit Playground Express
accel.Configure()
accel.SetRange(lis3dh.RANGE_2_G)
println(accel.Connected())
for {
x, y, z, _ := accel.ReadAcceleration()
println("X:", x, "Y:", y, "Z:", z)
rx, ry, rz := accel.ReadRawAcceleration()
println("X (raw):", rx, "Y (raw):", ry, "Z (raw):", rz)
time.Sleep(time.Millisecond * 100)
}
}
+26
View File
@@ -0,0 +1,26 @@
// Connects to an MAG3110 I2C magnetometer.
package main
import (
"machine"
"time"
"tinygo.org/x/drivers/mag3110"
)
func main() {
machine.I2C0.Configure(machine.I2CConfig{})
mag := mag3110.New(machine.I2C0)
mag.Configure()
for {
x, y, z := mag.ReadMagnetic()
println("Magnetic readings:", x, y, z)
c, _ := mag.ReadTemperature()
println("Temperature:", float32(c)/1000, "ºC")
time.Sleep(time.Millisecond * 100)
}
}
+59
View File
@@ -0,0 +1,59 @@
package main
import (
"image/color"
"math/rand"
"time"
"tinygo.org/x/drivers/microbitmatrix"
)
var display microbitmatrix.Device
func main() {
display = microbitmatrix.New()
display.Configure(microbitmatrix.Config{})
display.ClearDisplay()
x := int16(1)
y := int16(2)
deltaX := int16(1)
deltaY := int16(1)
then := time.Now()
c := color.RGBA{255, 255, 255, 255}
for {
if time.Since(then).Nanoseconds() > 80000000 {
then = time.Now()
pixel := display.GetPixel(x, y)
if pixel {
display.ClearDisplay()
x = 1 + int16(rand.Int31n(3))
y = 1 + int16(rand.Int31n(3))
deltaX = 1
deltaY = 1
if rand.Int31n(2) == 0 {
deltaX = -1
}
if rand.Int31n(2) == 0 {
deltaY = -1
}
}
display.SetPixel(x, y, c)
x += deltaX
y += deltaY
if x == 0 || x == 4 {
deltaX = -deltaX
}
if y == 0 || y == 4 {
deltaY = -deltaY
}
}
display.Display()
}
}
+22
View File
@@ -0,0 +1,22 @@
// Connects to an MMA8653 I2C accelerometer.
package main
import (
"machine"
"time"
"tinygo.org/x/drivers/mma8653"
)
func main() {
machine.I2C0.Configure(machine.I2CConfig{})
accel := mma8653.New(machine.I2C0)
accel.Configure(mma8653.DataRate200Hz, mma8653.Sensitivity2G)
for {
x, y, z, _ := accel.ReadAcceleration()
println(x, y, z)
time.Sleep(time.Millisecond * 100)
}
}
+22
View File
@@ -0,0 +1,22 @@
// Connects to an MPU6050 I2C accelerometer/gyroscope.
package main
import (
"machine"
"time"
"tinygo.org/x/drivers/mpu6050"
)
func main() {
machine.I2C0.Configure(machine.I2CConfig{})
accel := mpu6050.New(machine.I2C0)
accel.Configure()
for {
x, y, z := accel.ReadAcceleration()
println(x, y, z)
time.Sleep(time.Millisecond * 100)
}
}
+57
View File
@@ -0,0 +1,57 @@
package data
// example images
var Images = [2][]byte{
{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, 0x80, 0x80, 0x80, 0xc0, 0xc0, 0xc0, 0x60, 0x60, 0x60, 0x60, 0xe0, 0x60, 0x30, 0x18, 0x98,
0x70, 0x70, 0x60, 0x60, 0x60, 0xe0, 0x10, 0x08, 0x08, 0x04, 0x02, 0x02, 0xf2, 0xf2, 0x72, 0x72, 0x72, 0x04, 0x08, 0x10,
0x60, 0x80, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x20, 0x20,
0x20, 0x20, 0xe0, 0x18, 0x1c, 0x07, 0x07, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00,
0x00, 0x04, 0x03, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x87, 0x18, 0x60, 0x80, 0x00, 0x00, 0x00, 0x03, 0x03, 0x03, 0x03,
0x03, 0x00, 0x80, 0x40, 0x38, 0x07, 0x03, 0x07, 0x47, 0x47, 0x07, 0x9f, 0x87, 0xfc, 0x78, 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, 0x00, 0x01, 0x02, 0x02, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x07, 0x3f, 0x20, 0x30, 0x0f, 0x0b, 0x80, 0x40, 0x41, 0x41, 0x42,
0x42, 0x42, 0x42, 0x42, 0x42, 0x41, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xf9, 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, 0x00, 0x00, 0x00, 0x80, 0x40, 0x40, 0x7f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x40, 0x40, 0x40, 0x00, 0x00, 0xf8, 0x06, 0x01, 0x01,
0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x01, 0x00, 0x06, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x71,
0x60, 0xc0, 0x80, 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, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x1b, 0x20, 0x60, 0xc0, 0x80,
0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x05, 0x05, 0x84, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00,
0x03, 0x1c, 0x10, 0x10, 0x20, 0x40, 0x40, 0x40, 0x47, 0x47, 0x44, 0x44, 0x44, 0x20, 0x20, 0x98, 0x84, 0xc3, 0x60, 0x20,
0x1c, 0x1c, 0x1b, 0x1b, 0x18, 0x18, 0x1f, 0x07, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01, 0x01, 0x03, 0x03, 0x03, 0x07, 0x07, 0x0f, 0x3f, 0x3f, 0x3f, 0x1b, 0x03, 0x03, 0x01, 0x01, 0x01,
0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x03, 0x03, 0x01,
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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0xc0, 0xc0, 0x7e, 0x63, 0x63, 0xc1, 0x01, 0x81, 0x63, 0x23, 0x26, 0x3c,
0x20, 0x20, 0x20, 0xa0, 0x60, 0x10, 0x08, 0x08, 0x04, 0x02, 0xe2, 0xe2, 0xe2, 0x62, 0xe4, 0xec, 0x10, 0x20, 0x60, 0x80,
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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x40, 0x20,
0x20, 0x20, 0x20, 0xf8, 0x38, 0x06, 0x06, 0x03, 0x03, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x1c, 0x60, 0x80, 0x00, 0x00, 0x00, 0x01, 0x03, 0x03, 0x02, 0x01, 0x01,
0x00, 0x80, 0x60, 0x1f, 0x03, 0x06, 0x0c, 0x16, 0x16, 0x16, 0x06, 0x06, 0xf8, 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, 0x00,
0x00, 0x01, 0x03, 0x02, 0xfe, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x06, 0x3f, 0x21, 0x1f, 0x0a, 0x80, 0x40, 0x41, 0x42, 0x42, 0x22, 0x22,
0x22, 0x42, 0x42, 0x82, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0xfd, 0x01, 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, 0x00, 0x00, 0x00, 0x00, 0x80, 0x40, 0x7f, 0x7f, 0x7f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x0e, 0x1e, 0x61, 0x81, 0x01, 0x01, 0x82, 0x42, 0x24, 0x18, 0x00, 0xf8, 0x06, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0xc0, 0xe0, 0xe0, 0xe0, 0xc0, 0xc0, 0x01, 0x02, 0x9c, 0x60, 0x00, 0x00, 0x00, 0x2f, 0x2f, 0x30, 0x20, 0x20,
0xc0, 0x80, 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, 0x00, 0x03, 0x04, 0x08, 0x08, 0x08, 0x08, 0x38, 0x3f, 0xc0, 0x80, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02,
0x0c, 0x10, 0x20, 0x20, 0x40, 0x40, 0x43, 0x47, 0x47, 0x24, 0x23, 0x23, 0x18, 0x84, 0x83, 0xc0, 0x60, 0x30, 0x18, 0x18,
0x18, 0x19, 0x1a, 0x1a, 0x0f, 0x07, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x06, 0x06, 0x1c, 0x1c, 0x7e, 0x7e, 0x7e, 0x36, 0x06, 0x06, 0x02, 0x02, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 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},
}
+35
View File
@@ -0,0 +1,35 @@
package main
import (
"machine"
"time"
"tinygo.org/x/drivers/examples/pcd8544/setbuffer/data"
"tinygo.org/x/drivers/pcd8544"
)
func main() {
dcPin := machine.P3
dcPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
rstPin := machine.P4
rstPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
scePin := machine.P5
scePin.Configure(machine.PinConfig{Mode: machine.PinOutput})
machine.SPI0.Configure(machine.SPIConfig{})
lcd := pcd8544.New(machine.SPI0, dcPin, rstPin, scePin)
lcd.Configure(pcd8544.Config{})
i := 0
for {
err := lcd.SetBuffer(data.Images[i])
if err != nil {
println(err.Error())
}
lcd.Display()
i = (i + 1) % 2
time.Sleep(800 * time.Millisecond)
}
}
+49
View File
@@ -0,0 +1,49 @@
package main
import (
"image/color"
"machine"
"time"
"tinygo.org/x/drivers/pcd8544"
)
func main() {
dcPin := machine.P3
dcPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
rstPin := machine.P4
rstPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
scePin := machine.P5
scePin.Configure(machine.PinConfig{Mode: machine.PinOutput})
machine.SPI0.Configure(machine.SPIConfig{})
lcd := pcd8544.New(machine.SPI0, dcPin, rstPin, scePin)
lcd.Configure(pcd8544.Config{})
var x int16
var y int16
deltaX := int16(1)
deltaY := int16(1)
for {
pixel := lcd.GetPixel(x, y)
c := color.RGBA{255, 255, 255, 255}
if pixel {
c = color.RGBA{0, 0, 0, 255}
}
lcd.SetPixel(x, y, c)
lcd.Display()
x += deltaX
y += deltaY
if x == 0 || x == 83 {
deltaX = -deltaX
}
if y == 0 || y == 47 {
deltaY = -deltaY
}
time.Sleep(1 * time.Millisecond)
}
}
+23
View File
@@ -0,0 +1,23 @@
package main
import (
"fmt"
"machine"
"time"
"tinygo.org/x/drivers/sht3x"
)
func main() {
machine.I2C0.Configure(machine.I2CConfig{})
sensor := sht3x.New(machine.I2C0)
for {
temp, humidity, _ := sensor.ReadTemperatureHumidity()
t := fmt.Sprintf("%.2f", float32(temp)/1000)
h := fmt.Sprintf("%.2f", float32(humidity)/100)
println("Temperature:", t, "ºC")
println("Humidity", h, "%")
time.Sleep(2 * time.Second)
}
}
+51
View File
@@ -0,0 +1,51 @@
package i2c_128x32
import (
"machine"
"image/color"
"time"
"tinygo.org/x/drivers/ssd1306"
)
func main() {
machine.I2C0.Configure(machine.I2CConfig{
Frequency: machine.TWI_FREQ_400KHZ,
})
display := ssd1306.NewI2C(machine.I2C0)
display.Configure(ssd1306.Config{
Address: ssd1306.Address_128_32,
Width: 128,
Height: 32,
})
display.ClearDisplay()
x := int16(0)
y := int16(0)
deltaX := int16(1)
deltaY := int16(1)
for {
pixel := display.GetPixel(x, y)
c := color.RGBA{255, 255, 255, 255}
if pixel {
c = color.RGBA{0, 0, 0, 255}
}
display.SetPixel(x, y, c)
display.Display()
x += deltaX
y += deltaY
if x == 0 || x == 127 {
deltaX = -deltaX
}
if y == 0 || y == 31 {
deltaY = -deltaY
}
time.Sleep(1 * time.Millisecond)
}
}
+48
View File
@@ -0,0 +1,48 @@
package i2c_128x64
import (
"image/color"
"machine"
"time"
"tinygo.org/x/drivers/ssd1306"
)
func main() {
machine.SPI0.Configure(machine.SPIConfig{
Frequency: 8000000,
})
display := ssd1306.NewSPI(machine.SPI0, machine.P8, machine.P7, machine.P9)
display.Configure(ssd1306.Config{
Width: 128,
Height: 64,
})
display.ClearDisplay()
x := int16(64)
y := int16(32)
deltaX := int16(1)
deltaY := int16(1)
for {
pixel := display.GetPixel(x, y)
c := color.RGBA{255, 255, 255, 255}
if pixel {
c = color.RGBA{0, 0, 0, 255}
}
display.SetPixel(x, y, c)
display.Display()
x += deltaX
y += deltaY
if x == 0 || x == 127 {
deltaX = -deltaX
}
if y == 0 || y == 63 {
deltaY = -deltaY
}
time.Sleep(1 * time.Millisecond)
}
}
+26
View File
@@ -0,0 +1,26 @@
// This example uses the settings for the thermistor that is built in to the
// Adafruit Circuit Playground Express.
package main
import (
"machine"
"time"
"tinygo.org/x/drivers/thermistor"
)
const ADC_PIN = machine.TEMPSENSOR
func main() {
machine.InitADC()
sensor := thermistor.New(ADC_PIN)
sensor.Configure()
for {
temp, _ := sensor.ReadTemperature()
println("Temperature:", temp/1000, "ºC")
time.Sleep(2 * time.Second)
}
}
+34
View File
@@ -0,0 +1,34 @@
package main
import (
"machine"
"time"
"tinygo.org/x/drivers/vl53l1x"
)
func main() {
machine.I2C0.Configure(machine.I2CConfig{
Frequency: 400000,
})
sensor := vl53l1x.New(machine.I2C0)
connected := sensor.Connected()
if !connected {
println("VL53L1X device not found")
return
}
println("VL53L1X device found")
sensor.Configure(true)
sensor.SetMeasurementTimingBudget(50000)
sensor.StartContinuous(50)
for {
sensor.Read(true)
println("Distance (mm):", sensor.Distance())
println("Status:", sensor.Status())
println("Peak signal rate (cps):", sensor.SignalRate())
println("Ambient rate (cps):", sensor.AmbientRate())
println("---")
time.Sleep(100 * time.Millisecond)
}
}
+77
View File
@@ -0,0 +1,77 @@
package main
import (
"machine"
"image/color"
"time"
"tinygo.org/x/drivers/waveshare-epd/epd2in13"
)
var display epd2in13.Device
func main() {
machine.SPI0.Configure(machine.SPIConfig{
Frequency: 8000000,
Mode: 0,
})
display = epd2in13.New(machine.SPI0, machine.P6, machine.P7, machine.P8, machine.P9)
display.Configure(epd2in13.Config{})
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 < 16; i++ {
for j := int16(0); j < 25; j++ {
if (i+j)%2 == 0 {
showRect(i*8, j*10, 8, 10, 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)
}
}
}
// There are two memory areas in the display, once the display is refreshed, memory areas are auto-toggled.
// DisplayRect needs to be called twice
display.DisplayRect(40, 83, 48, 83)
display.WaitUntilIdle()
display.DisplayRect(40, 83, 48, 83)
display.WaitUntilIdle()
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)
}
}
}
+46
View File
@@ -0,0 +1,46 @@
package main
import (
"machine"
"image/color"
"tinygo.org/x/drivers/waveshare-epd/epd2in13x"
)
var display epd2in13x.Device
func main() {
machine.SPI0.Configure(machine.SPIConfig{
Frequency: 8000000,
Mode: 0,
})
display = epd2in13x.New(machine.SPI0, machine.P6, machine.P7, machine.P8, machine.P9)
display.Configure(epd2in13x.Config{})
white := color.RGBA{0, 0, 0, 255}
colored := color.RGBA{255, 0, 0, 255}
black := color.RGBA{1, 1, 1, 255}
display.ClearBuffer()
display.ClearDisplay()
// Show a checkered board
for i := int16(0); i < 27; i++ {
showRect((i%3)*35, i*8, 35, 8, colored)
showRect(((i+1)%3)*35, i*8, 35, 8, black)
showRect(((i+2)%3)*35, i*8, 35, 8, white)
}
display.Display()
display.WaitUntilIdle()
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)
}
}
}
+39
View File
@@ -0,0 +1,39 @@
// Connects to an WS2812 RGB LED strip with 10 LEDS, such as
// on an Adafruit Circuit Playground Express board.
//
// Replace machine.NEOPIXELS in the code below to match the pin
// that you are using, if you have a different board.
package main
import (
"image/color"
"machine"
"time"
"tinygo.org/x/drivers/ws2812"
)
func main() {
neo := machine.NEOPIXELS
neo.Configure(machine.PinConfig{Mode: machine.PinOutput})
ws := ws2812.New(neo)
leds := make([]color.RGBA, 10)
rg := false
for {
rg = !rg
for i := range leds {
rg = !rg
if rg {
// Alpha channel is not supported by WS2812 so we leave it out
leds[i] = color.RGBA{R: 0xff, G: 0x00, B: 0x00}
} else {
leds[i] = color.RGBA{R: 0x00, G: 0xff, B: 0x00}
}
}
ws.WriteColors(leds)
time.Sleep(100 * time.Millisecond)
}
}
+132
View File
@@ -0,0 +1,132 @@
// Package gps provides a driver for GPS receivers over UART and I2C
package gps // import "tinygo.org/x/drivers/gps"
import (
"encoding/hex"
"machine"
"strings"
"time"
)
// Device wraps a connection to a GPS device.
type GPSDevice struct {
buffer []byte
bufIdx int
sentence strings.Builder
uart *machine.UART
bus *machine.I2C
address uint16
}
// NewUART creates a new UART GPS connection. The UART must already be configured.
func NewUART(uart *machine.UART) GPSDevice {
return GPSDevice{
uart: uart,
buffer: make([]byte, bufferSize),
bufIdx: bufferSize,
sentence: strings.Builder{},
}
}
// NewI2C creates a new I2C GPS connection.
func NewI2C(bus *machine.I2C) GPSDevice {
return GPSDevice{
bus: bus,
address: I2C_ADDRESS,
buffer: make([]byte, bufferSize),
bufIdx: bufferSize,
sentence: strings.Builder{},
}
}
// ReadNextSentence returns the next valid NMEA sentence from the GPS device.
func (gps *GPSDevice) NextSentence() (sentence string) {
sentence = gps.readNextSentence()
for !validSentence(sentence) {
sentence = gps.readNextSentence()
}
return sentence
}
// readNextSentence returns the next sentence from the GPS device.
func (gps *GPSDevice) readNextSentence() (sentence string) {
gps.sentence.Reset()
var b byte = ' '
for b != '$' {
b = gps.readNextByte()
}
for b != '*' {
gps.sentence.WriteByte(b)
b = gps.readNextByte()
}
gps.sentence.WriteByte(b)
gps.sentence.WriteByte(gps.readNextByte())
gps.sentence.WriteByte(gps.readNextByte())
sentence = gps.sentence.String()
return sentence
}
func (gps *GPSDevice) readNextByte() (b byte) {
gps.bufIdx += 1
if gps.bufIdx >= bufferSize {
gps.fillBuffer()
}
return gps.buffer[gps.bufIdx]
}
func (gps *GPSDevice) fillBuffer() {
if gps.uart != nil {
gps.uartFillBuffer()
} else {
gps.i2cFillBuffer()
}
}
func (gps *GPSDevice) uartFillBuffer() {
for gps.uart.Buffered() < bufferSize {
time.Sleep(100 * time.Millisecond)
}
gps.uart.Read(gps.buffer[0:bufferSize])
gps.bufIdx = 0
}
func (gps *GPSDevice) i2cFillBuffer() {
for gps.available() < bufferSize {
time.Sleep(100 * time.Millisecond)
}
gps.bus.Tx(gps.address, []byte{DATA_STREAM_REG}, gps.buffer[0:bufferSize])
gps.bufIdx = 0
}
// Available returns how many bytes of GPS data are currently available.
func (gps *GPSDevice) available() (available int) {
var lengthBytes [2]byte
gps.bus.Tx(gps.address, []byte{BYTES_AVAIL_REG}, lengthBytes[0:2])
available = int(lengthBytes[0])*256 + int(lengthBytes[1])
return available
}
// WriteBytes sends data/commands to the GPS device
func (gps *GPSDevice) WriteBytes(bytes []byte) {
if gps.uart != nil {
gps.uart.Write(bytes)
} else {
gps.bus.Tx(gps.address, []byte{}, bytes)
}
}
// validSentence checks if a sentence has been received uncorrupted
func validSentence(sentence string) bool {
if len(sentence) < 4 || sentence[0] != '$' || sentence[len(sentence)-3] != '*' {
return false
}
var cs byte = 0
for i := 1; i < len(sentence)-3; i++ {
cs ^= sentence[i]
}
checksum := hex.EncodeToString([]byte{cs})
return (checksum[0] == sentence[len(sentence)-2]) && (checksum[1] == sentence[len(sentence)-1])
}
+124
View File
@@ -0,0 +1,124 @@
package gps
import (
"strconv"
"strings"
"time"
)
type GPSParser struct {
gpsDevice GPSDevice
}
// fix is a GPS location fix
type Fix struct {
Valid bool
Time time.Time
Latitude float32
Longitude float32
Altitude int32
Satellites int16
}
func Parser(gpsDevice GPSDevice) GPSParser {
return GPSParser{
gpsDevice: gpsDevice,
}
}
// NextFix returns the next GPS location Fix from the GPS device
func (parser *GPSParser) NextFix() (fix Fix) {
var ggaSentence = nextGGA(parser.gpsDevice)
var ggaFields = strings.Split(ggaSentence, ",")
fix.Altitude = findAltitude(ggaFields)
fix.Satellites = findSatellites(ggaFields)
fix.Longitude = findLongitude(ggaFields)
fix.Latitude = findLatitude(ggaFields)
fix.Time = findTime(ggaFields)
fix.Valid = (fix.Altitude != -99999) && (fix.Satellites > 0)
return fix
}
// nextGGA returns the next GGA type sentence from the GPS device
// $--GGA,,,,,,,,,,,,,,*hh
func nextGGA(gpsDevice GPSDevice) (sentence string) {
for {
sentence = gpsDevice.NextSentence()
if sentence[3:6] == "GGA" {
return sentence
}
}
}
// findTime returns the time from a GGA sentence:
// $--GGA,hhmmss.ss,,,,,,,,,,,,,*xx
func findTime(ggaFields []string) time.Time {
if len(ggaFields) < 1 || len(ggaFields[1]) < 6 {
return time.Time{}
}
ts := strings.Builder{}
ts.WriteString(ggaFields[1][0:2])
ts.WriteString(":")
ts.WriteString(ggaFields[1][2:4])
ts.WriteString(":")
ts.WriteString(ggaFields[1][4:6])
var t, _ = time.Parse("15:04:05", ts.String())
return t
}
// findAltitude returns the altitude from a GGA sentence:
// $--GGA,,,,,,,,,25.8,,,,,*63
func findAltitude(ggaFields []string) int32 {
if len(ggaFields) > 8 && len(ggaFields[9]) > 0 {
var v, _ = strconv.ParseFloat(ggaFields[9], 32)
return int32(v)
}
return -99999
}
// findLatitude returns the Latitude from a GGA sentence:
// $--GGA,,ddmm.mmmmm,x,,,,,,,,,,,*hh
func findLatitude(ggaFields []string) float32 {
if len(ggaFields) > 2 && len(ggaFields[2]) > 8 {
var dd = ggaFields[2][0:2]
var mm = ggaFields[2][2:]
var d, _ = strconv.ParseFloat(dd, 32)
var m, _ = strconv.ParseFloat(mm, 32)
var v = float32(d + (m / 60))
if ggaFields[3] == "S" {
v *= -1
}
return v
}
return 0.0
}
// findLatitude returns the longitude from a GGA sentence:
// $--GGA,,,,dddmm.mmmmm,x,,,,,,,,,*hh
func findLongitude(ggaFields []string) float32 {
if len(ggaFields) > 4 && len(ggaFields[4]) > 8 {
var ddd = ggaFields[4][0:3]
var mm = ggaFields[4][3:]
var d, _ = strconv.ParseFloat(ddd, 32)
var m, _ = strconv.ParseFloat(mm, 32)
var v = float32(d + (m / 60))
if ggaFields[5] == "W" {
v *= -1
}
return v
}
return 0.0
}
// findSatellites returns the satellites from a GGA sentence:
// $--GGA,,,,,,,nn,,,,,,,*hh
func findSatellites(ggaFields []string) (n int16) {
if len(ggaFields) > 6 && len(ggaFields[7]) > 0 {
var nn = ggaFields[7]
var v, _ = strconv.ParseInt(nn, 10, 32)
n = int16(v)
return n
}
return 0
}
+17
View File
@@ -0,0 +1,17 @@
package gps
// Constants/addresses used for u-blox I2C.
// The I2C address which this device listens to.
const (
I2C_ADDRESS = 0x42
)
const (
BYTES_AVAIL_REG = 0xfd
DATA_STREAM_REG = 0xff
)
const (
bufferSize = 32
)
+53
View File
@@ -0,0 +1,53 @@
package gps
import (
"errors"
"time"
)
// flight mode disables the GPS COCOM limits
var flight_mode_cmd = [...]byte{
0xB5, 0x62, 0x06, 0x24, 0x24, 0x00, 0xFF, 0xFF, 0x06, 0x03, 0x00, 0x00, 0x00,
0x00, 0x10, 0x27, 0x00, 0x00, 0x05, 0x00, 0xFA, 0x00, 0xFA, 0x00, 0x64, 0x00,
0x2C, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x16, 0xDC}
// Sets CFG-GNSS to disable everything other than GPS GNSS
// solution. Failure to do this means GPS power saving
// doesn't work. Not needed for MAX7, needed for MAX8's
var cfg_gnss_cmd = [...]byte{
0xB5, 0x62, 0x06, 0x3E, 0x2C, 0x00, 0x00, 0x00,
0x20, 0x05, 0x00, 0x08, 0x10, 0x00, 0x01, 0x00,
0x01, 0x01, 0x01, 0x01, 0x03, 0x00, 0x00, 0x00,
0x01, 0x01, 0x03, 0x08, 0x10, 0x00, 0x00, 0x00,
0x01, 0x01, 0x05, 0x00, 0x03, 0x00, 0x00, 0x00,
0x01, 0x01, 0x06, 0x08, 0x0E, 0x00, 0x00, 0x00,
0x01, 0x01, 0xFC, 0x11}
func FlightMode(gpsDevice GPSDevice) (err error) {
err = sendCommand(gpsDevice, flight_mode_cmd[:])
return err
}
func SetCfgGNSS(gpsDevice GPSDevice) (err error) {
err = sendCommand(gpsDevice, cfg_gnss_cmd[:])
return err
}
func sendCommand(gpsDevice GPSDevice, command []byte) (err error) {
gpsDevice.WriteBytes(command)
start := time.Now()
for time.Now().Sub(start) < 1000 {
if gpsDevice.readNextByte() == '\n' {
if gpsDevice.readNextByte() == 0xB5 {
gpsDevice.readNextByte()
if gpsDevice.readNextByte() == 0x05 {
if gpsDevice.readNextByte() == 0x01 {
return
}
}
}
}
}
return errors.New("No ACK to GPS command")
}
+147
View File
@@ -0,0 +1,147 @@
package hd44780
import (
"errors"
"machine"
)
type GPIO struct {
dataPins []machine.Pin
en machine.Pin
rw machine.Pin
rs machine.Pin
write func(data byte)
read func() byte
}
func newGPIO(dataPins []machine.Pin, en, rs, rw machine.Pin, mode byte) Device {
pins := make([]machine.Pin, len(dataPins))
for i := 0; i < len(dataPins); i++ {
dataPins[i].Configure(machine.PinConfig{Mode: machine.PinOutput})
pins[i] = dataPins[i]
}
en.Configure(machine.PinConfig{Mode: machine.PinOutput})
rs.Configure(machine.PinConfig{Mode: machine.PinOutput})
rw.Configure(machine.PinConfig{Mode: machine.PinOutput})
rw.Low()
gpio := GPIO{
dataPins: pins,
en: en,
rs: rs,
rw: rw,
}
if mode == DATA_LENGTH_4BIT {
gpio.write = gpio.write4BitMode
gpio.read = gpio.read4BitMode
} else {
gpio.write = gpio.write8BitMode
gpio.read = gpio.read8BitMode
}
return Device{
bus: &gpio,
datalength: mode,
}
}
// SetCommandMode sets command/instruction mode
func (g *GPIO) SetCommandMode(set bool) {
if set {
g.rs.Low()
} else {
g.rs.High()
}
}
// Write writes len(data) bytes from data to display driver
func (g *GPIO) Write(data []byte) (n int, err error) {
g.rw.Low()
for _, d := range data {
g.write(d)
n++
}
return n, nil
}
func (g *GPIO) write8BitMode(data byte) {
g.en.High()
g.setPins(data)
g.en.Low()
}
func (g *GPIO) write4BitMode(data byte) {
g.en.High()
g.setPins(data >> 4)
g.en.Low()
g.en.High()
g.setPins(data)
g.en.Low()
}
// Read reads len(data) bytes from display RAM to data starting from RAM address counter position
// Ram address can be changed by writing address in command mode
func (g *GPIO) Read(data []byte) (n int, err error) {
if len(data) == 0 {
return 0, errors.New("Length greater than 0 is required")
}
g.rw.High()
g.reconfigureGPIOMode(machine.PinInput)
for i := 0; i < len(data); i++ {
data[i] = g.read()
n++
}
g.reconfigureGPIOMode(machine.PinInput)
return n, nil
}
func (g *GPIO) read4BitMode() byte {
g.en.High()
data := (g.pins() << 4 & 0xF0)
g.en.Low()
g.en.High()
data |= (g.pins() & 0x0F)
g.en.Low()
return data
}
func (g *GPIO) read8BitMode() byte {
g.en.High()
data := g.pins()
g.en.Low()
return data
}
func (g *GPIO) reconfigureGPIOMode(mode machine.PinMode) {
for i := 0; i < len(g.dataPins); i++ {
g.dataPins[i].Configure(machine.PinConfig{Mode: mode})
}
}
// setPins sets high or low state on all data pins depending on data
func (g *GPIO) setPins(data byte) {
mask := byte(1)
for i := 0; i < len(g.dataPins); i++ {
if (data & mask) != 0 {
g.dataPins[i].High()
} else {
g.dataPins[i].Low()
}
mask = mask << 1
}
}
// pins returns current state of data pins. MSB is D7
func (g *GPIO) pins() byte {
bits := byte(0)
for i := uint8(0); i < uint8(len(g.dataPins)); i++ {
if g.dataPins[i].Get() {
bits |= (1 << i)
}
}
return bits
}
+231
View File
@@ -0,0 +1,231 @@
// Package lis3dh provides a driver for the HD44780 LCD controller.
//
// Datasheet: https://www.sparkfun.com/datasheets/LCD/HD44780.pdf
//
package hd44780 // import "tinygo.org/x/drivers/hd44780"
import (
"errors"
"io"
"machine"
"time"
)
type Buser interface {
io.ReadWriter
SetCommandMode(set bool)
}
type Device struct {
bus Buser
width uint8
height uint8
buffer []uint8
bufferLength uint8
rowOffset []uint8 // Row offsets in DDRAM
datalength uint8
cursor cursor
busyStatus []byte
}
type cursor struct {
x, y uint8
}
type Config struct {
Width int16
Height int16
CursorBlink bool
CursorOnOff bool
Font uint8
}
// NewGPIO4Bit returns 4bit data length HD44780 driver. Datapins are LCD DB pins starting from DB4 to DB7
func NewGPIO4Bit(dataPins []machine.Pin, e, rs, rw machine.Pin) (Device, error) {
const fourBitMode = 4
if len(dataPins) != fourBitMode {
return Device{}, errors.New("4 pins are required in data slice (D4-D7) when HD44780 is used in 4 bit mode")
}
return newGPIO(dataPins, e, rs, rw, DATA_LENGTH_4BIT), nil
}
// NewGPIO8Bit returns 8bit data length HD44780 driver. Datapins are LCD DB pins starting from DB0 to DB7
func NewGPIO8Bit(dataPins []machine.Pin, e, rs, rw machine.Pin) (Device, error) {
const eightBitMode = 8
if len(dataPins) != eightBitMode {
return Device{}, errors.New("8 pins are required in data slice (D0-D7) when HD44780 is used in 8 bit mode")
}
return newGPIO(dataPins, e, rs, rw, DATA_LENGTH_8BIT), nil
}
// Configure initializes device
func (d *Device) Configure(cfg Config) error {
d.busyStatus = make([]byte, 1)
d.width = uint8(cfg.Width)
d.height = uint8(cfg.Height)
if d.width == 0 || d.height == 0 {
return errors.New("Width and height must be set")
}
memoryMap := uint8(ONE_LINE)
if d.height > 1 {
memoryMap = TWO_LINE
}
d.setRowOffsets()
d.ClearBuffer()
cursor := CURSOR_OFF
if cfg.CursorOnOff {
cursor = CURSOR_ON
}
cursorBlink := CURSOR_BLINK_OFF
if cfg.CursorBlink {
cursorBlink = CURSOR_BLINK_ON
}
if !(cfg.Font == FONT_5X8 || cfg.Font == FONT_5X10) {
cfg.Font = FONT_5X8
}
//Wait 15ms after Vcc rises to 4.5V
time.Sleep(15 * time.Millisecond)
d.bus.SetCommandMode(true)
d.bus.Write([]byte{DATA_LENGTH_8BIT})
time.Sleep(5 * time.Millisecond)
for i := 0; i < 2; i++ {
d.bus.Write([]byte{DATA_LENGTH_8BIT})
time.Sleep(150 * time.Microsecond)
}
if d.datalength == DATA_LENGTH_4BIT {
d.bus.Write([]byte{DATA_LENGTH_4BIT >> 4})
}
// Busy flag is now accessible
d.SendCommand(memoryMap | cfg.Font | d.datalength)
d.SendCommand(DISPLAY_OFF)
d.SendCommand(DISPLAY_CLEAR)
d.SendCommand(ENTRY_MODE | CURSOR_INCREASE | DISPLAY_NO_SHIFT)
d.SendCommand(DISPLAY_ON | uint8(cursor) | uint8(cursorBlink))
return nil
}
// Write writes data to internal buffer
func (d *Device) Write(data []byte) (n int, err error) {
size := len(data)
if size > len(d.buffer) {
size = len(d.buffer)
}
d.bufferLength = uint8(size)
for i := uint8(0); i < d.bufferLength; i++ {
d.buffer[i] = data[i]
}
return size, nil
}
// Display sends the whole buffer to the screen at cursor position
func (d *Device) Display() error {
// Buffer may contain less characters than its capacity.
// We must be sure that we will not send unassigned characters
// That would result in sending zero values of buffer slice and
// potentialy displaying some character.
var totalDisplayedChars uint8
var bufferPos uint8
for ; d.cursor.y < d.height; d.cursor.y++ {
d.SetCursor(d.cursor.x, d.cursor.y)
for ; d.cursor.x < d.width && totalDisplayedChars < d.bufferLength; d.cursor.x++ {
d.sendData(d.buffer[bufferPos])
bufferPos++
totalDisplayedChars++
}
if d.cursor.x >= d.width {
d.cursor.x = 0
}
if totalDisplayedChars >= d.bufferLength {
break
}
}
return nil
}
// SetCursor moves cursor to position x,y, where (0,0) is top left corner and (width-1, height-1) bottom right
func (d *Device) SetCursor(x, y uint8) {
d.cursor.x = x
d.cursor.y = y
d.SendCommand(DDRAM_SET | (x + (d.rowOffset[y] * y)))
}
// SetRowOffsets sets initial memory addresses coresponding to the display rows
// Each row on display has different starting address in DDRAM. Rows are not mapped in order.
// These addresses tend to differ between the types of the displays (16x2, 16x4, 20x4 etc ..),
// https://web.archive.org/web/20111122175541/http://web.alfredstate.edu/weimandn/lcd/lcd_addressing/lcd_addressing_index.html
func (d *Device) setRowOffsets() {
switch d.height {
case 1:
d.rowOffset = []uint8{}
case 2:
d.rowOffset = []uint8{0x0, 0x40, 0x0, 0x40}
case 4:
d.rowOffset = []uint8{0x0, 0x40, d.width, 0x40 + d.width}
default:
d.rowOffset = []uint8{0x0, 0x40, d.width, 0x40 + d.width}
}
}
// SendCommand sends commands to driver
func (d *Device) SendCommand(command byte) {
d.bus.SetCommandMode(true)
d.bus.Write([]byte{command})
for d.Busy() {
}
}
// sendData sends byte data directly to display.
func (d *Device) sendData(data byte) {
d.bus.SetCommandMode(false)
d.bus.Write([]byte{data})
for d.Busy() {
}
}
// CreateCharacter crates characters using data and stores it under cgram Addr in CGRAM
func (d *Device) CreateCharacter(cgramAddr uint8, data []byte) {
d.SendCommand(CGRAM_SET | cgramAddr)
for _, dd := range data {
d.sendData(dd)
}
}
// Busy returns true when hd447890 is busy
func (d *Device) Busy() bool {
d.bus.SetCommandMode(true)
d.bus.Read(d.busyStatus)
return (d.busyStatus[0] & BUSY) > 0
}
// Size returns the current size of the display.
func (d *Device) Size() (w, h int16) {
return int16(d.width), int16(d.height)
}
// ClearDisplay clears displayed content and buffer
func (d *Device) ClearDisplay() {
d.SendCommand(DISPLAY_CLEAR)
d.ClearBuffer()
}
// ClearBuffer clears internal buffer
func (d *Device) ClearBuffer() {
d.buffer = make([]uint8, d.width*d.height)
}
+38
View File
@@ -0,0 +1,38 @@
package hd44780
const (
DISPLAY_CLEAR = 0x1
CURSOR_HOME = 0x2
ENTRY_MODE = 0x4
CURSOR_DECREASE = ENTRY_MODE | 0x0
CURSOR_INCREASE = ENTRY_MODE | 0x2
DISPLAY_SHIFT = ENTRY_MODE | 0x1
DISPLAY_NO_SHIFT = ENTRY_MODE | 0x0
DISPLAY_ON_OFF = 0x8
DISPLAY_ON = DISPLAY_ON_OFF | 0x4
DISPLAY_OFF = DISPLAY_ON_OFF | 0x0
CURSOR_ON = DISPLAY_ON_OFF | 0x2
CURSOR_OFF = DISPLAY_ON_OFF | 0x0
CURSOR_BLINK_ON = DISPLAY_ON_OFF | 0x1
CURSOR_BLINK_OFF = DISPLAY_ON_OFF | 0x0
CURSOR_DISPLAY_SHIFT = 0x10
CURSOR_SHIFT_RIGHT = CURSOR_DISPLAY_SHIFT | 0x4
CURSOR_SHIFT_LEFT = CURSOR_DISPLAY_SHIFT | 0x0
DISPLAY_SHIFT_RIGHT = CURSOR_DISPLAY_SHIFT | 0xC
DISPLAY_SHIFT_LEFT = CURSOR_DISPLAY_SHIFT | 0x8
FUNCTION_MODE = 0x20
DATA_LENGTH_8BIT = FUNCTION_MODE | 0x10
DATA_LENGTH_4BIT = FUNCTION_MODE | 0x0
TWO_LINE = FUNCTION_MODE | 0x8
ONE_LINE = FUNCTION_MODE | 0x0
FONT_5X10 = FUNCTION_MODE | 0x4
FONT_5X8 = FUNCTION_MODE | 0x0
BUSY = 0x80
CGRAM_SET = 0x40
DDRAM_SET = 0x80
)
+269
View File
@@ -0,0 +1,269 @@
// Package hub75 implements a driver for the HUB75 LED matrix.
//
// Guide: https://cdn-learn.adafruit.com/downloads/pdf/32x16-32x32-rgb-led-matrix.pdf
// This driver was inspired by https://github.com/2dom/PxMatrix
//
package hub75 // import "tinygo.org/x/drivers/hub75"
import (
"image/color"
"machine"
"time"
)
type Config struct {
Width int16
Height int16
ColorDepth uint16
RowPattern int16
Brightness uint8
FastUpdate bool
}
type Device struct {
bus machine.SPI
a machine.Pin
b machine.Pin
c machine.Pin
d machine.Pin
oe machine.Pin
lat machine.Pin
width int16
height int16
brightness uint8
fastUpdate bool
colorDepth uint16
colorStep uint16
colorHalfStep uint16
colorThirdStep uint16
colorTwoThirdStep uint16
rowPattern int16
rowsPerBuffer int16
panelWidth int16
panelWidthBytes int16
pixelCounter uint32
lineCounter uint32
patternColorBytes uint8
rowSetsPerBuffer uint8
sendBufferSize uint16
rowOffset []uint32
buffer [][]uint8 // [ColorDepth][(width * height * 3(rgb)) / 8]uint8
displayColor uint16
}
// New returns a new HUB75 driver. Pass in a fully configured SPI bus.
func New(b machine.SPI, latPin, oePin, aPin, bPin, cPin, dPin machine.Pin) Device {
aPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
bPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
cPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
dPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
oePin.Configure(machine.PinConfig{Mode: machine.PinOutput})
latPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
return Device{
bus: b,
a: aPin,
b: bPin,
c: cPin,
d: dPin,
oe: oePin,
lat: latPin,
}
}
// Configure sets up the device.
func (d *Device) Configure(cfg Config) {
if cfg.Width != 0 {
d.width = cfg.Width
} else {
d.width = 64
}
if cfg.Height != 0 {
d.height = cfg.Height
} else {
d.height = 32
}
if cfg.ColorDepth != 0 {
d.colorDepth = cfg.ColorDepth
} else {
d.colorDepth = 8
}
if cfg.RowPattern != 0 {
d.rowPattern = cfg.RowPattern
} else {
d.rowPattern = 16
}
if cfg.Brightness != 0 {
d.brightness = cfg.Brightness
} else {
d.brightness = 255
}
d.fastUpdate = cfg.FastUpdate
d.rowsPerBuffer = d.height / 2
d.panelWidth = 1
d.panelWidthBytes = (d.width / d.panelWidth) / 8
d.rowOffset = make([]uint32, d.height)
d.patternColorBytes = uint8((d.height / d.rowPattern) * (d.width / 8))
d.rowSetsPerBuffer = uint8(d.rowsPerBuffer / d.rowPattern)
d.sendBufferSize = uint16(d.patternColorBytes) * 3
d.colorStep = 256 / d.colorDepth
d.colorHalfStep = d.colorStep / 2
d.colorThirdStep = d.colorStep / 3
d.colorTwoThirdStep = 2 * d.colorThirdStep
d.buffer = make([][]uint8, d.colorDepth)
for i := range d.buffer {
d.buffer[i] = make([]uint8, (d.width*d.height*3)/8)
}
d.colorHalfStep = d.colorStep / 2
d.colorThirdStep = d.colorStep / 3
d.colorTwoThirdStep = 2 * d.colorThirdStep
d.a.Low()
d.b.Low()
d.c.Low()
d.d.Low()
d.oe.High()
var i uint32
for i = 0; i < uint32(d.height); i++ {
d.rowOffset[i] = (i%uint32(d.rowPattern))*uint32(d.sendBufferSize) + uint32(d.sendBufferSize) - 1
}
}
// SetPixel modifies the internal buffer in a single pixel.
func (d *Device) SetPixel(x int16, y int16, c color.RGBA) {
d.fillMatrixBuffer(x, y, c.R, c.G, c.B)
}
// fillMatrixBuffer modifies a pixel in the internal buffer given position and RGB values
func (d *Device) fillMatrixBuffer(x int16, y int16, r uint8, g uint8, b uint8) {
if x < 0 || x >= d.width || y < 0 || y >= d.height {
return
}
x = d.width - 1 - x
var offsetR uint32
var offsetG uint32
var offsetB uint32
vertIndexInBuffer := uint8((int32(y) % int32(d.rowsPerBuffer)) / int32(d.rowPattern))
whichBuffer := uint8(y / d.rowsPerBuffer)
xByte := x / 8
whichPanel := uint8(xByte / d.panelWidthBytes)
inRowByteOffset := uint8(xByte % d.panelWidthBytes)
offsetR = d.rowOffset[y] - uint32(inRowByteOffset) - uint32(d.panelWidthBytes)*
(uint32(d.rowSetsPerBuffer)*(uint32(d.panelWidth)*uint32(whichBuffer)+uint32(whichPanel))+uint32(vertIndexInBuffer))
offsetG = offsetR - uint32(d.patternColorBytes)
offsetB = offsetG - uint32(d.patternColorBytes)
bitSelect := uint8(x % 8)
for c := uint16(0); c < d.colorDepth; c++ {
colorTresh := uint8(c*d.colorStep + d.colorHalfStep)
if r > colorTresh {
d.buffer[c][offsetR] |= 1 << bitSelect
} else {
d.buffer[c][offsetR] = d.buffer[c][offsetR] &^ 1 << bitSelect
}
if g > colorTresh {
d.buffer[(c+d.colorThirdStep)%d.colorDepth][offsetG] |= 1 << bitSelect
} else {
d.buffer[(c+d.colorThirdStep)%d.colorDepth][offsetG] &^= 1 << bitSelect
}
if b > colorTresh {
d.buffer[(c+d.colorTwoThirdStep)%d.colorDepth][offsetB] |= 1 << bitSelect
} else {
d.buffer[(c+d.colorTwoThirdStep)%d.colorDepth][offsetB] &^= 1 << bitSelect
}
}
}
// Display sends the buffer (if any) to the screen.
func (d *Device) Display() error {
rp := uint16(d.rowPattern)
for i := uint16(0); i < rp; i++ {
// FAST UPDATES (only if brightness = 255)
if d.fastUpdate && d.brightness == 255 {
d.setMux((i + rp - 1) % rp)
d.lat.High()
d.oe.Low()
d.lat.Low()
time.Sleep(1 * time.Microsecond)
d.bus.Tx(d.buffer[d.displayColor][i*d.sendBufferSize:(i+1)*d.sendBufferSize], nil)
time.Sleep(10 * time.Microsecond)
d.oe.High()
} else { // NO FAST UPDATES
d.setMux(i)
d.bus.Tx(d.buffer[d.displayColor][i*d.sendBufferSize:(i+1)*d.sendBufferSize], nil)
d.latch((255 * uint16(d.brightness)) / 255)
}
}
d.displayColor++
if d.displayColor >= d.colorDepth {
d.displayColor = 0
}
return nil
}
func (d *Device) latch(showTime uint16) {
d.lat.High()
d.lat.Low()
d.oe.Low()
time.Sleep(time.Duration(showTime) * time.Microsecond)
d.oe.High()
}
func (d *Device) setMux(value uint16) {
if (value & 0x01) == 0x01 {
d.a.High()
} else {
d.a.Low()
}
if (value & 0x02) == 0x02 {
d.b.High()
} else {
d.b.Low()
}
if (value & 0x04) == 0x04 {
d.c.High()
} else {
d.c.Low()
}
if (value & 0x08) == 0x08 {
d.d.High()
} else {
d.d.Low()
}
}
// FlushDisplay flushes the display
func (d *Device) FlushDisplay() {
var i uint16
for i = 0; i < d.sendBufferSize; i++ {
d.bus.Tx([]byte{0x00}, nil)
}
}
// SetBrightness changes the brightness of the display
func (d *Device) SetBrightness(brightness uint8) {
d.brightness = brightness
}
// ClearDisplay erases the internal buffer
func (d *Device) ClearDisplay() {
bufferSize := (d.width * d.height * 3) / 8
for c := uint16(0); c < d.colorDepth; c++ {
for j := int16(0); j < bufferSize; j++ {
d.buffer[c][j] = 0
}
}
}
// Size returns the current size of the display.
func (d *Device) Size() (w, h int16) {
return d.width, d.height
}
-14
View File
@@ -1,14 +0,0 @@
package drivers
import (
"image/color"
)
// LEDArray is an array of RGB LEDs. It may have any shape, but in general it is
// a strip of daisy-chained LEDs.
type LEDArray interface {
// WriteColors updates all LEDs in the LED strip to the given RGB color. It
// depends on the protocol what happens when you do not provide a
// correctly-sized slice of colors.
WriteColors(buf []color.RGBA) error
}
+127
View File
@@ -0,0 +1,127 @@
// Package lis3dh provides a driver for the LIS3DH digital accelerometer.
//
// Datasheet: https://www.st.com/resource/en/datasheet/lis3dh.pdf
//
package lis3dh // import "tinygo.org/x/drivers/lis3dh"
import (
"machine"
)
// Device wraps an I2C connection to a LIS3DH device.
type Device struct {
bus machine.I2C
Address uint16
r Range
}
// New creates a new LIS3DH connection. The I2C bus must already be configured.
//
// This function only creates the Device object, it does not touch the device.
func New(bus machine.I2C) Device {
return Device{bus: bus, Address: Address0}
}
// Configure sets up the device for communication
func (d *Device) Configure() {
// enable all axes, normal mode
d.bus.WriteRegister(uint8(d.Address), REG_CTRL1, []byte{0x07})
// 400Hz rate
d.SetDataRate(DATARATE_400_HZ)
// High res & BDU enabled
d.bus.WriteRegister(uint8(d.Address), REG_CTRL4, []byte{0x88})
// get current range
d.r = d.ReadRange()
}
// Connected returns whether a LIS3DH has been found.
// It does a "who am I" request and checks the response.
func (d *Device) Connected() bool {
data := []byte{0}
err := d.bus.ReadRegister(uint8(d.Address), WHO_AM_I, data)
if err != nil {
return false
}
return data[0] == 0x33
}
// SetDataRate sets the speed of data collected by the LIS3DH.
func (d *Device) SetDataRate(rate DataRate) {
ctl1 := []byte{0}
err := d.bus.ReadRegister(uint8(d.Address), REG_CTRL1, ctl1)
if err != nil {
println(err.Error())
}
// mask off bits
ctl1[0] &^= 0xf0
ctl1[0] |= (byte(rate) << 4)
d.bus.WriteRegister(uint8(d.Address), REG_CTRL1, ctl1)
}
// SetRange sets the G range for LIS3DH.
func (d *Device) SetRange(r Range) {
ctl := []byte{0}
err := d.bus.ReadRegister(uint8(d.Address), REG_CTRL4, ctl)
if err != nil {
println(err.Error())
}
// mask off bits
ctl[0] &^= 0x30
ctl[0] |= (byte(r) << 4)
d.bus.WriteRegister(uint8(d.Address), REG_CTRL4, ctl)
// store the new range
d.r = r
}
// ReadRange returns the current G range for LIS3DH.
func (d *Device) ReadRange() (r Range) {
ctl := []byte{0}
err := d.bus.ReadRegister(uint8(d.Address), REG_CTRL4, ctl)
if err != nil {
println(err.Error())
}
// mask off bits
r = Range(ctl[0] >> 4)
r &= 0x03
return r
}
// 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() (int32, int32, int32, error) {
x, y, z := d.ReadRawAcceleration()
divider := float32(1)
switch d.r {
case RANGE_16_G:
divider = 1365
case RANGE_8_G:
divider = 4096
case RANGE_4_G:
divider = 8190
case RANGE_2_G:
divider = 16380
}
return int32(float32(x) / divider * 1000000), int32(float32(y) / divider * 1000000), int32(float32(z) / divider * 1000000), nil
}
// ReadRawAcceleration returns the raw x, y and z axis from the LIS3DH
func (d *Device) ReadRawAcceleration() (x int16, y int16, z int16) {
d.bus.WriteRegister(uint8(d.Address), REG_OUT_X_L|0x80, nil)
data := []byte{0, 0, 0, 0, 0, 0}
d.bus.Tx(d.Address, nil, data)
x = int16((uint16(data[1]) << 8) | uint16(data[0]))
y = int16((uint16(data[3]) << 8) | uint16(data[2]))
z = int16((uint16(data[5]) << 8) | uint16(data[4]))
return
}
+77
View File
@@ -0,0 +1,77 @@
package lis3dh
// Constants/addresses used for I2C.
// The I2C addresses which this device listens to.
const (
Address0 = 0x18 // SA0 is low
Address1 = 0x19 // SA0 is high
)
// Registers. Names, addresses and comments copied from the datasheet.
const (
WHO_AM_I = 0x0F
REG_STATUS1 = 0x07
REG_OUTADC1_L = 0x08
REG_OUTADC1_H = 0x09
REG_OUTADC2_L = 0x0A
REG_OUTADC2_H = 0x0B
REG_OUTADC3_L = 0x0C
REG_OUTADC3_H = 0x0D
REG_INTCOUNT = 0x0E
REG_WHOAMI = 0x0F
REG_TEMPCFG = 0x1F
REG_CTRL1 = 0x20
REG_CTRL2 = 0x21
REG_CTRL3 = 0x22
REG_CTRL4 = 0x23
REG_CTRL5 = 0x24
REG_CTRL6 = 0x25
REG_REFERENCE = 0x26
REG_STATUS2 = 0x27
REG_OUT_X_L = 0x28
REG_OUT_X_H = 0x29
REG_OUT_Y_L = 0x2A
REG_OUT_Y_H = 0x2B
REG_OUT_Z_L = 0x2C
REG_OUT_Z_H = 0x2D
REG_FIFOCTRL = 0x2E
REG_FIFOSRC = 0x2F
REG_INT1CFG = 0x30
REG_INT1SRC = 0x31
REG_INT1THS = 0x32
REG_INT1DUR = 0x33
REG_CLICKCFG = 0x38
REG_CLICKSRC = 0x39
REG_CLICKTHS = 0x3A
REG_TIMELIMIT = 0x3B
REG_TIMELATEN = 0x3C
REG_TIMEWINDO = 0x3D
REG_ACTTHS = 0x3E
REG_ACTDUR = 0x3F
)
type Range uint8
const (
RANGE_16_G Range = 3 // +/- 16g
RANGE_8_G = 2 // +/- 8g
RANGE_4_G = 1 // +/- 4g
RANGE_2_G = 0 // +/- 2g (default value)
)
type DataRate uint8
// Data rate constants.
const (
DATARATE_400_HZ DataRate = 7 // 400Hz
DATARATE_200_HZ = 6 // 200Hz
DATARATE_100_HZ = 5 // 100Hz
DATARATE_50_HZ = 4 // 50Hz
DATARATE_25_HZ = 3 // 25Hz
DATARATE_10_HZ = 2 // 10 Hz
DATARATE_1_HZ = 1 // 1 Hz
DATARATE_POWERDOWN = 0
DATARATE_LOWPOWER_1K6HZ = 8
DATARATE_LOWPOWER_5KHZ = 9
)
+14 -11
View File
@@ -2,7 +2,8 @@
// Freescale/NXP.
//
// Datasheet: https://www.nxp.com/docs/en/data-sheet/MAG3110.pdf
package mag3110
//
package mag3110 // import "tinygo.org/x/drivers/mag3110"
import (
"machine"
@@ -10,7 +11,8 @@ import (
// Device wraps an I2C connection to a MAG3110 device.
type Device struct {
bus machine.I2C
bus machine.I2C
Address uint16
}
// New creates a new MAG3110 connection. The I2C bus must already be
@@ -18,38 +20,39 @@ type Device struct {
//
// This function only creates the Device object, it does not touch the device.
func New(bus machine.I2C) Device {
return Device{bus}
return Device{bus, Address}
}
// Connected returns whether a MAG3110 has been found.
// It does a "who am I" request and checks the response.
func (d Device) Connected() bool {
data := []byte{0}
d.bus.ReadRegister(Address, WHO_AM_I, data)
d.bus.ReadRegister(uint8(d.Address), WHO_AM_I, data)
return data[0] == 0xC4
}
// Configure sets up the device for communication.
func (d Device) Configure() {
d.bus.WriteRegister(Address, CTRL_REG2, []uint8{0x80}) // Power down when not used
d.bus.WriteRegister(uint8(d.Address), CTRL_REG2, []uint8{0x80}) // Power down when not used
}
// ReadMagnetic reads the vectors of the magnetic field of the device and
// returns it.
func (d Device) ReadMagnetic() (x int16, y int16, z int16) {
d.bus.WriteRegister(Address, CTRL_REG1, []uint8{0x1a}) // Request a measurement
d.bus.WriteRegister(uint8(d.Address), CTRL_REG1, []uint8{0x1a}) // Request a measurement
data := make([]byte, 6)
d.bus.ReadRegister(Address, OUT_X_MSB, data)
d.bus.ReadRegister(uint8(d.Address), OUT_X_MSB, data)
x = int16((uint16(data[0]) << 8) | uint16(data[1]))
y = int16((uint16(data[2]) << 8) | uint16(data[3]))
z = int16((uint16(data[4]) << 8) | uint16(data[5]))
return
}
// ReadTemperature reads the current die temperature in degrees Celsius.
func (d Device) ReadTemperature() (temp int8) {
// ReadTemperature reads and returns the current die temperature in
// celsius milli degrees (ºC/1000).
func (d Device) ReadTemperature() (int32, error) {
data := make([]byte, 1)
d.bus.ReadRegister(Address, DIE_TEMP, data)
return int8(data[0])
d.bus.ReadRegister(uint8(d.Address), DIE_TEMP, data)
return int32(data[0]) * 1000, nil
}
+145
View File
@@ -0,0 +1,145 @@
// Package microbitmatrix implements a driver for the BBC micro:bit's LED matrix.
//
// Schematic: https://github.com/bbcmicrobit/hardware/blob/master/SCH_BBC-Microbit_V1.3B.pdf
//
package microbitmatrix // import "tinygo.org/x/drivers/microbitmatrix"
import (
"image/color"
"machine"
"time"
)
var matrixRotations = [4][5][5][2]uint8{
{ // 0
{{0, 0}, {1, 3}, {0, 1}, {1, 4}, {0, 2}},
{{2, 3}, {2, 4}, {2, 5}, {2, 6}, {2, 7}},
{{1, 1}, {0, 8}, {1, 2}, {2, 8}, {1, 0}},
{{0, 7}, {0, 6}, {0, 5}, {0, 4}, {0, 3}},
{{2, 2}, {1, 6}, {2, 0}, {1, 5}, {2, 1}},
},
{ // 90 CCW
{{0, 2}, {2, 7}, {1, 0}, {0, 3}, {2, 1}},
{{1, 4}, {2, 6}, {2, 8}, {0, 4}, {1, 5}},
{{0, 1}, {2, 5}, {1, 2}, {0, 5}, {2, 0}},
{{1, 3}, {2, 4}, {0, 8}, {0, 6}, {1, 6}},
{{0, 0}, {2, 3}, {1, 1}, {0, 7}, {2, 2}},
},
{ // 180
{{2, 1}, {1, 5}, {2, 0}, {1, 6}, {2, 2}},
{{0, 3}, {0, 4}, {0, 5}, {0, 6}, {0, 7}},
{{1, 0}, {2, 8}, {1, 2}, {0, 8}, {1, 1}},
{{2, 7}, {2, 6}, {2, 5}, {2, 4}, {2, 3}},
{{0, 2}, {1, 4}, {0, 1}, {1, 3}, {0, 0}},
},
{ // 270
{{2, 2}, {0, 7}, {1, 1}, {2, 3}, {0, 0}},
{{1, 6}, {0, 6}, {0, 8}, {2, 4}, {1, 3}},
{{2, 0}, {0, 5}, {1, 2}, {2, 5}, {0, 1}},
{{1, 5}, {0, 4}, {2, 8}, {2, 6}, {1, 4}},
{{2, 1}, {0, 3}, {1, 0}, {2, 7}, {0, 2}},
},
}
type Config struct {
Rotation uint8
}
type Device struct {
pin [12]machine.Pin
buffer [3][9]bool
rotation uint8
}
// New returns a new microbitmatrix driver.
func New() Device {
return Device{}
}
// Configure sets up the device.
func (d *Device) Configure(cfg Config) {
d.SetRotation(cfg.Rotation)
for i := machine.LED_COL_1; i <= machine.LED_ROW_3; i++ {
d.pin[i-machine.LED_COL_1] = i
d.pin[i-machine.LED_COL_1].Configure(machine.PinConfig{Mode: machine.PinOutput})
}
d.ClearDisplay()
d.DisableAll()
}
// SetRotation changes the rotation of the LED matrix
func (d *Device) SetRotation(rotation uint8) {
d.rotation = rotation % 4
}
// SetPixel modifies the internal buffer in a single pixel.
func (d *Device) SetPixel(x int16, y int16, c color.RGBA) {
if x < 0 || x >= 5 || y < 0 || y >= 5 {
return
}
if c.R != 0 || c.G != 0 || c.B != 0 {
d.buffer[matrixRotations[d.rotation][x][y][0]][matrixRotations[d.rotation][x][y][1]] = true
} else {
d.buffer[matrixRotations[d.rotation][x][y][0]][matrixRotations[d.rotation][x][y][1]] = false
}
}
// GetPixel returns if the specific pixels is enabled
func (d *Device) GetPixel(x int16, y int16) bool {
if x < 0 || x >= 5 || y < 0 || y >= 5 {
return false
}
return d.buffer[matrixRotations[d.rotation][x][y][0]][matrixRotations[d.rotation][x][y][1]]
}
// Display sends the buffer (if any) to the screen.
func (d *Device) Display() error {
for row := 0; row < 3; row++ {
d.DisableAll()
d.pin[9+row].High()
for col := 0; col < 9; col++ {
if d.buffer[row][col] {
d.pin[col].Low()
}
}
time.Sleep(time.Millisecond * 2)
}
return nil
}
// ClearDisplay erases the internal buffer
func (d *Device) ClearDisplay() {
for row := 0; row < 3; row++ {
for col := 0; col < 9; col++ {
d.buffer[row][col] = false
}
}
}
// DisableAll disables all the LEDs without modifying the buffer
func (d *Device) DisableAll() {
for i := machine.LED_COL_1; i <= machine.LED_COL_9; i++ {
d.pin[i-machine.LED_COL_1].High()
}
for i := machine.LED_ROW_1; i <= machine.LED_ROW_3; i++ {
d.pin[i-machine.LED_COL_1].Low()
}
}
// EnableAll enables all the LEDs without modifying the buffer
func (d *Device) EnableAll() {
for i := machine.LED_COL_1; i <= machine.LED_COL_9; i++ {
d.pin[i-machine.LED_COL_1].Low()
}
for i := machine.LED_ROW_1; i <= machine.LED_ROW_3; i++ {
d.pin[i-machine.LED_COL_1].High()
}
}
// Size returns the current size of the display.
func (d *Device) Size() (w, h int16) {
return 5, 5
}
+42 -13
View File
@@ -3,7 +3,8 @@
//
// Datasheet:
// https://www.nxp.com/docs/en/data-sheet/MMA8653FC.pdf
package mma8653
//
package mma8653 // import "tinygo.org/x/drivers/mma8653"
import (
"machine"
@@ -11,7 +12,9 @@ import (
// Device wraps an I2C connection to a MMA8653 device.
type Device struct {
bus machine.I2C
bus machine.I2C
Address uint16
sensitivity Sensitivity
}
// New creates a new MMA8653 connection. The I2C bus must already be
@@ -19,30 +22,56 @@ type Device struct {
//
// This function only creates the Device object, it does not touch the device.
func New(bus machine.I2C) Device {
return Device{bus}
return Device{bus, Address, Sensitivity2G}
}
// Connected returns whether a MMA8653 has been found.
// It does a "who am I" request and checks the response.
func (d Device) Connected() bool {
data := []byte{0}
d.bus.ReadRegister(Address, WHO_AM_I, data)
d.bus.ReadRegister(uint8(d.Address), WHO_AM_I, data)
return data[0] == 0x5A
}
// Configure sets up the device for communication.
func (d Device) Configure(speed DataRate) {
data := (uint8(speed) << 3) | 1 // set data rate and ACTIVE mode
d.bus.WriteRegister(Address, CTRL_REG1, []uint8{data})
func (d *Device) Configure(speed DataRate, sensitivity Sensitivity) error {
// Set mode to STANDBY to be able to change the sensitivity.
err := d.bus.WriteRegister(uint8(d.Address), CTRL_REG1, []uint8{0})
if err != nil {
return err
}
// Set sensitivity (2G, 4G, 8G).
err = d.bus.WriteRegister(uint8(d.Address), XYZ_DATA_CFG, []uint8{uint8(sensitivity)})
if err != nil {
return err
}
d.sensitivity = sensitivity
// Set mode to ACTIVE and set the data rate.
err = d.bus.WriteRegister(uint8(d.Address), CTRL_REG1, []uint8{(uint8(speed) << 3) | 1})
if err != nil {
return err
}
return nil
}
// ReadAcceleration reads the current acceleration from the device and returns
// it.
func (d Device) ReadAcceleration() (x int16, y int16, z int16) {
// 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, err error) {
data := make([]byte, 6)
d.bus.ReadRegister(Address, OUT_X_MSB, data)
x = int16((uint16(data[0]) << 8) | uint16(data[1]))
y = int16((uint16(data[2]) << 8) | uint16(data[3]))
z = int16((uint16(data[4]) << 8) | uint16(data[5]))
err = d.bus.ReadRegister(uint8(d.Address), OUT_X_MSB, data)
shift := uint32(8)
switch d.sensitivity {
case Sensitivity4G:
shift = 7
case Sensitivity8G:
shift = 6
}
x = int32(int16((uint16(data[0])<<8)|uint16(data[1]))) * 15625 >> shift
y = int32(int16((uint16(data[2])<<8)|uint16(data[3]))) * 15625 >> shift
z = int32(int16((uint16(data[4])<<8)|uint16(data[5]))) * 15625 >> shift
return
}
+9
View File
@@ -51,3 +51,12 @@ const (
DataRate6Hz // 6.25Hz, 160ms interval
DataRate2Hz // 1.56Hz, 640ms interval
)
type Sensitivity uint8
// Sensitivity constants.
const (
Sensitivity2G Sensitivity = iota
Sensitivity4G
Sensitivity8G
)
+9 -7
View File
@@ -4,7 +4,8 @@
// Datasheets:
// https://store.invensense.com/datasheets/invensense/MPU-6050_DataSheet_V3%204.pdf
// https://www.invensense.com/wp-content/uploads/2015/02/MPU-6000-Register-Map1.pdf
package mpu6050
//
package mpu6050 // import "tinygo.org/x/drivers/mpu6050"
import (
"machine"
@@ -12,7 +13,8 @@ import (
// Device wraps an I2C connection to a MPU6050 device.
type Device struct {
bus machine.I2C
bus machine.I2C
Address uint16
}
// New creates a new MPU6050 connection. The I2C bus must already be
@@ -20,27 +22,27 @@ type Device struct {
//
// This function only creates the Device object, it does not touch the device.
func New(bus machine.I2C) Device {
return Device{bus}
return Device{bus, Address}
}
// Connected returns whether a MPU6050 has been found.
// It does a "who am I" request and checks the response.
func (d Device) Connected() bool {
data := []byte{0}
d.bus.ReadRegister(Address, WHO_AM_I, data)
d.bus.ReadRegister(uint8(d.Address), WHO_AM_I, data)
return data[0] == 0x68
}
// Configure sets up the device for communication.
func (d Device) Configure() {
d.bus.WriteRegister(Address, PWR_MGMT_1, []uint8{0})
d.bus.WriteRegister(uint8(d.Address), PWR_MGMT_1, []uint8{0})
}
// ReadAcceleration reads the current acceleration from the device and returns
// it.
func (d Device) ReadAcceleration() (x int16, y int16, z int16) {
data := make([]byte, 6)
d.bus.ReadRegister(Address, ACCEL_XOUT_H, data)
d.bus.ReadRegister(uint8(d.Address), ACCEL_XOUT_H, data)
x = int16((uint16(data[0]) << 8) | uint16(data[1]))
y = int16((uint16(data[2]) << 8) | uint16(data[3]))
z = int16((uint16(data[4]) << 8) | uint16(data[5]))
@@ -50,7 +52,7 @@ func (d Device) ReadAcceleration() (x int16, y int16, z int16) {
// ReadRotation reads the current rotation from the device and returns it.
func (d Device) ReadRotation() (x int16, y int16, z int16) {
data := make([]byte, 6)
d.bus.ReadRegister(Address, GYRO_XOUT_H, data)
d.bus.ReadRegister(uint8(d.Address), GYRO_XOUT_H, data)
x = int16((uint16(data[0]) << 8) | uint16(data[1]))
y = int16((uint16(data[2]) << 8) | uint16(data[3]))
z = int16((uint16(data[4]) << 8) | uint16(data[5]))
+151
View File
@@ -0,0 +1,151 @@
// Package pcd8544 implements a driver for the PCD8544 48x84 pixels matrix LCD, used in Nokia's 5110 and 3310 phones.
//
// Datasheet: http://eia.udg.edu/~forest/PCD8544_1.pdf
//
package pcd8544 // import "tinygo.org/x/drivers/pcd8544"
import (
"errors"
"image/color"
"machine"
"time"
)
// Device wraps an SPI connection.
type Device struct {
bus machine.SPI
dcPin machine.Pin
rstPin machine.Pin
scePin machine.Pin
buffer []byte
width int16
height int16
bufferSize int16
}
type Config struct {
Width int16
Height int16
}
// New creates a new PCD8544 connection. The SPI bus must already be configured.
func New(bus machine.SPI, dcPin, rstPin, scePin machine.Pin) *Device {
return &Device{
bus: bus,
dcPin: dcPin,
rstPin: rstPin,
scePin: scePin,
}
}
// Configure initializes the display with default configuration
func (d *Device) Configure(cfg Config) {
if cfg.Width != 0 {
d.width = cfg.Width
} else {
d.width = 84
}
if cfg.Height != 0 {
d.height = cfg.Height
} else {
d.height = 48
}
d.bufferSize = d.width * d.height / 8
d.buffer = make([]byte, d.bufferSize)
d.rstPin.Low()
time.Sleep(100 * time.Nanosecond)
d.rstPin.High()
d.SendCommand(FUNCTIONSET | EXTENDEDINSTRUCTION) // H = 1
d.SendCommand(SETVOP | 0x3f) // 0x3f : Vop6 = 0, Vop5 to Vop0 = 1
d.SendCommand(SETTEMP | 0x03) // Experimentally determined
d.SendCommand(SETBIAS | 0x03) // Experimentally determined
d.SendCommand(FUNCTIONSET) // H = 0
d.SendCommand(DISPLAYCONTROL | DISPLAYNORMAL)
}
// ClearBuffer clears the image buffer
func (d *Device) ClearBuffer() {
d.buffer = make([]byte, d.bufferSize)
}
// ClearDisplay clears the image buffer and clear the display
func (d *Device) ClearDisplay() {
d.ClearBuffer()
d.Display()
}
// Display sends the whole buffer to the screen
func (d *Device) Display() error {
d.SendCommand(FUNCTIONSET) // H = 0
d.SendCommand(SETXADDR)
d.SendCommand(SETYADDR)
for i := int16(0); i < d.bufferSize; i++ {
d.SendData(d.buffer[i])
}
return nil
}
// sendDataCommand sends image data or a command to the screen
func (d *Device) sendDataCommand(isCommand bool, data uint8) {
if isCommand {
d.dcPin.Low()
} else {
d.dcPin.High()
}
d.scePin.Low()
d.bus.Transfer(data)
d.scePin.High()
}
// SetPixel enables or disables a pixel in the buffer
// color.RGBA{0, 0, 0, 255} is consider transparent, anything else
// with enable a pixel on the screen
func (d *Device) SetPixel(x int16, y int16, c color.RGBA) {
if x < 0 || x >= d.width || y < 0 || y >= d.height {
return
}
byteIndex := x + (y/8)*d.width
if c.R != 0 || c.G != 0 || c.B != 0 {
d.buffer[byteIndex] |= 1 << uint8(y%8)
} else {
d.buffer[byteIndex] &^= 1 << uint8(y%8)
}
}
// GetPixel returns if the specified pixel is on (true) or off (false)
func (d *Device) GetPixel(x int16, y int16) bool {
if x < 0 || x >= d.width || y < 0 || y >= d.height {
return false
}
byteIndex := x + (y/8)*d.width
return (d.buffer[byteIndex] >> uint8(y%8) & 0x1) == 1
}
// SetBuffer changes the whole buffer at once
func (d *Device) SetBuffer(buffer []byte) error {
if int16(len(buffer)) != d.bufferSize {
//return ErrBuffer
return errors.New("Wrong size buffer")
}
for i := int16(0); i < d.bufferSize; i++ {
d.buffer[i] = buffer[i]
}
return nil
}
// 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)
}
// Size returns the current size of the display.
func (d *Device) Size() (w, h int16) {
return d.width, d.height
}
+21
View File
@@ -0,0 +1,21 @@
package pcd8544
// Registers
const (
POWERDOWN = 0x04
ENTRYMODE = 0x02
EXTENDEDINSTRUCTION = 0x01
FUNCTIONSET = 0x20
DISPLAYCONTROL = 0x08
DISPLAYBLANK = 0x0
DISPLAYNORMAL = 0x04
DISPLAYALLON = 0x01
DISPLAYINVERTED = 0x05
SETYADDR = 0x40
SETXADDR = 0x80
SETTEMP = 0x04
SETBIAS = 0x10
SETVOP = 0x80
)
+15
View File
@@ -0,0 +1,15 @@
package sht3x
// Constants/addresses used for I2C.
// The I2C address which this device listens to.
const (
AddressA = 0x44
AddressB = 0x45
)
const (
// single shot, high repeatability
MEASUREMENT_COMMAND_MSB = 0x24
MEASUREMENT_COMMAND_LSB = 0x00
)
+72
View File
@@ -0,0 +1,72 @@
// Package sht3x provides a driver for the SHT3x digital humidity sensor
// series by Sensirion.
//
// Datasheet:
// https://www.sensirion.com/fileadmin/user_upload/customers/sensirion/Dokumente/0_Datasheets/Humidity/Sensirion_Humidity_Sensors_SHT3x_Datasheet_digital.pdf
//
package sht3x // import "tinygo.org/x/drivers/sht3x"
import (
"machine"
"time"
)
// Device wraps an I2C connection to a SHT31 device.
type Device struct {
bus machine.I2C
Address uint16
}
// New creates a new SHT31 connection. The I2C bus must already be
// configured.
//
// This function only creates the Device object, it does not initialize the device.
// You must call Configure() first in order to use the device itself.
func New(bus machine.I2C) Device {
return Device{
bus: bus,
Address: AddressA,
}
}
// Read returns the temperature in celsius milli degrees (ºC/1000).
func (d *Device) ReadTemperature() (tempMilliCelsius int32, err error) {
tempMilliCelsius, _, err = d.ReadTemperatureHumidity()
return tempMilliCelsius, err
}
// Read returns the relative humidity in hundredths of a percent.
func (d *Device) ReadHumidity() (relativeHumidity int16, err error) {
_, relativeHumidity, err = d.ReadTemperatureHumidity()
return relativeHumidity, err
}
// Read returns both the temperature and relative humidity.
func (d *Device) ReadTemperatureHumidity() (tempMilliCelsius int32, relativeHumidity int16, err error) {
var rawTemp, rawHum, errx = d.rawReadings()
if errx != nil {
err = errx
return
}
tempMilliCelsius = (35000 * int32(rawTemp) / 13107) - 45000
relativeHumidity = int16(2000 * int32(rawHum) / 13107)
return tempMilliCelsius, relativeHumidity, err
}
// rawReadings returns the sensor's raw values of the temperature and humidity
func (d *Device) rawReadings() (uint16, uint16, error) {
d.bus.Tx(d.Address, []byte{MEASUREMENT_COMMAND_MSB, MEASUREMENT_COMMAND_LSB}, nil)
time.Sleep(17 * time.Millisecond)
var data [5]byte
d.bus.Tx(d.Address, []byte{}, data[:])
// ignore crc for now
return readUint(data[0], data[1]), readUint(data[3], data[4]), nil
}
// readUint converts two bytes to uint16
func readUint(msb byte, lsb byte) uint16 {
return (uint16(msb) << 8) | uint16(lsb)
}
+41
View File
@@ -0,0 +1,41 @@
package ssd1306
// Registers
const (
Address = 0x3D
Address_128_32 = 0x3C
SETCONTRAST = 0x81
DISPLAYALLON_RESUME = 0xA4
DISPLAYALLON = 0xA5
NORMALDISPLAY = 0xA6
INVERTDISPLAY = 0xA7
DISPLAYOFF = 0xAE
DISPLAYON = 0xAF
SETDISPLAYOFFSET = 0xD3
SETCOMPINS = 0xDA
SETVCOMDETECT = 0xDB
SETDISPLAYCLOCKDIV = 0xD5
SETPRECHARGE = 0xD9
SETMULTIPLEX = 0xA8
SETLOWCOLUMN = 0x00
SETHIGHCOLUMN = 0x10
SETSTARTLINE = 0x40
MEMORYMODE = 0x20
COLUMNADDR = 0x21
PAGEADDR = 0x22
COMSCANINC = 0xC0
COMSCANDEC = 0xC8
SEGREMAP = 0xA0
CHARGEPUMP = 0x8D
ACTIVATE_SCROLL = 0x2F
DEACTIVATE_SCROLL = 0x2E
SET_VERTICAL_SCROLL_AREA = 0xA3
RIGHT_HORIZONTAL_SCROLL = 0x26
LEFT_HORIZONTAL_SCROLL = 0x27
VERTICAL_AND_RIGHT_HORIZONTAL_SCROLL = 0x29
VERTICAL_AND_LEFT_HORIZONTAL_SCROLL = 0x2A
EXTERNALVCC VccMode = 0x1
SWITCHCAPVCC VccMode = 0x2
)
+300
View File
@@ -0,0 +1,300 @@
// Package ssd1306 implements a driver for the SSD1306 led matrix controller, it comes in various colors and screen sizes.
//
// Datasheet: https://cdn-shop.adafruit.com/datasheets/SSD1306.pdf
//
package ssd1306 // import "tinygo.org/x/drivers/ssd1306"
import (
"errors"
"image/color"
"machine"
"time"
)
// Device wraps an SPI connection.
type Device struct {
bus Buser
buffer []byte
width int16
height int16
bufferSize int16
vccState VccMode
}
// Config is the configuration for the display
type Config struct {
Width int16
Height int16
VccState VccMode
Address uint16
}
type I2CBus struct {
wire machine.I2C
Address uint16
}
type SPIBus struct {
wire machine.SPI
dcPin machine.Pin
resetPin machine.Pin
csPin machine.Pin
}
type Buser interface {
configure()
tx(data []byte, isCommand bool)
setAddress(address uint16)
}
type VccMode uint8
// NewI2C creates a new SSD1306 connection. The I2C wire must already be configured.
func NewI2C(bus machine.I2C) Device {
return Device{
bus: &I2CBus{
wire: bus,
Address: Address,
},
}
}
// NewSPI creates a new SSD1306 connection. The SPI wire must already be configured.
func NewSPI(bus machine.SPI, dcPin, resetPin, csPin machine.Pin) Device {
dcPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
resetPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
csPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
return Device{
bus: &SPIBus{
wire: bus,
dcPin: dcPin,
resetPin: resetPin,
csPin: csPin,
},
}
}
// Configure initializes the display with default configuration
func (d *Device) Configure(cfg Config) {
if cfg.Width != 0 {
d.width = cfg.Width
} else {
d.width = 128
}
if cfg.Height != 0 {
d.height = cfg.Height
} else {
d.height = 64
}
if cfg.Address != 0 {
d.bus.setAddress(cfg.Address)
}
if cfg.VccState != 0 {
d.vccState = cfg.VccState
} else {
d.vccState = SWITCHCAPVCC
}
d.bufferSize = d.width * d.height / 8
d.buffer = make([]byte, d.bufferSize)
d.bus.configure()
time.Sleep(100 * time.Nanosecond)
d.Command(DISPLAYOFF)
d.Command(SETDISPLAYCLOCKDIV)
d.Command(0x80)
d.Command(SETMULTIPLEX)
d.Command(uint8(d.height - 1))
d.Command(SETDISPLAYOFFSET)
d.Command(0x0)
d.Command(SETSTARTLINE | 0x0)
d.Command(CHARGEPUMP)
if d.vccState == EXTERNALVCC {
d.Command(0x10)
} else {
d.Command(0x14)
}
d.Command(MEMORYMODE)
d.Command(0x00)
d.Command(SEGREMAP | 0x1)
d.Command(COMSCANDEC)
if (d.width == 128 && d.height == 64) || (d.width == 64 && d.height == 48) { // 128x64 or 64x48
d.Command(SETCOMPINS)
d.Command(0x12)
d.Command(SETCONTRAST)
if d.vccState == EXTERNALVCC {
d.Command(0x9F)
} else {
d.Command(0xCF)
}
} else if d.width == 128 && d.height == 32 { // 128x32
d.Command(SETCOMPINS)
d.Command(0x02)
d.Command(SETCONTRAST)
d.Command(0x8F)
} else if d.width == 96 && d.height == 16 { // 96x16
d.Command(SETCOMPINS)
d.Command(0x2)
d.Command(SETCONTRAST)
if d.vccState == EXTERNALVCC {
d.Command(0x10)
} else {
d.Command(0xAF)
}
} else {
// fail silently, it might work
println("there's no configuration for this display's size")
}
d.Command(SETPRECHARGE)
if d.vccState == EXTERNALVCC {
d.Command(0x22)
} else {
d.Command(0xF1)
}
d.Command(SETVCOMDETECT)
d.Command(0x40)
d.Command(DISPLAYALLON_RESUME)
d.Command(NORMALDISPLAY)
d.Command(DEACTIVATE_SCROLL)
d.Command(DISPLAYON)
}
// ClearBuffer clears the image buffer
func (d *Device) ClearBuffer() {
for i := int16(0); i < d.bufferSize; i++ {
d.buffer[i] = 0
}
}
// ClearDisplay clears the image buffer and clear the display
func (d *Device) ClearDisplay() {
d.ClearBuffer()
d.Display()
}
// Display sends the whole buffer to the screen
func (d *Device) Display() error {
// In the 128x64 (SPI) screen resetting to 0x0 after 128 times corrupt the buffer
// Since we're printing the whole buffer, avoid resetting it
if d.width != 128 || d.height != 64 {
d.Command(COLUMNADDR)
d.Command(0)
d.Command(uint8(d.width - 1))
d.Command(PAGEADDR)
d.Command(0)
d.Command(uint8(d.height/8) - 1)
}
d.Tx(d.buffer, false)
return nil
}
// SetPixel enables or disables a pixel in the buffer
// color.RGBA{0, 0, 0, 255} is consider transparent, anything else
// with enable a pixel on the screen
func (d *Device) SetPixel(x int16, y int16, c color.RGBA) {
if x < 0 || x >= d.width || y < 0 || y >= d.height {
return
}
byteIndex := x + (y/8)*d.width
if c.R != 0 || c.G != 0 || c.B != 0 {
d.buffer[byteIndex] |= 1 << uint8(y%8)
} else {
d.buffer[byteIndex] &^= 1 << uint8(y%8)
}
}
// GetPixel returns if the specified pixel is on (true) or off (false)
func (d *Device) GetPixel(x int16, y int16) bool {
if x < 0 || x >= d.width || y < 0 || y >= d.height {
return false
}
byteIndex := x + (y/8)*d.width
return (d.buffer[byteIndex] >> uint8(y%8) & 0x1) == 1
}
// SetBuffer changes the whole buffer at once
func (d *Device) SetBuffer(buffer []byte) error {
if int16(len(buffer)) != d.bufferSize {
//return ErrBuffer
return errors.New("Wrong size buffer")
}
for i := int16(0); i < d.bufferSize; i++ {
d.buffer[i] = buffer[i]
}
return nil
}
// Command sends a command to the display
func (d *Device) Command(command uint8) {
d.bus.tx([]byte{command}, true)
}
// setAddress sets the address to the I2C bus
func (b *I2CBus) setAddress(address uint16) {
b.Address = address
}
// setAddress does nothing, but it's required to avoid reflection
func (b *SPIBus) setAddress(address uint16) {
// do nothing
println("trying to Configure an address on a SPI device")
}
// configure does nothing, but it's required to avoid reflection
func (b *I2CBus) configure() {}
// configure configures some pins with the SPI bus
func (b *SPIBus) configure() {
b.csPin.Low()
b.dcPin.Low()
b.resetPin.Low()
b.resetPin.High()
time.Sleep(1 * time.Millisecond)
b.resetPin.Low()
time.Sleep(10 * time.Millisecond)
b.resetPin.High()
}
// Tx sends data to the display
func (d *Device) Tx(data []byte, isCommand bool) {
d.bus.tx(data, isCommand)
}
// tx sends data to the display (I2CBus implementation)
func (b *I2CBus) tx(data []byte, isCommand bool) {
if isCommand {
b.wire.WriteRegister(uint8(b.Address), 0x00, data)
} else {
b.wire.WriteRegister(uint8(b.Address), 0x40, data)
}
}
// tx sends data to the display (SPIBus implementation)
func (b *SPIBus) tx(data []byte, isCommand bool) {
if isCommand {
b.csPin.High()
time.Sleep(1 * time.Millisecond)
b.dcPin.Low()
b.csPin.Low()
b.wire.Tx(data, nil)
b.csPin.High()
} else {
b.csPin.High()
time.Sleep(1 * time.Millisecond)
b.dcPin.High()
b.csPin.Low()
b.wire.Tx(data, nil)
b.csPin.High()
}
}
// Size returns the current size of the display.
func (d *Device) Size() (w, h int16) {
return d.width, d.height
}
+86
View File
@@ -0,0 +1,86 @@
// Package thermistor is for temperature sensing using a thermistor
// such as the NTC 3950.
//
// Datasheet: https://www.farnell.com/datasheets/33552.pdf
//
// This code is an interpretation of Adafruit Thermistor module in Python:
// https://github.com/adafruit/Adafruit_CircuitPython_Thermistor
//
// It uses the SteinhartHart equation to calculate the temperature
// based on the resistance:
// https://en.wikipedia.org/wiki/Steinhart%E2%80%93Hart_equation
//
// To use with other thermistors adjust the BCoefficient and NominalTemperature
// values to match the specific thermistor you wish to use.
//
// sensor.NominalTemperature = 25
// sensor.BCoefficient = 3950
//
// Set the SeriesResistor and NominalResistance based on the microcontroller voltage and
// circuit that you have in use. Set HighSide based on if the thermistor is connected from
// the ADC pin to the powered side (true) or to ground (false).
//
// sensor.SeriesResistor = 10000
// sensor.NominalResistance = 10000
// sensor.HighSide = true
//
package thermistor // import "tinygo.org/x/drivers/thermistor"
import (
"machine"
"math"
)
// Device holds the ADC pin and the needed settings for calculating the
// temperature based on the resistance.
type Device struct {
adc *machine.ADC
SeriesResistor uint32
NominalResistance uint32
NominalTemperature uint32
BCoefficient uint32
HighSide bool
}
// New returns a new thermistor driver given an ADC pin.
func New(pin machine.Pin) Device {
adc := machine.ADC{pin}
return Device{
adc: &adc,
SeriesResistor: 10000,
NominalResistance: 10000,
NominalTemperature: 25,
BCoefficient: 3950,
HighSide: true,
}
}
// Configure configures the ADC pin used for the thermistor.
func (d *Device) Configure() {
d.adc.Configure()
}
// ReadTemperature returns the temperature in celsius milli degrees (ºC/1000)
func (d *Device) ReadTemperature() (temperature int32, err error) {
var reading uint32
if d.HighSide {
// Thermistor connected from analog input to high logic level.
val := d.adc.Get()
reading = uint32(val) / 64
reading = (1023 * d.SeriesResistor) / reading
reading -= d.SeriesResistor
} else {
// Thermistor connected from analog input to ground.
reading = d.SeriesResistor / uint32(65535/d.adc.Get()-1)
}
var steinhart float64
steinhart = float64(reading) / float64(d.NominalResistance) // (R/Ro)
steinhart = math.Log(steinhart) // ln(R/Ro)
steinhart /= float64(d.BCoefficient) // 1/B * ln(R/Ro)
steinhart += 1.0 / (float64(d.NominalTemperature) + 273.15) // + (1/To)
steinhart = 1.0 / steinhart // Invert
steinhart -= 273.15 // convert to C
return int32(steinhart * 1000), nil
}
+88
View File
@@ -0,0 +1,88 @@
package vl53l1x
// The I2C address which this device listens to.
const Address = 0x29 //0x52
// Registers
const (
CHIP_ID = 0xEACC
SOFT_RESET = 0x0000
OSC_MEASURED_FAST_OSC_FREQUENCY = 0x0006
VHV_CONFIG_TIMEOUT_MACROP_LOOP_BOUND = 0x0008
VHV_CONFIG_INIT = 0x000B
ALGO_PART_TO_PART_RANGE_OFFSET_MM = 0x001E
MM_CONFIG_OUTER_OFFSET_MM = 0x0022
DSS_CONFIG_TARGET_TOTAL_RATE_MCPS = 0x0024
PAD_I2C_HV_EXTSUP_CONFIG = 0x002E
GPIO_TIO_HV_STATUS = 0x0031
SIGMA_ESTIMATOR_EFFECTIVE_PULSE_WIDTH_NS = 0x0036
SIGMA_ESTIMATOR_EFFECTIVE_AMBIENT_WIDTH_NS = 0x0037
ALGO_CROSSTALK_COMPENSATION_VALID_HEIGHT_MM = 0x0039
ALGO_RANGE_MIN_CLIP = 0x003F
ALGO_CONSISTENCY_CHECK_TOLERANCE = 0x0040
CAL_CONFIG_VCSEL_START = 0x0047
PHASECAL_CONFIG_TIMEOUT_MACROP = 0x004B
PHASECAL_CONFIG_OVERRIDE = 0x004D
DSS_CONFIG_ROI_MODE_CONTROL = 0x004F
SYSTEM_THRESH_RATE_HIGH = 0x0050
SYSTEM_THRESH_RATE_LOW = 0x0052
DSS_CONFIG_MANUAL_EFFECTIVE_SPADS_SELECT = 0x0054
DSS_CONFIG_APERTURE_ATTENUATION = 0x0057
MM_CONFIG_TIMEOUT_MACROP_A = 0x005A
MM_CONFIG_TIMEOUT_MACROP_B = 0x005C
RANGE_CONFIG_TIMEOUT_MACROP_A = 0x005E
RANGE_CONFIG_VCSEL_PERIOD_A = 0x0060
RANGE_CONFIG_TIMEOUT_MACROP_B = 0x0061
RANGE_CONFIG_VCSEL_PERIOD_B = 0x0063
RANGE_CONFIG_SIGMA_THRESH = 0x0064
RANGE_CONFIG_MIN_COUNT_RATE_RTN_LIMIT_MCPS = 0x0066
RANGE_CONFIG_VALID_PHASE_HIGH = 0x0069
SYSTEM_INTERMEASUREMENT_PERIOD = 0x006C
SYSTEM_GROUPED_PARAMETER_HOLD_0 = 0x0071
SYSTEM_SEED_CONFIG = 0x0077
SD_CONFIG_WOI_SD0 = 0x0078
SD_CONFIG_WOI_SD1 = 0x0079
SD_CONFIG_INITIAL_PHASE_SD0 = 0x007A
SD_CONFIG_INITIAL_PHASE_SD1 = 0x007B
SYSTEM_GROUPED_PARAMETER_HOLD_1 = 0x007C
SD_CONFIG_QUANTIFIER = 0x007E
SYSTEM_SEQUENCE_CONFIG = 0x0081
SYSTEM_GROUPED_PARAMETER_HOLD = 0x0082
SYSTEM_INTERRUPT_CLEAR = 0x0086
SYSTEM_MODE_START = 0x0087
RESULT_RANGE_STATUS = 0x0089
PHASECAL_RESULT_VCSEL_START = 0x00D8
RESULT_OSC_CALIBRATE_VAL = 0x00DE
FIRMWARE_SYSTEM_STATUS = 0x00E5
WHO_AM_I = 0x010F
SHADOW_RESULT_FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD0_HI = 0x0FBE
TIMING_GUARD = 4528
TARGETRATE = 0x0A00
)
const (
SHORT DistanceMode = iota
MEDIUM
LONG
)
const (
RangeValid RangeStatus = iota
SigmaFail
SignalFail
RangeValidMinRangeClipped
OutOfBoundsFail
HardwareFail
RangeValidNoWrapCheckFail
WrapTargetFail
ProcessingFail
XtalkSignalFail
SynchronizationInt
MergedPulse
TargetPresentLackOfSignal
MinRangeFail
RangeInvalid
None RangeStatus = 255
)
+546
View File
@@ -0,0 +1,546 @@
// Package vl53l1x provides a driver for the VL53L1X time-of-flight
// distance sensor
//
// Datasheet:
// https://www.st.com/resource/en/datasheet/vl53l1x.pdf
// This driver was based on the library https://github.com/pololu/vl53l1x-arduino
// and ST's VL53L1X API (STSW-IMG007)
// https://www.st.com/content/st_com/en/products/embedded-software/proximity-sensors-software/stsw-img007.html
//
package vl53l1x // import "tinygo.org/x/drivers/vl53l1x"
import (
"machine"
"time"
)
type DistanceMode uint8
type RangeStatus uint8
type rangingData struct {
mm uint16
status RangeStatus
signalRateMCPS int32 //MCPS : Mega Count Per Second
ambientRateMCPS int32
}
type resultBuffer struct {
status uint8
streamCount uint8
effectiveSPADCount uint16
ambientRateMCPSSD0 uint16
mmCrosstalkSD0 uint16
signalRateCrosstalkMCPSSD0 uint16
}
// Device wraps an I2C connection to a VL53L1X device.
type Device struct {
bus machine.I2C
Address uint16
mode DistanceMode
timeout uint32
fastOscillatorFreq uint16
oscillatorOffset uint16
calibrated bool
VHVInit uint8
VHVTimeout uint8
rangingData rangingData
results resultBuffer
}
// New creates a new VL53L1X connection. The I2C bus must already be
// configured.
//
// This function only creates the Device object, it does not touch the device.
func New(bus machine.I2C) Device {
return Device{
bus: bus,
Address: Address,
mode: LONG,
timeout: 500,
}
}
// Connected returns whether a VL53L1X has been found.
// It does a "who am I" request and checks the response.
func (d *Device) Connected() bool {
return d.readReg16Bit(WHO_AM_I) == CHIP_ID
}
// Configure sets up the device for communication
func (d *Device) Configure(use2v8Mode bool) bool {
if !d.Connected() {
return false
}
d.writeReg(SOFT_RESET, 0x00)
time.Sleep(100 * time.Microsecond)
d.writeReg(SOFT_RESET, 0x01)
time.Sleep(1 * time.Millisecond)
start := time.Now()
for (d.readReg(FIRMWARE_SYSTEM_STATUS) & 0x01) == 0 {
elapsed := time.Since(start)
if d.timeout > 0 && uint32(elapsed.Seconds()*1000) > d.timeout {
return false
}
}
if use2v8Mode {
d.writeReg(PAD_I2C_HV_EXTSUP_CONFIG, d.readReg(PAD_I2C_HV_EXTSUP_CONFIG)|0x01)
}
d.fastOscillatorFreq = d.readReg16Bit(OSC_MEASURED_FAST_OSC_FREQUENCY)
d.oscillatorOffset = d.readReg16Bit(RESULT_OSC_CALIBRATE_VAL)
// static config
d.writeReg16Bit(DSS_CONFIG_TARGET_TOTAL_RATE_MCPS, TARGETRATE)
d.writeReg(GPIO_TIO_HV_STATUS, 0x02)
d.writeReg(SIGMA_ESTIMATOR_EFFECTIVE_PULSE_WIDTH_NS, 8)
d.writeReg(SIGMA_ESTIMATOR_EFFECTIVE_AMBIENT_WIDTH_NS, 16)
d.writeReg(ALGO_CROSSTALK_COMPENSATION_VALID_HEIGHT_MM, 0xFF)
d.writeReg(ALGO_RANGE_MIN_CLIP, 0)
d.writeReg(ALGO_CONSISTENCY_CHECK_TOLERANCE, 2)
// general config
d.writeReg16Bit(SYSTEM_THRESH_RATE_HIGH, 0x0000)
d.writeReg16Bit(SYSTEM_THRESH_RATE_LOW, 0x0000)
d.writeReg(DSS_CONFIG_APERTURE_ATTENUATION, 0x38)
// timing config
d.writeReg16Bit(RANGE_CONFIG_SIGMA_THRESH, 360)
d.writeReg16Bit(RANGE_CONFIG_MIN_COUNT_RATE_RTN_LIMIT_MCPS, 192)
// dynamic config
d.writeReg(SYSTEM_GROUPED_PARAMETER_HOLD_0, 0x01)
d.writeReg(SYSTEM_GROUPED_PARAMETER_HOLD_1, 0x01)
d.writeReg(SD_CONFIG_QUANTIFIER, 2)
d.writeReg(SYSTEM_GROUPED_PARAMETER_HOLD, 0x00)
d.writeReg(SYSTEM_SEED_CONFIG, 1)
// Low power auto mode
d.writeReg(SYSTEM_SEQUENCE_CONFIG, 0x8B) // VHV, PHASECAL, DSS1, RANGE
d.writeReg16Bit(DSS_CONFIG_MANUAL_EFFECTIVE_SPADS_SELECT, 200<<8)
d.writeReg(DSS_CONFIG_ROI_MODE_CONTROL, 2) // REQUESTED_EFFFECTIVE_SPADS
d.SetDistanceMode(d.mode)
d.SetMeasurementTimingBudget(50000)
d.writeReg16Bit(ALGO_PART_TO_PART_RANGE_OFFSET_MM, d.readReg16Bit(MM_CONFIG_OUTER_OFFSET_MM)*4)
return true
}
// SetTimeout configures the timeout
func (d *Device) SetTimeout(timeout uint32) {
d.timeout = timeout
}
// SetDistanceMode sets the mode for calculating the distance.
// Distance mode vs. max. distance
// SHORT: 136cm (dark) - 135cm (strong ambient light)
// MEDIUM: 290cm (dark) - 76cm (strong ambient light)
// LONG: 360cm (dark) - 73cm (strong ambient light)
// It returns false if an invalid mode is provided
func (d *Device) SetDistanceMode(mode DistanceMode) bool {
budgetMicroseconds := d.GetMeasurementTimingBudget()
switch mode {
case SHORT:
// timing config
d.writeReg(RANGE_CONFIG_VCSEL_PERIOD_A, 0x07)
d.writeReg(RANGE_CONFIG_VCSEL_PERIOD_B, 0x05)
d.writeReg(RANGE_CONFIG_VALID_PHASE_HIGH, 0x38)
// dynamic config
d.writeReg(SD_CONFIG_WOI_SD0, 0x07)
d.writeReg(SD_CONFIG_WOI_SD1, 0x05)
d.writeReg(SD_CONFIG_INITIAL_PHASE_SD0, 6)
d.writeReg(SD_CONFIG_INITIAL_PHASE_SD1, 6)
break
case MEDIUM:
// timing config
d.writeReg(RANGE_CONFIG_VCSEL_PERIOD_A, 0x0B)
d.writeReg(RANGE_CONFIG_VCSEL_PERIOD_B, 0x09)
d.writeReg(RANGE_CONFIG_VALID_PHASE_HIGH, 0x78)
// dynamic config
d.writeReg(SD_CONFIG_WOI_SD0, 0x0B)
d.writeReg(SD_CONFIG_WOI_SD1, 0x09)
d.writeReg(SD_CONFIG_INITIAL_PHASE_SD0, 10)
d.writeReg(SD_CONFIG_INITIAL_PHASE_SD1, 10)
break
case LONG:
// timing config
d.writeReg(RANGE_CONFIG_VCSEL_PERIOD_A, 0x0F)
d.writeReg(RANGE_CONFIG_VCSEL_PERIOD_B, 0x0D)
d.writeReg(RANGE_CONFIG_VALID_PHASE_HIGH, 0xB8)
// dynamic config
d.writeReg(SD_CONFIG_WOI_SD0, 0x0F)
d.writeReg(SD_CONFIG_WOI_SD1, 0x0D)
d.writeReg(SD_CONFIG_INITIAL_PHASE_SD0, 14)
d.writeReg(SD_CONFIG_INITIAL_PHASE_SD1, 14)
break
default:
return false
}
d.SetMeasurementTimingBudget(budgetMicroseconds)
d.mode = mode
return true
}
// GetMeasurementTimingBudget returns the timing budget in microseconds
func (d *Device) GetMeasurementTimingBudget() uint32 {
macroPeriod := d.calculateMacroPeriod(uint32(d.readReg(RANGE_CONFIG_VCSEL_PERIOD_A)))
rangeConfigTimeout := timeoutMclksToMicroseconds(decodeTimeout(d.readReg16Bit(RANGE_CONFIG_TIMEOUT_MACROP_A)), macroPeriod)
return 2 * uint32(rangeConfigTimeout) * TIMING_GUARD
}
// SetMeasurementTimingBudget configures the timing budget in microseconds
// It returns false if an invalid timing budget is provided
func (d *Device) SetMeasurementTimingBudget(budgetMicroseconds uint32) bool {
if budgetMicroseconds <= TIMING_GUARD {
return false
}
budgetMicroseconds -= TIMING_GUARD
if budgetMicroseconds > 1100000 {
return false
}
rangeConfigTimeout := budgetMicroseconds / 2
// Update Macro Period for Range A VCSEL Period
macroPeriod := d.calculateMacroPeriod(uint32(d.readReg(RANGE_CONFIG_VCSEL_PERIOD_A)))
// Update Phase timeout - uses Timing A
phasecalTimeoutMclks := timeoutMicrosecondsToMclks(1000, macroPeriod)
if phasecalTimeoutMclks > 0xFF {
phasecalTimeoutMclks = 0xFF
}
d.writeReg(PHASECAL_CONFIG_TIMEOUT_MACROP, uint8(phasecalTimeoutMclks))
// Update MM Timing A timeout
d.writeReg16Bit(MM_CONFIG_TIMEOUT_MACROP_A, encodeTimeout(timeoutMicrosecondsToMclks(1, macroPeriod)))
// Update Range Timing A timeout
d.writeReg16Bit(RANGE_CONFIG_TIMEOUT_MACROP_A, encodeTimeout(timeoutMicrosecondsToMclks(rangeConfigTimeout, macroPeriod)))
macroPeriod = d.calculateMacroPeriod(uint32(d.readReg(RANGE_CONFIG_VCSEL_PERIOD_B)))
// Update MM Timing B timeout
d.writeReg16Bit(MM_CONFIG_TIMEOUT_MACROP_B, encodeTimeout(timeoutMicrosecondsToMclks(1, macroPeriod)))
// Update Range Timing B timeout
d.writeReg16Bit(RANGE_CONFIG_TIMEOUT_MACROP_B, encodeTimeout(timeoutMicrosecondsToMclks(rangeConfigTimeout, macroPeriod)))
return true
}
// Read stores in the buffer the values of the sensor and returns
// the current distance in mm
func (d *Device) Read(blocking bool) uint16 {
if blocking {
start := time.Now()
for !d.dataReady() {
elapsed := time.Since(start)
if d.timeout > 0 && uint32(elapsed.Seconds()*1000) > d.timeout {
d.rangingData.status = None
d.rangingData.mm = 0
d.rangingData.signalRateMCPS = 0
d.rangingData.ambientRateMCPS = 0
return d.rangingData.mm
}
}
}
d.readResults()
if !d.calibrated {
d.setupManualCalibration()
d.calibrated = true
}
d.updateDSS()
d.getRangingData()
d.writeReg(SYSTEM_INTERRUPT_CLEAR, 0x01) //sys_interrupt_clear_range
return d.rangingData.mm
}
// updateDSS updates the DSS
func (d *Device) updateDSS() {
spadCount := d.results.effectiveSPADCount
if spadCount != 0 {
totalRatePerSpad := uint32(d.results.signalRateCrosstalkMCPSSD0) + uint32(d.results.ambientRateMCPSSD0)
if totalRatePerSpad > 0xFFFF {
totalRatePerSpad = 0xFFFF
}
totalRatePerSpad <<= 16
totalRatePerSpad /= uint32(spadCount)
if totalRatePerSpad != 0 {
requireSpads := (uint32(TARGETRATE) << 16) / totalRatePerSpad
if requireSpads > 0xFFFF {
requireSpads = 0xFFFF
}
d.writeReg16Bit(DSS_CONFIG_MANUAL_EFFECTIVE_SPADS_SELECT, uint16(requireSpads))
return
}
}
d.writeReg16Bit(DSS_CONFIG_MANUAL_EFFECTIVE_SPADS_SELECT, 0x8000)
}
// readResults read the register and stores the data in the results buffer
func (d *Device) readResults() {
data := make([]byte, 17)
msb := byte((RESULT_RANGE_STATUS >> 8) & 0xFF)
lsb := byte(RESULT_RANGE_STATUS & 0xFF)
d.bus.Tx(d.Address, []byte{msb, lsb}, data)
d.results.status = data[0]
// data[1] report_status : not used
d.results.streamCount = data[2]
d.results.effectiveSPADCount = readUint(data[3], data[4])
// data[5] , data[6] peak signal count rate mcps sd0 : not used
d.results.ambientRateMCPSSD0 = readUint(data[7], data[8])
// data[9] , data[10] sigma_sd0 : not used
// data[11] , data[12] phase_sd0 : not used
d.results.mmCrosstalkSD0 = readUint(data[13], data[14])
d.results.signalRateCrosstalkMCPSSD0 = readUint(data[15], data[16])
}
// dataReady returns true when the data is ready to be read
func (d *Device) dataReady() bool {
return (d.readReg(GPIO_TIO_HV_STATUS) & 0x01) == 0
}
// Distance returns the distance in mm
func (d *Device) Distance() int32 {
return int32(d.rangingData.mm)
}
// Status returns the status of the sensor
func (d *Device) Status() RangeStatus {
return d.rangingData.status
}
// SignalRate returns the peak signal rate in count per second (cps)
func (d *Device) SignalRate() int32 {
return d.rangingData.signalRateMCPS
}
// AmbientRate returns the ambient rate in count per second (cps)
func (d *Device) AmbientRate() int32 {
return d.rangingData.ambientRateMCPS
}
// getRangingData stores in the buffer the ranging data
func (d *Device) getRangingData() {
d.rangingData.mm = uint16((uint32(d.results.mmCrosstalkSD0)*2011 + 0x0400) / 0x0800)
switch d.results.status {
case 17: // MULTCLIPFAIL
case 2: // VCSELWATCHDOGTESTFAILURE
case 1: // VCSELCONTINUITYTESTFAILURE
case 3: // NOVHVVALUEFOUND
d.rangingData.status = HardwareFail
break
case 13: // USERROICLIP
d.rangingData.status = MinRangeFail
break
case 18: // GPHSTREAMCOUNT0READY
d.rangingData.status = SynchronizationInt
break
case 5: // RANGEPHASECHECK
d.rangingData.status = OutOfBoundsFail
break
case 4: // MSRCNOTARGET
d.rangingData.status = SignalFail
break
case 6: // SIGMATHRESHOLDCHECK
d.rangingData.status = SignalFail
break
case 7: // PHASECONSISTENCY
d.rangingData.status = WrapTargetFail
break
case 12: // RANGEIGNORETHRESHOLD
d.rangingData.status = XtalkSignalFail
break
case 8: // MINCLIP
d.rangingData.status = RangeValidMinRangeClipped
break
case 9: // RANGECOMPLETE
if d.results.streamCount == 0 {
d.rangingData.status = RangeValidNoWrapCheckFail
} else {
d.rangingData.status = RangeValid
}
break
default:
d.rangingData.status = None
}
d.rangingData.signalRateMCPS = 1000000 * int32(d.results.signalRateCrosstalkMCPSSD0) / (1 << 7)
d.rangingData.ambientRateMCPS = 1000000 * int32(d.results.ambientRateMCPSSD0) / (1 << 7)
}
// setupManualCalibration configures the manual calibration
func (d *Device) setupManualCalibration() {
// save original VHV configs
d.VHVInit = d.readReg(VHV_CONFIG_INIT)
d.VHVTimeout = d.readReg(VHV_CONFIG_TIMEOUT_MACROP_LOOP_BOUND)
// disable VHV init
d.writeReg(VHV_CONFIG_INIT, d.VHVInit&0x7F)
// set loop bound to tuning param
d.writeReg(VHV_CONFIG_TIMEOUT_MACROP_LOOP_BOUND, (d.VHVTimeout&0x03)+(3<<2))
// override phasecal
d.writeReg(PHASECAL_CONFIG_OVERRIDE, 0x01)
d.writeReg(CAL_CONFIG_VCSEL_START, d.readReg(PHASECAL_RESULT_VCSEL_START))
}
// StartContinuous starts the continuous sensing mode
func (d *Device) StartContinuous(periodMs uint32) {
d.writeReg32Bit(SYSTEM_INTERMEASUREMENT_PERIOD, periodMs*uint32(d.oscillatorOffset))
d.writeReg(SYSTEM_INTERRUPT_CLEAR, 0x01) // sys_interrupt_clear_range
d.writeReg(SYSTEM_MODE_START, 0x40) // mode_range_timed
}
// StopContinuous stops the continuous sensing mode
func (d *Device) StopContinuous() {
d.writeReg(SYSTEM_MODE_START, 0x80) // mode_range_abort
d.calibrated = false
// restore vhv configs
if d.VHVInit != 0 {
d.writeReg(VHV_CONFIG_INIT, d.VHVInit)
}
if d.VHVTimeout != 0 {
d.writeReg(VHV_CONFIG_TIMEOUT_MACROP_LOOP_BOUND, d.VHVTimeout)
}
// remove phasecal override
d.writeReg(PHASECAL_CONFIG_OVERRIDE, 0x00)
}
// writeReg sends a single byte to the specified register address
func (d *Device) writeReg(reg uint16, value uint8) {
msb := byte((reg >> 8) & 0xFF)
lsb := byte(reg & 0xFF)
d.bus.Tx(d.Address, []byte{msb, lsb, value}, nil)
}
// writeReg16Bit sends two bytes to the specified register address
func (d *Device) writeReg16Bit(reg uint16, value uint16) {
data := make([]byte, 4)
data[0] = byte((reg >> 8) & 0xFF)
data[1] = byte(reg & 0xFF)
data[2] = byte((value >> 8) & 0xFF)
data[3] = byte(value & 0xFF)
d.bus.Tx(d.Address, data, nil)
}
// writeReg32Bit sends four bytes to the specified register address
func (d *Device) writeReg32Bit(reg uint16, value uint32) {
data := make([]byte, 6)
data[0] = byte((reg >> 8) & 0xFF)
data[1] = byte(reg & 0xFF)
data[2] = byte((value >> 24) & 0xFF)
data[3] = byte((value >> 16) & 0xFF)
data[4] = byte((value >> 8) & 0xFF)
data[5] = byte(value & 0xFF)
d.bus.Tx(d.Address, data, nil)
}
// readReg reads a single byte from the specified address
func (d *Device) readReg(reg uint16) uint8 {
data := []byte{0}
msb := byte((reg >> 8) & 0xFF)
lsb := byte(reg & 0xFF)
d.bus.Tx(d.Address, []byte{msb, lsb}, data)
return data[0]
}
// readReg16Bit reads two bytes from the specified address
// and returns it as a uint16
func (d *Device) readReg16Bit(reg uint16) uint16 {
data := []byte{0, 0}
msb := byte((reg >> 8) & 0xFF)
lsb := byte(reg & 0xFF)
d.bus.Tx(d.Address, []byte{msb, lsb}, data)
return readUint(data[0], data[1])
}
// readReg32Bit reads four bytes from the specified address
// and returns it as a uint32
func (d *Device) readReg32Bit(reg uint16) uint32 {
data := make([]byte, 4)
msb := byte((reg >> 8) & 0xFF)
lsb := byte(reg & 0xFF)
d.bus.Tx(d.Address, []byte{msb, lsb}, data)
return readUint32(data)
}
// readUint converts two bytes to uint16
func readUint(msb byte, lsb byte) uint16 {
return (uint16(msb) << 8) | uint16(lsb)
}
// readUint converts four bytes to uint32
func readUint32(data []byte) uint32 {
if len(data) != 4 {
return 0
}
var value uint32
value = uint32(data[0]) << 24
value |= uint32(data[1]) << 16
value |= uint32(data[2]) << 8
value |= uint32(data[3])
return value
}
// encodeTimeout encodes the timeout in the correct format: (LSByte * 2^MSByte) + 1
func encodeTimeout(timeoutMclks uint32) uint16 {
if timeoutMclks == 0 {
return 0
}
msb := 0
lsb := timeoutMclks - 1
for (lsb & 0xFFFFFF00) > 0 {
lsb >>= 1
msb++
}
return uint16(msb<<8) | uint16(lsb&0xFF)
}
// decodeTimeout decodes the timeout from the format: (LSByte * 2^MSByte) + 1
func decodeTimeout(regVal uint16) uint32 {
return (uint32(regVal&0xFF) << (regVal >> 8)) + 1
}
// timeoutMclksToMicroseconds transform from mclks to microseconds
func timeoutMclksToMicroseconds(timeoutMclks uint32, macroPeriodMicroseconds uint32) uint32 {
return uint32((uint64(timeoutMclks)*uint64(macroPeriodMicroseconds) + 0x800) >> 12)
}
// timeoutMicrosecondsToMclks transform from microseconds to mclks
func timeoutMicrosecondsToMclks(timeoutMicroseconds uint32, macroPeriodMicroseconds uint32) uint32 {
return ((timeoutMicroseconds << 12) + (macroPeriodMicroseconds >> 1)) / macroPeriodMicroseconds
}
// calculateMacroPerios calculates the macro period in microsendos from the vcsel period
func (d *Device) calculateMacroPeriod(vcselPeriod uint32) uint32 {
pplPeriodMicroseconds := (uint32(1) << 30) / uint32(d.fastOscillatorFreq)
vcselPeriodPclks := (vcselPeriod + 1) << 1
macroPeriodMicroseconds := 2304 * pplPeriodMicroseconds
macroPeriodMicroseconds >>= 6
macroPeriodMicroseconds *= vcselPeriodPclks
macroPeriodMicroseconds >>= 6
return macroPeriodMicroseconds
}
+1
View File
@@ -0,0 +1 @@
package waveshareepd // import "tinygo.org/x/drivers/waveshare-epd"
+278
View File
@@ -0,0 +1,278 @@
// Package epd2in13 implements a driver for Waveshare 2.13in black and white e-paper device.
//
// Datasheet: https://www.waveshare.com/w/upload/e/e6/2.13inch_e-Paper_Datasheet.pdf
//
package epd2in13 // import "tinygo.org/x/drivers/waveshare-epd/epd2in13"
import (
"errors"
"image/color"
"machine"
"time"
)
type Config struct {
Width int16
Height int16
}
type Device struct {
bus machine.SPI
cs machine.Pin
dc machine.Pin
rst machine.Pin
busy machine.Pin
width int16
height int16
buffer []uint8
bufferLength uint32
}
// Look up table for full updates
var lutFullUpdate = [30]uint8{
0x22, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x11,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
}
// Look up table for partial updates, faster but there will be some ghosting
var lutPartialUpdate = [30]uint8{
0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x0F, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
}
// New returns a new epd2in13x driver. Pass in a fully configured SPI bus.
func New(bus machine.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 = 128
}
if cfg.Height != 0 {
d.height = cfg.Height
} else {
d.height = 250
}
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.cs.Low()
d.dc.Low()
d.rst.Low()
d.Reset()
d.SendCommand(DRIVER_OUTPUT_CONTROL)
d.SendData(uint8((d.height - 1) & 0xFF))
d.SendData(uint8(((d.height - 1) >> 8) & 0xFF))
d.SendData(0x00) // GD = 0; SM = 0; TB = 0;
d.SendCommand(BOOSTER_SOFT_START_CONTROL)
d.SendData(0xD7)
d.SendData(0xD6)
d.SendData(0x9D)
d.SendCommand(WRITE_VCOM_REGISTER)
d.SendData(0xA8) // VCOM 7C
d.SendCommand(SET_DUMMY_LINE_PERIOD)
d.SendData(0x1A) // 4 dummy lines per gate
d.SendCommand(SET_GATE_TIME)
d.SendData(0x08) // 2us per line
d.SendCommand(DATA_ENTRY_MODE_SETTING)
d.SendData(0x03) // X increment; Y increment
d.SetLUT(true)
}
// Reset resets the device
func (d *Device) Reset() {
d.rst.Low()
time.Sleep(200 * time.Millisecond)
d.rst.High()
time.Sleep(200 * time.Millisecond)
}
// DeepSleep puts the display into deepsleep
func (d *Device) DeepSleep() {
d.SendCommand(DEEP_SLEEP_MODE)
d.WaitUntilIdle()
}
// 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()
}
// SetLUT sets the look up tables for full or partial updates
func (d *Device) SetLUT(fullUpdate bool) {
d.SendCommand(WRITE_LUT_REGISTER)
if fullUpdate {
for i := 0; i < 30; i++ {
d.SendData(lutFullUpdate[i])
}
} else {
for i := 0; i < 30; i++ {
d.SendData(lutPartialUpdate[i])
}
}
}
// 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) {
if x < 0 || x >= d.width || y < 0 || y >= d.height {
return
}
byteIndex := (x + 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 {
d.setMemoryArea(0, 0, d.width-1, d.height-1)
for j := int16(0); j < d.height; j++ {
d.setMemoryPointer(0, j)
d.SendCommand(WRITE_RAM)
for i := int16(0); i < d.width/8; i++ {
d.SendData(d.buffer[i+j*(d.width/8)])
}
}
d.SendCommand(DISPLAY_UPDATE_CONTROL_2)
d.SendData(0xC4)
d.SendCommand(MASTER_ACTIVATION)
d.SendCommand(TERMINATE_FRAME_READ_WRITE)
return nil
}
// DisplayRect sends only an area of the buffer to the screen.
func (d *Device) DisplayRect(x int16, y int16, width int16, height int16) error {
if x < 0 || y < 0 || x >= d.width || y >= d.height || width < 0 || height < 0 {
return errors.New("wrong rectangle")
}
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.setMemoryArea(x, y, width, height)
x = x / 8
width = width / 8
for ; y < height; y++ {
d.setMemoryPointer(8*x, y)
d.SendCommand(WRITE_RAM)
for i := int16(x); i < width; i++ {
d.SendData(d.buffer[i+y*d.width/8])
}
}
d.SendCommand(DISPLAY_UPDATE_CONTROL_2)
d.SendData(0xC4)
d.SendCommand(MASTER_ACTIVATION)
d.SendCommand(TERMINATE_FRAME_READ_WRITE)
return nil
}
// ClearDisplay erases the device SRAM
func (d *Device) ClearDisplay() {
d.setMemoryArea(0, 0, d.width-1, d.height-1)
d.setMemoryPointer(0, 0)
d.SendCommand(WRITE_RAM)
for i := uint32(0); i < d.bufferLength; i++ {
d.SendData(0xFF)
}
d.Display()
}
// setMemoryArea sets the area of the display that will be updated
func (d *Device) setMemoryArea(x0 int16, y0 int16, x1 int16, y1 int16) {
d.SendCommand(SET_RAM_X_ADDRESS_START_END_POSITION)
d.SendData(uint8((x0 >> 3) & 0xFF))
d.SendData(uint8((x1 >> 3) & 0xFF))
d.SendCommand(SET_RAM_Y_ADDRESS_START_END_POSITION)
d.SendData(uint8(y0 & 0xFF))
d.SendData(uint8((y0 >> 8) & 0xFF))
d.SendData(uint8(y1 & 0xFF))
d.SendData(uint8((y1 >> 8) & 0xFF))
}
// setMemoryPointer moves the internal pointer to the speficied coordinates
func (d *Device) setMemoryPointer(x int16, y int16) {
d.SendCommand(SET_RAM_X_ADDRESS_COUNTER)
d.SendData(uint8((x >> 3) & 0xFF))
d.SendCommand(SET_RAM_Y_ADDRESS_COUNTER)
d.SendData(uint8(y & 0xFF))
d.SendData(uint8((y >> 8) & 0xFF))
d.WaitUntilIdle()
}
// 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] = 0xFF
}
}
// Size returns the current size of the display.
func (d *Device) Size() (w, h int16) {
return d.width, d.height
}
+26
View File
@@ -0,0 +1,26 @@
package epd2in13
// Registers
const (
DRIVER_OUTPUT_CONTROL = 0x01
BOOSTER_SOFT_START_CONTROL = 0x0C
GATE_SCAN_START_POSITION = 0x0F
DEEP_SLEEP_MODE = 0x10
DATA_ENTRY_MODE_SETTING = 0x11
SW_RESET = 0x12
TEMPERATURE_SENSOR_CONTROL = 0x1A
MASTER_ACTIVATION = 0x20
DISPLAY_UPDATE_CONTROL_1 = 0x21
DISPLAY_UPDATE_CONTROL_2 = 0x22
WRITE_RAM = 0x24
WRITE_VCOM_REGISTER = 0x2C
WRITE_LUT_REGISTER = 0x32
SET_DUMMY_LINE_PERIOD = 0x3A
SET_GATE_TIME = 0x3B
BORDER_WAVEFORM_CONTROL = 0x3C
SET_RAM_X_ADDRESS_START_END_POSITION = 0x44
SET_RAM_Y_ADDRESS_START_END_POSITION = 0x45
SET_RAM_X_ADDRESS_COUNTER = 0x4E
SET_RAM_Y_ADDRESS_COUNTER = 0x4F
TERMINATE_FRAME_READ_WRITE = 0xFF
)
+301
View File
@@ -0,0 +1,301 @@
// Package epd2in13x implements a driver for Waveshare 2.13in (B & C versions) tri-color e-paper device.
//
// Datasheet: https://www.waveshare.com/w/upload/d/d3/2.13inch-e-paper-b-Specification.pdf
//
package epd2in13x // import "tinygo.org/x/drivers/waveshare-epd/epd2in13x"
import (
"errors"
"image/color"
"machine"
"time"
)
type Config struct {
Width int16
Height int16
NumColors uint8
}
type Device struct {
bus machine.SPI
cs machine.Pin
dc machine.Pin
rst machine.Pin
busy machine.Pin
width int16
height int16
buffer [][]uint8
bufferLength uint32
}
type Color uint8
// New returns a new epd2in13x driver. Pass in a fully configured SPI bus.
func New(bus machine.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 = 104
}
if cfg.Height != 0 {
d.height = cfg.Height
} else {
d.height = 212
}
if cfg.NumColors == 0 {
cfg.NumColors = 3
} else if cfg.NumColors == 1 {
cfg.NumColors = 2
}
d.bufferLength = (uint32(d.width) * uint32(d.height)) / 8
d.buffer = make([][]uint8, cfg.NumColors-1)
for i := range d.buffer {
d.buffer[i] = make([]uint8, d.bufferLength)
}
for i := range d.buffer {
for j := uint32(0); j < d.bufferLength; j++ {
d.buffer[i][j] = 0xFF
}
}
d.cs.Low()
d.dc.Low()
d.rst.Low()
d.Reset()
d.SendCommand(BOOSTER_SOFT_START)
d.SendData(0x17)
d.SendData(0x17)
d.SendData(0x17)
d.SendCommand(POWER_ON)
d.WaitUntilIdle()
d.SendCommand(PANEL_SETTING)
d.SendData(0x8F)
d.SendCommand(VCOM_AND_DATA_INTERVAL_SETTING)
d.SendData(0x37)
d.SendCommand(RESOLUTION_SETTING)
d.SendData(uint8(d.width))
d.SendData(0x00)
d.SendData(uint8(d.height))
}
// Reset resets the device
func (d *Device) Reset() {
d.rst.Low()
time.Sleep(200 * time.Millisecond)
d.rst.High()
time.Sleep(200 * time.Millisecond)
}
// DeepSleep puts the display into deepsleep
func (d *Device) DeepSleep() {
d.SendCommand(POWER_OFF)
d.WaitUntilIdle()
d.SendCommand(DEEP_SLEEP)
d.SendData(0xA5)
}
// 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 3 colors: black, white and a third color that could be red or yellow
// We use RGBA(0,0,0, 255) as white (transparent)
// RGBA(1-255,0,0,255) as colored (red or yellow)
// Anything else as black
func (d *Device) SetPixel(x int16, y int16, c color.RGBA) {
if x < 0 || x >= d.width || y < 0 || y >= d.height {
return
}
if c.R != 0 && c.G == 0 && c.B == 0 { // COLORED
d.SetEPDPixel(x, y, COLORED)
} else if c.G != 0 || c.B != 0 { // BLACK
d.SetEPDPixel(x, y, BLACK)
} else { // WHITE / EMPTY
d.SetEPDPixel(x, y, WHITE)
}
}
// SetEPDPixel modifies the internal buffer in a single pixel.
func (d *Device) SetEPDPixel(x int16, y int16, c Color) {
if x < 0 || x >= d.width || y < 0 || y >= d.height {
return
}
byteIndex := (x + y*d.width) / 8
if c == WHITE {
d.buffer[BLACK-1][byteIndex] |= 0x80 >> uint8(x%8)
d.buffer[COLORED-1][byteIndex] |= 0x80 >> uint8(x%8)
} else if c == COLORED {
d.buffer[BLACK-1][byteIndex] |= 0x80 >> uint8(x%8)
d.buffer[COLORED-1][byteIndex] &^= 0x80 >> uint8(x%8)
} else { // BLACK
d.buffer[COLORED-1][byteIndex] |= 0x80 >> uint8(x%8)
d.buffer[BLACK-1][byteIndex] &^= 0x80 >> uint8(x%8)
}
}
// Display sends the buffer (if any) to the screen.
func (d *Device) Display() error {
d.SendCommand(DATA_START_TRANSMISSION_1) // black
time.Sleep(2 * time.Millisecond)
for i := uint32(0); i < d.bufferLength; i++ {
d.SendData(d.buffer[BLACK-1][i])
}
time.Sleep(2 * time.Millisecond)
d.SendCommand(DATA_START_TRANSMISSION_2) // red
time.Sleep(2 * time.Millisecond)
for i := uint32(0); i < d.bufferLength; i++ {
d.SendData(d.buffer[COLORED-1][i])
}
time.Sleep(2 * time.Millisecond)
d.SendCommand(DISPLAY_REFRESH)
return nil
}
// SetDisplayRect sends a rectangle of data at specific coordinates to the device SRAM directly
func (d *Device) SetDisplayRect(buffer [][]uint8, x int16, y int16, w int16, h int16) error {
if w%8 != 0 {
return errors.New("rectangle width needs to be a multiple of 8")
}
for i := range buffer {
if int16(len(buffer[i])) < (w/8)*h {
return errors.New("buffer has the wrong size")
}
}
d.SendCommand(PARTIAL_IN)
d.SendCommand(PARTIAL_WINDOW)
d.SendData(uint8(x) & 0xF8)
d.SendData(((uint8(x) & 0xF8) + uint8(w) - 1) | 0x07)
d.SendData(uint8(y) >> 8)
d.SendData(uint8(y) & 0xFF)
d.SendData(uint8(y+h-1) >> 8)
d.SendData(uint8(y+h-1) & 0xFF)
d.SendData(0x01)
time.Sleep(2 * time.Millisecond)
d.SendCommand(DATA_START_TRANSMISSION_1)
for i := int16(0); i < (w/8)*h; i++ {
d.SendData(buffer[BLACK-1][i])
}
time.Sleep(2 * time.Millisecond)
if len(buffer) > 1 {
d.SendCommand(DATA_START_TRANSMISSION_2)
for i := int16(0); i < (w/8)*h; i++ {
d.SendData(buffer[COLORED-1][i])
}
time.Sleep(2 * time.Millisecond)
}
d.SendCommand(PARTIAL_OUT)
return nil
}
// SetDisplayRectColor sends a rectangle of data at specific coordinates to the device SRAM directly
func (d *Device) SetDisplayRectColor(buffer []uint8, x int16, y int16, w int16, h int16, c Color) error {
if w%8 != 0 {
return errors.New("rectangle width needs to be a multiple of 8")
}
if int16(len(buffer)) < (w/8)*h {
return errors.New("buffer has the wrong size")
}
if c == WHITE {
return errors.New("wrong color")
}
d.SendCommand(PARTIAL_IN)
d.SendCommand(PARTIAL_WINDOW)
d.SendData(uint8(x) & 0xF8)
d.SendData(((uint8(x) & 0xF8) + uint8(w) - 1) | 0x07)
d.SendData(uint8(y) >> 8)
d.SendData(uint8(y) & 0xFF)
d.SendData(uint8(y+h-1) >> 8)
d.SendData(uint8(y+h-1) & 0xFF)
d.SendData(0x01)
time.Sleep(2 * time.Millisecond)
if c == COLORED {
d.SendCommand(DATA_START_TRANSMISSION_2)
} else {
d.SendCommand(DATA_START_TRANSMISSION_1)
}
for i := int16(0); i < (w/8)*h; i++ {
d.SendData(buffer[i])
}
time.Sleep(2 * time.Millisecond)
d.SendCommand(PARTIAL_OUT)
return nil
}
// ClearDisplay erases the device SRAM
func (d *Device) ClearDisplay() {
d.SendCommand(DATA_START_TRANSMISSION_1) // black
time.Sleep(2 * time.Millisecond)
for i := uint32(0); i < d.bufferLength; i++ {
d.SendData(0xFF)
}
time.Sleep(2 * time.Millisecond)
d.SendCommand(DATA_START_TRANSMISSION_2) // red
time.Sleep(2 * time.Millisecond)
for i := uint32(0); i < d.bufferLength; i++ {
d.SendData(0xFF)
}
time.Sleep(2 * time.Millisecond)
}
// 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 := uint8(0); i < uint8(len(d.buffer)); i++ {
for j := uint32(0); j < d.bufferLength; j++ {
d.buffer[i][j] = 0xFF
}
}
}
// Size returns the current size of the display.
func (d *Device) Size() (w, h int16) {
return d.width, d.height
}
+46
View File
@@ -0,0 +1,46 @@
package epd2in13x
// Registers
const (
WHITE Color = 0
BLACK Color = 1
COLORED Color = 2 // In some board it's red in others yellow
PANEL_SETTING = 0x00
POWER_SETTING = 0x01
POWER_OFF = 0x02
POWER_OFF_SEQUENCE_SETTING = 0x03
POWER_ON = 0x04
POWER_ON_MEASURE = 0x05
BOOSTER_SOFT_START = 0x06
DEEP_SLEEP = 0x07
DATA_START_TRANSMISSION_1 = 0x10
DATA_STOP = 0x11
DISPLAY_REFRESH = 0x12
DATA_START_TRANSMISSION_2 = 0x13
VCOM_LUT = 0x20
W2W_LUT = 0x21
B2W_LUT = 0x22
W2B_LUT = 0x23
B2B_LUT = 0x24
PLL_CONTROL = 0x30
TEMPERATURE_SENSOR_CALIBRATION = 0x40
TEMPERATURE_SENSOR_SELECTION = 0x41
TEMPERATURE_SENSOR_WRITE = 0x42
TEMPERATURE_SENSOR_READ = 0x43
VCOM_AND_DATA_INTERVAL_SETTING = 0x50
LOW_POWER_DETECTION = 0x51
TCON_SETTING = 0x60
RESOLUTION_SETTING = 0x61
GET_STATUS = 0x71
AUTO_MEASURE_VCOM = 0x80
READ_VCOM_VALUE = 0x81
VCM_DC_SETTING = 0x82
PARTIAL_WINDOW = 0x90
PARTIAL_IN = 0x91
PARTIAL_OUT = 0x92
PROGRAM_MODE = 0xA0
ACTIVE_PROGRAM = 0xA1
READ_OTP_DATA = 0xA2
POWER_SAVING = 0xE3
)
+3 -3
View File
@@ -1,5 +1,5 @@
// Package ws2812 implements a driver for WS2812 and SK6812 RGB LED strips.
package ws2812
package ws2812 // import "tinygo.org/x/drivers/ws2812"
import (
"image/color"
@@ -8,12 +8,12 @@ import (
// Device wraps a pin object for an easy driver interface.
type Device struct {
Pin machine.GPIO
Pin machine.Pin
}
// New returns a new WS2812 driver. It does not touch the pin object: you have
// to configure it as an output pin before calling New.
func New(pin machine.GPIO) Device {
func New(pin machine.Pin) Device {
return Device{pin}
}
+10 -3
View File
@@ -14,6 +14,12 @@ func (d Device) WriteByte(c byte) error {
portSet, maskSet := d.Pin.PortMaskSet()
portClear, maskClear := d.Pin.PortMaskClear()
// See:
// https://wp.josh.com/2014/05/13/ws2812-neopixels-are-not-so-finicky-once-you-get-to-know-them/
// T0H: 4 cycles or 250ns
// T0L: 14 cycles or 875ns -> together 18 cycles or 1125ns
// T1H: 9 cycles or 562ns
// T1L: 8 cycles or 500ns -> together 17 cycles or 1062ns
avr.AsmFull(`
send_bit:
st {portSet}, {maskSet} ; [2] set output high
@@ -21,13 +27,14 @@ func (d Device) WriteByte(c byte) error {
brcs skip_store ; [1/2] branch if this bit is high (long pulse)
st {portClear}, {maskClear} ; [2] set output low (short pulse)
skip_store:
nop ; [6] wait before changing the output again
nop
nop
nop ; [4] wait before changing the output again
nop
nop
nop
st {portClear}, {maskClear} ; [2] set output low (end of pulse)
nop ; [3]
nop
nop
subi {i}, 1 ; [1] subtract one (for the loop)
brne send_bit ; [1/2] send the next bit, if not at the end of the loop
`, map[string]interface{}{
+15 -9
View File
@@ -15,23 +15,29 @@ func (d Device) WriteByte(c byte) error {
portSet, maskSet := d.Pin.PortMaskSet()
portClear, maskClear := d.Pin.PortMaskClear()
// See:
// https://wp.josh.com/2014/05/13/ws2812-neopixels-are-not-so-finicky-once-you-get-to-know-them/
// T0H: 4 cycles or 250ns
// T0L: 14 cycles or 875ns -> together 18 cycles or 1125ns
// T1H: 10 cycles or 625ns
// T1H: 8 cycles or 500ns -> together 18 cycles or 1125ns
value := uint32(c) << 24
arm.AsmFull(`
send_bit:
str {maskSet}, {portSet}
lsls {value}, #1
bcs.n skip_store
str {maskClear}, {portClear}
str {maskSet}, {portSet} @ [2] T0H and T0L start here
lsls {value}, #1 @ [1]
bcs.n skip_store @ [1/3]
str {maskClear}, {portClear} @ [2] T0H -> T0L transition
skip_store:
nop @ [4]
nop
nop
nop
str {maskClear}, {portClear} @ [2] T1H -> T1L transition
nop @ [2]
nop
nop
nop
str {maskClear}, {portClear}
subs {i}, #1
bne.n send_bit
subs {i}, #1 @ [1]
bne.n send_bit @ [1/3]
`, map[string]interface{}{
"value": value,
"i": 8,

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