mirror of
https://github.com/tinygo-org/drivers.git
synced 2026-07-28 11:38:41 +00:00
Compare commits
70 Commits
interfaces
..
v0.2.0
| Author | SHA1 | Date | |
|---|---|---|---|
| e4f6fbcb52 | |||
| 7487c6b3a2 | |||
| d1553458f5 | |||
| 79d3609f76 | |||
| 00a9b9db77 | |||
| f68388702d | |||
| 0722fe7161 | |||
| c09f0a8075 | |||
| 7757122598 | |||
| 93849555f3 | |||
| 3f946868e1 | |||
| 2c0bde0dad | |||
| 848385d625 | |||
| dce063e0c2 | |||
| 30b118cf63 | |||
| 9127719933 | |||
| a24657d1eb | |||
| c259da1f17 | |||
| 374d3f6bec | |||
| 5930c149d9 | |||
| d22cf7a3e1 | |||
| a9e5c8f710 | |||
| ad2b76be8b | |||
| ba4de1efb8 | |||
| 4560c61c61 | |||
| 715829cdbd | |||
| 8ac1fa2b22 | |||
| b7fe897b8c | |||
| 35a7590c2d | |||
| de22b8839e | |||
| 38e0d39f48 | |||
| 59db5fef76 | |||
| c0cadf8d82 | |||
| 720097ebad | |||
| 772a1044c4 | |||
| cd992bd7d6 | |||
| f6b9080f9e | |||
| 137e667d5a | |||
| cc391102ff | |||
| df3b3aa1ca | |||
| 84b1cd0bfb | |||
| 8e384a00f1 | |||
| 7006a675ad | |||
| 1895c84912 | |||
| 3c68510cbc | |||
| a332118087 | |||
| ed2d334d55 | |||
| f6bdc9734f | |||
| d9b426b90b | |||
| b8e278e08f | |||
| b0c4f3e2ce | |||
| c83475d09d | |||
| f144bfed79 | |||
| 8e1df6d63a | |||
| 239c10a6ab | |||
| 55d12ce0a1 | |||
| 7116778235 | |||
| 0f7b458d9c | |||
| 0c2924cd77 | |||
| ae53ea7d81 | |||
| 4ff437e5d4 | |||
| 1475a63798 | |||
| 693e7a1db7 | |||
| 31bc74101d | |||
| 7dfd72c1b1 | |||
| bb56d49ce3 | |||
| 96b5daee8e | |||
| be048a35bf | |||
| 3af2f18437 | |||
| 773b01e911 |
@@ -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
|
||||
@@ -0,0 +1 @@
|
||||
build
|
||||
@@ -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
|
||||
@@ -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
|
||||
```
|
||||
@@ -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.
|
||||
@@ -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
|
||||
@@ -0,0 +1,92 @@
|
||||
# TinyGo Drivers
|
||||
|
||||
[](https://godoc.org/tinygo.org/x/drivers) [](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.
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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
|
||||
)
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package at24cx
|
||||
|
||||
// The I2C address which this device listens to.
|
||||
const Address = 0x57
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
}
|
||||
@@ -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
|
||||
)
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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
|
||||
)
|
||||
@@ -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
@@ -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"
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
)
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
)
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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)
|
||||
}
|
||||
@@ -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
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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("")
|
||||
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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, " ")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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())
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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"))
|
||||
}
|
||||
@@ -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"))
|
||||
}
|
||||
@@ -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"))
|
||||
}
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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},
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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])
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
)
|
||||
@@ -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
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
@@ -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]))
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
)
|
||||
@@ -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
|
||||
)
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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
|
||||
)
|
||||
@@ -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
|
||||
}
|
||||
@@ -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 Steinhart–Hart 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
|
||||
}
|
||||
@@ -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
|
||||
)
|
||||
@@ -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
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
package waveshareepd // import "tinygo.org/x/drivers/waveshare-epd"
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
)
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
@@ -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}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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
Reference in New Issue
Block a user