mirror of
https://github.com/tinygo-org/drivers.git
synced 2026-07-26 10:38:41 +00:00
Compare commits
91 Commits
interfaces
...
v0.3.0
| Author | SHA1 | Date | |
|---|---|---|---|
| d64069a517 | |||
| e4b80d8e0e | |||
| 50633f3e86 | |||
| 7e78e2c998 | |||
| 40d9287ac4 | |||
| 59aece351a | |||
| 1b81b992c2 | |||
| 506819c93c | |||
| 1d9e393948 | |||
| 9c88d1fab4 | |||
| a267fdb8ce | |||
| 500f3d9813 | |||
| fe58e9b762 | |||
| d8675515bc | |||
| 28dbbe2e65 | |||
| 78fdaad9c0 | |||
| cb49783f18 | |||
| 8534e67c83 | |||
| 95755ebae7 | |||
| f4583f5144 | |||
| 1e59a3970e | |||
| 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,62 @@
|
||||
0.3.0
|
||||
---
|
||||
- **new devices**
|
||||
- Buzzer for piezo or small speaker
|
||||
- PDM MEMS microphone support using I2S interface
|
||||
- **enhancements**
|
||||
- epd2in13: added rotation
|
||||
- espat
|
||||
- add built-in support for MQTT publish using the Paho library packets, alongside some modifications needed for the AT protocol.
|
||||
- add DialTLS and Dial methods, update MQTT example to allow both MQTT and MQTTS connections
|
||||
- add example that uses MQTT publish to open server
|
||||
- add README with information on how to flash ESP32 or ESP8266 with AT command set firmware.
|
||||
- add ResolveUDPAddr and ResolveTCPAddr implementations using AT command for DNS lookup
|
||||
- change Response() method to use a passed-in timeout value instead of fixed pauses.
|
||||
- implement TCPConn using AT command set
|
||||
- improve error handling for key TCP functions
|
||||
- refactor net and tls interface compatible code into separate sub-packages
|
||||
- update MQTT example for greater stability
|
||||
- use only AT commands that work on both ESP8266 and ESP32
|
||||
- add documentation on how to use Arduino Nano33 IoT built-in WiFi NINA-W102 chip.
|
||||
- **bugfixes**
|
||||
- core: Error strings should not be capitalized (unless beginning with proper nouns or acronyms) or end with punctuation, since they are usually printed following other context.
|
||||
- docs: add note to current/future contributors to please start by opening a GH issue to avoid duplication of efforts
|
||||
- examples: typo in package name of examples
|
||||
- mpu6050: properly scale the outputs of the accel/gyro
|
||||
|
||||
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,40 @@
|
||||
# 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.
|
||||
|
||||
Please first open a Github issue. We want to help, and also make sure that there is no duplications of efforts. Sometimes what you need is already being worked on by someone else.
|
||||
|
||||
## 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,49 @@
|
||||
|
||||
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
|
||||
tinygo build -size short -o ./build/test.elf -target=circuitplay-express ./examples/microphone/main.go
|
||||
tinygo build -size short -o ./build/test.elf -target=circuitplay-express ./examples/buzzer/main.go
|
||||
|
||||
test: clean fmt-check smoke-test
|
||||
@@ -0,0 +1,94 @@
|
||||
# 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 |
|
||||
| [Buzzer](https://en.wikipedia.org/wiki/Buzzer#Piezoelectric) | GPIO |
|
||||
| [DS1307 real time clock](https://datasheets.maximintegrated.com/en/ds/DS1307.pdf) | I2C |
|
||||
| [DS3231 real time clock](https://datasheets.maximintegrated.com/en/ds/DS3231.pdf) | I2C |
|
||||
| ["Easystepper" stepper motor controller](https://en.wikipedia.org/wiki/Stepper_motor) | GPIO |
|
||||
| [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 |
|
||||
| [Microphone - PDM](https://cdn-learn.adafruit.com/assets/assets/000/049/977/original/MP34DT01-M.pdf) | I2S/PDM |
|
||||
| [MMA8653 accelerometer](https://www.nxp.com/docs/en/data-sheet/MMA8653FC.pdf) | I2C |
|
||||
| [MPU6050 accelerometer/gyroscope](https://store.invensense.com/datasheets/invensense/MPU-6050_DataSheet_V3%204.pdf) | I2C |
|
||||
| [PCD8544 display](http://eia.udg.edu/~forest/PCD8544_1.pdf) | SPI |
|
||||
| [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,71 @@
|
||||
// Package buzzer provides a very simplistic driver for a connected buzzer or low-fidelity speaker.
|
||||
//
|
||||
package buzzer // import "tinygo.org/x/drivers/buzzer"
|
||||
|
||||
import (
|
||||
"machine"
|
||||
|
||||
"time"
|
||||
)
|
||||
|
||||
// Device wraps a GPIO connection to a buzzer.
|
||||
type Device struct {
|
||||
pin machine.Pin
|
||||
High bool
|
||||
BPM float64
|
||||
}
|
||||
|
||||
// New returns a new buzzer driver given which pin to use
|
||||
func New(pin machine.Pin) Device {
|
||||
return Device{
|
||||
pin: pin,
|
||||
High: false,
|
||||
BPM: 96.0,
|
||||
}
|
||||
}
|
||||
|
||||
// On sets the buzzer to a high state.
|
||||
func (l *Device) On() (err error) {
|
||||
l.pin.Set(true)
|
||||
l.High = true
|
||||
return
|
||||
}
|
||||
|
||||
// Off sets the buzzer to a low state.
|
||||
func (l *Device) Off() (err error) {
|
||||
l.pin.Set(false)
|
||||
l.High = false
|
||||
return
|
||||
}
|
||||
|
||||
// Toggle sets the buzzer to the opposite of it's current state
|
||||
func (l *Device) Toggle() (err error) {
|
||||
if l.High {
|
||||
err = l.Off()
|
||||
} else {
|
||||
err = l.On()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Tone plays a tone of the requested frequency and duration.
|
||||
func (l *Device) Tone(hz, duration float64) (err error) {
|
||||
// calculation based off https://www.arduino.cc/en/Tutorial/Melody
|
||||
tone := (1.0 / (2.0 * hz)) * 1000000.0
|
||||
|
||||
tempo := ((60 / l.BPM) * (duration * 1000))
|
||||
|
||||
for i := 0.0; i < tempo*1000; i += tone * 2.0 {
|
||||
if err = l.On(); err != nil {
|
||||
return
|
||||
}
|
||||
time.Sleep(time.Duration(tone) * time.Microsecond)
|
||||
|
||||
if err = l.Off(); err != nil {
|
||||
return
|
||||
}
|
||||
time.Sleep(time.Duration(tone) * time.Microsecond)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
+121
@@ -0,0 +1,121 @@
|
||||
package buzzer
|
||||
|
||||
const (
|
||||
Whole = 4
|
||||
Half = 2
|
||||
Quarter = 1
|
||||
Eighth = 0.500
|
||||
)
|
||||
|
||||
// The values seem like they are little off, so feel free to make corrections, as needed.
|
||||
const (
|
||||
Rest = 0
|
||||
C0 = 16.35
|
||||
Db0 = 17.32
|
||||
D0 = 18.35
|
||||
Eb0 = 19.45
|
||||
E0 = 20.60
|
||||
F0 = 21.83
|
||||
Gb0 = 23.12
|
||||
G0 = 24.50
|
||||
Ab0 = 25.96
|
||||
A0 = 27.50
|
||||
Bb0 = 29.14
|
||||
B0 = 30.87
|
||||
C1 = 32.70
|
||||
Db1 = 34.65
|
||||
D1 = 36.71
|
||||
Eb1 = 38.89
|
||||
E1 = 41.20
|
||||
F1 = 43.65
|
||||
Gb1 = 46.25
|
||||
G1 = 49.00
|
||||
Ab1 = 51.91
|
||||
A1 = 55.00
|
||||
Bb1 = 58.27
|
||||
B1 = 61.74
|
||||
C2 = 65.41
|
||||
Db2 = 69.30
|
||||
D2 = 73.42
|
||||
Eb2 = 77.78
|
||||
E2 = 82.41
|
||||
F2 = 87.31
|
||||
Gb2 = 92.50
|
||||
G2 = 98.00
|
||||
Ab2 = 103.83
|
||||
A2 = 110.00
|
||||
Bb2 = 116.54
|
||||
B2 = 123.47
|
||||
C3 = 130.81
|
||||
Db3 = 138.59
|
||||
D3 = 146.83
|
||||
Eb3 = 155.56
|
||||
E3 = 164.81
|
||||
F3 = 174.61
|
||||
Gb3 = 185.00
|
||||
G3 = 196.00
|
||||
Ab3 = 207.65
|
||||
A3 = 220.00
|
||||
Bb3 = 233.08
|
||||
B3 = 246.94
|
||||
C4 = 261.63
|
||||
Db4 = 277.18
|
||||
D4 = 293.66
|
||||
Eb4 = 311.13
|
||||
E4 = 329.63
|
||||
F4 = 349.23
|
||||
Gb4 = 369.99
|
||||
G4 = 392.00
|
||||
Ab4 = 415.30
|
||||
A4 = 440.00
|
||||
Bb4 = 466.16
|
||||
B4 = 493.88
|
||||
C5 = 523.25
|
||||
Db5 = 554.37
|
||||
D5 = 587.33
|
||||
Eb5 = 622.25
|
||||
E5 = 659.25
|
||||
F5 = 698.46
|
||||
Gb5 = 739.99
|
||||
G5 = 783.99
|
||||
Ab5 = 830.61
|
||||
A5 = 880.00
|
||||
Bb5 = 932.33
|
||||
B5 = 987.77
|
||||
C6 = 1046.50
|
||||
Db6 = 1108.73
|
||||
D6 = 1174.66
|
||||
Eb6 = 1244.51
|
||||
E6 = 1318.51
|
||||
F6 = 1396.91
|
||||
Gb6 = 1479.98
|
||||
G6 = 1567.98
|
||||
Ab6 = 1661.22
|
||||
A6 = 1760.00
|
||||
Bb6 = 1864.66
|
||||
B6 = 1975.53
|
||||
C7 = 2093.00
|
||||
Db7 = 2217.46
|
||||
D7 = 2349.32
|
||||
Eb7 = 2489.02
|
||||
E7 = 2637.02
|
||||
F7 = 2793.83
|
||||
Gb7 = 2959.96
|
||||
G7 = 3135.96
|
||||
Ab7 = 3322.44
|
||||
A7 = 3520.00
|
||||
Bb7 = 3729.31
|
||||
B7 = 3951.07
|
||||
C8 = 4186.01
|
||||
Db8 = 4434.92
|
||||
D8 = 4698.63
|
||||
Eb8 = 4978.03
|
||||
E8 = 5274.04
|
||||
F8 = 5587.65
|
||||
Gb8 = 5919.91
|
||||
G8 = 6271.93
|
||||
Ab8 = 6644.88
|
||||
A8 = 7040.00
|
||||
Bb8 = 7458.62
|
||||
B8 = 7902.13
|
||||
)
|
||||
@@ -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,82 @@
|
||||
# ESP-AT Driver
|
||||
|
||||
This package provides a driver to use a separate connected WiFi processor either the ESP8266 or the ESP32 from Espressif.
|
||||
|
||||
The way this driver works is by using the UART interface to communicate with the WiFi chip using the Espressif AT command set.
|
||||
|
||||
## ESP-AT Firmware Installation
|
||||
|
||||
In order to use this driver, you must have the ESP-AT firmware installed on the ESP8266/ESP32 chip.
|
||||
|
||||
### Installing on Arduino Nano33 IoT
|
||||
|
||||
In order to install the needed firmware on the Arduino Nano33 IoT board's built-in NINA W102 chip, you will need to use the `arduino-nano33-iot` branch of this fork of the firmware:
|
||||
|
||||
https://github.com/hybridgroup/esp32-at
|
||||
|
||||
To flash this firmware on the Arduino Nano33 IoT you will need to follow the following procedure:
|
||||
|
||||
- Using the normal Arduino software, load the `SerialNINAPassthrough` sketch on to the board.
|
||||
- Flash the NINA 102 firmware using the `make flash` command in the https://github.com/hybridgroup/esp32-at repo.
|
||||
|
||||
You only need to do this one time, and then the correct ESP-AT firmware will be on the NINA chip, and you can just flash the Arduino Nano33 IoT board using TinyGo. We should be able to remove some of these step in a future release of this software.
|
||||
|
||||
### Installing on ESP32
|
||||
|
||||
The official repository for the ESP-AT for the ESP32 processor is located here:
|
||||
|
||||
https://github.com/espressif/esp32-at
|
||||
|
||||
Your best option is to follow the instructions in the official repo.
|
||||
|
||||
### Installing on ESP8266
|
||||
|
||||
The official repository for the AT command set firmware for the ESP8266 processor is located here:
|
||||
|
||||
https://github.com/espressif/ESP8266_NONOS_SDK
|
||||
|
||||
First clone the repo:
|
||||
|
||||
```shell
|
||||
git clone https://github.com/espressif/ESP8266_NONOS_SDK.git
|
||||
```
|
||||
|
||||
You will also need to install the Espressif `esptool` to flash this firmware on your ESP8266:
|
||||
|
||||
https://github.com/espressif/esptool
|
||||
|
||||
Once you have obtained the binary code, and installed `esptool`, you can flash the ESP8266.
|
||||
|
||||
Here is an example shell script that flashes a Wemos D1 Mini board:
|
||||
|
||||
|
||||
```python
|
||||
#!/bin/sh
|
||||
SPToolDir="$HOME/.local/lib/python2.7/site-packages"
|
||||
FirmwareDir="$HOME/Development/ESP8266_NONOS_SDK"
|
||||
cd "$SPToolDir"
|
||||
port=/dev/ttyUSB0
|
||||
if [ ! -c $port ]; then
|
||||
port=/dev/ttyUSB0
|
||||
fi
|
||||
if [ ! -c $port ]; then
|
||||
echo "No device appears to be plugged in. Stopping."
|
||||
fi
|
||||
printf "Writing AT firmware to the Wemos D1 Mini in 3..."
|
||||
sleep 1; printf "2..."
|
||||
sleep 1; printf "1..."
|
||||
sleep 1; echo "done."
|
||||
echo "Erasing the flash first"
|
||||
esptool.py --port $port erase_flash
|
||||
esptool.py --port /dev/ttyUSB0 --baud 115200 \
|
||||
write_flash -fm dio -ff 20m -fs detect \
|
||||
0x0000 "$FirmwareDir/bin/boot_v1.7.bin" \
|
||||
0x01000 "$FirmwareDir/bin/at/512+512/user1.1024.new.2.bin" \
|
||||
0x3fc000 "$FirmwareDir/bin/esp_init_data_default_v05.bin" \
|
||||
0x7e000 "$FirmwareDir/bin/blank.bin" \
|
||||
0x3fe000 "$FirmwareDir/bin/blank.bin"
|
||||
|
||||
echo "Check the boot by typing: miniterm $port 74800"
|
||||
echo " and then resetting. Use Ctrl-] to quit miniterm."
|
||||
|
||||
```
|
||||
@@ -0,0 +1,111 @@
|
||||
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.
|
||||
// On the ESP8266 the settings will not be saved in flash memory, so they will be forgotten on next reset.
|
||||
// On the ESP32 the settings WILL be saved in flash memory, so they will be used on next reset.
|
||||
SoftAPConfigCurrent = "+CWSAP"
|
||||
|
||||
// Set softAP configuration. This also activates the ESP8266/ESP32 to act as an access point.
|
||||
// On the ESP8266 the settings will not be saved in flash memory, so they will be forgotten on next reset.
|
||||
// On the ESP32 the settings WILL be saved in flash memory, so they will be used on next reset.
|
||||
SoftAPConfigFlash = "+CWSAP"
|
||||
|
||||
// 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.
|
||||
// On the ESP8266 the IP address will not be saved in flash memory, so it will be forgotten on next reset.
|
||||
// On the ESP32 the IP address WILL be saved in flash memory, so it will be used on next reset.
|
||||
SetSoftAPIPCurrent = "+CIPAP"
|
||||
|
||||
// Set IP address of ESP8266/ESP32 when acting as access point.
|
||||
// On the ESP8266 the IP address will not be saved in flash memory, so it will be forgotten on next reset.
|
||||
// On the ESP32 the IP address WILL be saved in flash memory, so it will be used on next reset.
|
||||
SetSoftAPIPFlash = "+CIPAP"
|
||||
)
|
||||
|
||||
// TCP/IP commands
|
||||
const (
|
||||
// Get connection status
|
||||
TCPStatus = "+CIPSTATUS"
|
||||
|
||||
// Establish TCP connection or register UDP port
|
||||
TCPConnect = "+CIPSTART"
|
||||
|
||||
// DNS Lookup
|
||||
TCPDNSLookup = "+CIPDOMAIN"
|
||||
|
||||
// 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"
|
||||
)
|
||||
+259
@@ -0,0 +1,259 @@
|
||||
// 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
|
||||
}
|
||||
|
||||
// ActiveDevice is the currently configured Device in use. There can only be one.
|
||||
var ActiveDevice *Device
|
||||
|
||||
// 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() {
|
||||
ActiveDevice = &d
|
||||
}
|
||||
|
||||
// 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(100)
|
||||
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(100)
|
||||
}
|
||||
|
||||
// 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(100)
|
||||
}
|
||||
|
||||
// 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(100)
|
||||
}
|
||||
|
||||
// ReadSocket returns the data that has already been read in from the responses.
|
||||
func (d *Device) ReadSocket(b []byte) (n int, err error) {
|
||||
// make sure no data in buffer
|
||||
d.Response(100)
|
||||
|
||||
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.
|
||||
// The call will retry for up to timeout milliseconds before returning nothing.
|
||||
func (d *Device) Response(timeout int) []byte {
|
||||
var i int
|
||||
pause := 10 // pause to wait for 10 ms
|
||||
retries := timeout / pause
|
||||
|
||||
header := make([]byte, 2)
|
||||
for {
|
||||
for d.bus.Buffered() > 0 {
|
||||
// get the first 2 bytes
|
||||
header[0], _ = d.bus.ReadByte()
|
||||
header[1], _ = d.bus.ReadByte()
|
||||
|
||||
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 == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
// pause to make sure is no more data to be read
|
||||
time.Sleep(time.Duration(pause) * time.Millisecond)
|
||||
}
|
||||
return d.response[:i]
|
||||
}
|
||||
|
||||
func (d *Device) isLeadingCRLF(b []byte) bool {
|
||||
if len(b) < 2 {
|
||||
return false
|
||||
}
|
||||
if b[0] == 13 && b[1] == 10 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (d *Device) 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
|
||||
}
|
||||
@@ -0,0 +1,188 @@
|
||||
package mqtt
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/eclipse/paho.mqtt.golang/packets"
|
||||
"tinygo.org/x/drivers/espat"
|
||||
"tinygo.org/x/drivers/espat/net"
|
||||
"tinygo.org/x/drivers/espat/tls"
|
||||
)
|
||||
|
||||
// NewClient will create an MQTT v3.1.1 client with all of the options specified
|
||||
// in the provided ClientOptions. The client must have the Connect method called
|
||||
// on it before it may be used. This is to make sure resources (such as a net
|
||||
// connection) are created before the application is actually ready.
|
||||
func NewClient(o *ClientOptions) Client {
|
||||
c := &mqttclient{opts: o, adaptor: o.Adaptor}
|
||||
return c
|
||||
}
|
||||
|
||||
type mqttclient struct {
|
||||
adaptor *espat.Device
|
||||
conn net.Conn
|
||||
connected bool
|
||||
opts *ClientOptions
|
||||
mid uint16
|
||||
}
|
||||
|
||||
// AddRoute allows you to add a handler for messages on a specific topic
|
||||
// without making a subscription. For example having a different handler
|
||||
// for parts of a wildcard subscription
|
||||
func (c *mqttclient) AddRoute(topic string, callback MessageHandler) {
|
||||
return
|
||||
}
|
||||
|
||||
// IsConnected returns a bool signifying whether
|
||||
// the client is connected or not.
|
||||
func (c *mqttclient) IsConnected() bool {
|
||||
return c.connected
|
||||
}
|
||||
|
||||
// IsConnectionOpen return a bool signifying whether the client has an active
|
||||
// connection to mqtt broker, i.e not in disconnected or reconnect mode
|
||||
func (c *mqttclient) IsConnectionOpen() bool {
|
||||
return c.connected
|
||||
}
|
||||
|
||||
// Connect will create a connection to the message broker.
|
||||
func (c *mqttclient) Connect() Token {
|
||||
var err error
|
||||
|
||||
// make connection
|
||||
if strings.Contains(c.opts.Servers, "ssl://") {
|
||||
url := strings.TrimPrefix(c.opts.Servers, "ssl://")
|
||||
c.conn, err = tls.Dial("tcp", url, nil)
|
||||
if err != nil {
|
||||
return &mqtttoken{err: err}
|
||||
}
|
||||
} else if strings.Contains(c.opts.Servers, "tcp://") {
|
||||
url := strings.TrimPrefix(c.opts.Servers, "tcp://")
|
||||
c.conn, err = net.Dial("tcp", url)
|
||||
if err != nil {
|
||||
return &mqtttoken{err: err}
|
||||
}
|
||||
} else {
|
||||
// invalid protocol
|
||||
return &mqtttoken{err: errors.New("invalid protocol")}
|
||||
}
|
||||
|
||||
// send the MQTT connect message
|
||||
connectPkt := packets.NewControlPacket(packets.Connect).(*packets.ConnectPacket)
|
||||
connectPkt.Qos = 0
|
||||
if c.opts.Username != "" {
|
||||
connectPkt.Username = c.opts.Username
|
||||
connectPkt.UsernameFlag = true
|
||||
}
|
||||
|
||||
if c.opts.Password != "" {
|
||||
connectPkt.Password = []byte(c.opts.Password)
|
||||
connectPkt.PasswordFlag = true
|
||||
}
|
||||
|
||||
connectPkt.ClientIdentifier = c.opts.ClientID //"tinygo-client-" + randomString(10)
|
||||
connectPkt.ProtocolVersion = byte(c.opts.ProtocolVersion)
|
||||
connectPkt.ProtocolName = "MQTT"
|
||||
connectPkt.Keepalive = 30
|
||||
|
||||
err = connectPkt.Write(c.conn)
|
||||
if err != nil {
|
||||
return &mqtttoken{err: err}
|
||||
}
|
||||
|
||||
// TODO: handle timeout
|
||||
for {
|
||||
packet, _ := packets.ReadPacket(c.conn)
|
||||
|
||||
if packet != nil {
|
||||
ack, ok := packet.(*packets.ConnackPacket)
|
||||
if ok {
|
||||
if ack.ReturnCode == 0 {
|
||||
// success
|
||||
return &mqtttoken{}
|
||||
}
|
||||
// otherwise something went wrong
|
||||
return &mqtttoken{err: errors.New(packet.String())}
|
||||
}
|
||||
}
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
}
|
||||
|
||||
c.connected = true
|
||||
return &mqtttoken{}
|
||||
}
|
||||
|
||||
// Disconnect will end the connection with the server, but not before waiting
|
||||
// the specified number of milliseconds to wait for existing work to be
|
||||
// completed.
|
||||
func (c *mqttclient) Disconnect(quiesce uint) {
|
||||
c.conn.Close()
|
||||
return
|
||||
}
|
||||
|
||||
// Publish will publish a message with the specified QoS and content
|
||||
// to the specified topic.
|
||||
// Returns a token to track delivery of the message to the broker
|
||||
func (c *mqttclient) Publish(topic string, qos byte, retained bool, payload interface{}) Token {
|
||||
pub := packets.NewControlPacket(packets.Publish).(*packets.PublishPacket)
|
||||
pub.Qos = qos
|
||||
pub.TopicName = topic
|
||||
switch payload.(type) {
|
||||
case string:
|
||||
pub.Payload = []byte(payload.(string))
|
||||
case []byte:
|
||||
pub.Payload = payload.([]byte)
|
||||
default:
|
||||
return &mqtttoken{err: errors.New("Unknown payload type")}
|
||||
}
|
||||
pub.MessageID = c.mid
|
||||
c.mid++
|
||||
|
||||
err := pub.Write(c.conn)
|
||||
return &mqtttoken{err: err}
|
||||
}
|
||||
|
||||
// Subscribe starts a new subscription. Provide a MessageHandler to be executed when
|
||||
// a message is published on the topic provided.
|
||||
func (c *mqttclient) Subscribe(topic string, qos byte, callback MessageHandler) Token {
|
||||
return &mqtttoken{}
|
||||
}
|
||||
|
||||
// SubscribeMultiple starts a new subscription for multiple topics. Provide a MessageHandler to
|
||||
// be executed when a message is published on one of the topics provided.
|
||||
func (c *mqttclient) SubscribeMultiple(filters map[string]byte, callback MessageHandler) Token {
|
||||
return &mqtttoken{}
|
||||
}
|
||||
|
||||
// Unsubscribe will end the subscription from each of the topics provided.
|
||||
// Messages published to those topics from other clients will no longer be
|
||||
// received.
|
||||
func (c *mqttclient) Unsubscribe(topics ...string) Token {
|
||||
return &mqtttoken{}
|
||||
}
|
||||
|
||||
// OptionsReader returns a ClientOptionsReader which is a copy of the clientoptions
|
||||
// in use by the client.
|
||||
func (c *mqttclient) OptionsReader() ClientOptionsReader {
|
||||
r := ClientOptionsReader{}
|
||||
return r
|
||||
}
|
||||
|
||||
type mqtttoken struct {
|
||||
err error
|
||||
}
|
||||
|
||||
func (t *mqtttoken) Wait() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (t *mqtttoken) WaitTimeout(time.Duration) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (t *mqtttoken) Error() error {
|
||||
return t.err
|
||||
}
|
||||
@@ -0,0 +1,245 @@
|
||||
// The following code is a slightly modified version of code taken from the Paho MQTT library.
|
||||
// It is here until TinyGo can compile the "net" package from the standard library, at which time
|
||||
// it can be removed.
|
||||
|
||||
/*
|
||||
* Copyright (c) 2013 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Seth Hoenig
|
||||
* Allan Stockdill-Mander
|
||||
* Mike Robertson
|
||||
*/
|
||||
|
||||
// Portions copyright © 2018 TIBCO Software Inc.
|
||||
|
||||
package mqtt
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/espat"
|
||||
)
|
||||
|
||||
const (
|
||||
disconnected uint32 = iota
|
||||
connecting
|
||||
reconnecting
|
||||
connected
|
||||
)
|
||||
|
||||
// Client is the interface definition for a Client as used by this
|
||||
// library, the interface is primarily to allow mocking tests.
|
||||
//
|
||||
// It is an MQTT v3.1.1 client for communicating
|
||||
// with an MQTT server using non-blocking methods that allow work
|
||||
// to be done in the background.
|
||||
// An application may connect to an MQTT server using:
|
||||
// A plain TCP socket
|
||||
// A secure SSL/TLS socket
|
||||
// A websocket
|
||||
// To enable ensured message delivery at Quality of Service (QoS) levels
|
||||
// described in the MQTT spec, a message persistence mechanism must be
|
||||
// used. This is done by providing a type which implements the Store
|
||||
// interface. For convenience, FileStore and MemoryStore are provided
|
||||
// implementations that should be sufficient for most use cases. More
|
||||
// information can be found in their respective documentation.
|
||||
// Numerous connection options may be specified by configuring a
|
||||
// and then supplying a ClientOptions type.
|
||||
type Client interface {
|
||||
// IsConnected returns a bool signifying whether
|
||||
// the client is connected or not.
|
||||
IsConnected() bool
|
||||
// IsConnectionOpen return a bool signifying wether the client has an active
|
||||
// connection to mqtt broker, i.e not in disconnected or reconnect mode
|
||||
IsConnectionOpen() bool
|
||||
// Connect will create a connection to the message broker, by default
|
||||
// it will attempt to connect at v3.1.1 and auto retry at v3.1 if that
|
||||
// fails
|
||||
Connect() Token
|
||||
// Disconnect will end the connection with the server, but not before waiting
|
||||
// the specified number of milliseconds to wait for existing work to be
|
||||
// completed.
|
||||
Disconnect(quiesce uint)
|
||||
// Publish will publish a message with the specified QoS and content
|
||||
// to the specified topic.
|
||||
// Returns a token to track delivery of the message to the broker
|
||||
Publish(topic string, qos byte, retained bool, payload interface{}) Token
|
||||
// Subscribe starts a new subscription. Provide a MessageHandler to be executed when
|
||||
// a message is published on the topic provided, or nil for the default handler
|
||||
Subscribe(topic string, qos byte, callback MessageHandler) Token
|
||||
// SubscribeMultiple starts a new subscription for multiple topics. Provide a MessageHandler to
|
||||
// be executed when a message is published on one of the topics provided, or nil for the
|
||||
// default handler
|
||||
SubscribeMultiple(filters map[string]byte, callback MessageHandler) Token
|
||||
// Unsubscribe will end the subscription from each of the topics provided.
|
||||
// Messages published to those topics from other clients will no longer be
|
||||
// received.
|
||||
Unsubscribe(topics ...string) Token
|
||||
// AddRoute allows you to add a handler for messages on a specific topic
|
||||
// without making a subscription. For example having a different handler
|
||||
// for parts of a wildcard subscription
|
||||
AddRoute(topic string, callback MessageHandler)
|
||||
// OptionsReader returns a ClientOptionsReader which is a copy of the clientoptions
|
||||
// in use by the client.
|
||||
OptionsReader() ClientOptionsReader
|
||||
}
|
||||
|
||||
// Token defines the interface for the tokens used to indicate when
|
||||
// actions have completed.
|
||||
type Token interface {
|
||||
Wait() bool
|
||||
WaitTimeout(time.Duration) bool
|
||||
Error() error
|
||||
}
|
||||
|
||||
// MessageHandler is a callback type which can be set to be
|
||||
// executed upon the arrival of messages published to topics
|
||||
// to which the client is subscribed.
|
||||
type MessageHandler func(Client, Message)
|
||||
|
||||
// Message defines the externals that a message implementation must support
|
||||
// these are received messages that are passed to the callbacks, not internal
|
||||
// messages
|
||||
type Message interface {
|
||||
Duplicate() bool
|
||||
Qos() byte
|
||||
Retained() bool
|
||||
Topic() string
|
||||
MessageID() uint16
|
||||
Payload() []byte
|
||||
Ack()
|
||||
}
|
||||
|
||||
type message struct {
|
||||
duplicate bool
|
||||
qos byte
|
||||
retained bool
|
||||
topic string
|
||||
messageID uint16
|
||||
payload []byte
|
||||
ack func()
|
||||
}
|
||||
|
||||
func (m *message) Duplicate() bool {
|
||||
return m.duplicate
|
||||
}
|
||||
|
||||
func (m *message) Qos() byte {
|
||||
return m.qos
|
||||
}
|
||||
|
||||
func (m *message) Retained() bool {
|
||||
return m.retained
|
||||
}
|
||||
|
||||
func (m *message) Topic() string {
|
||||
return m.topic
|
||||
}
|
||||
|
||||
func (m *message) MessageID() uint16 {
|
||||
return m.messageID
|
||||
}
|
||||
|
||||
func (m *message) Payload() []byte {
|
||||
return m.payload
|
||||
}
|
||||
|
||||
func (m *message) Ack() {
|
||||
return
|
||||
}
|
||||
|
||||
// ClientOptionsReader provides an interface for reading ClientOptions after the client has been initialized.
|
||||
type ClientOptionsReader struct {
|
||||
options *ClientOptions
|
||||
}
|
||||
|
||||
// ClientOptions contains configurable options for an MQTT Client.
|
||||
type ClientOptions struct {
|
||||
Adaptor *espat.Device
|
||||
|
||||
//Servers []*url.URL
|
||||
Servers string
|
||||
ClientID string
|
||||
Username string
|
||||
Password string
|
||||
//CredentialsProvider CredentialsProvider
|
||||
CleanSession bool
|
||||
Order bool
|
||||
WillEnabled bool
|
||||
WillTopic string
|
||||
WillPayload []byte
|
||||
WillQos byte
|
||||
WillRetained bool
|
||||
ProtocolVersion uint
|
||||
protocolVersionExplicit bool
|
||||
//TLSConfig *tls.Config
|
||||
KeepAlive int64
|
||||
PingTimeout time.Duration
|
||||
ConnectTimeout time.Duration
|
||||
MaxReconnectInterval time.Duration
|
||||
AutoReconnect bool
|
||||
//Store Store
|
||||
//DefaultPublishHandler MessageHandler
|
||||
//OnConnect OnConnectHandler
|
||||
//OnConnectionLost ConnectionLostHandler
|
||||
WriteTimeout time.Duration
|
||||
MessageChannelDepth uint
|
||||
ResumeSubs bool
|
||||
//HTTPHeaders http.Header
|
||||
}
|
||||
|
||||
// NewClientOptions returns a new ClientOptions struct.
|
||||
func NewClientOptions(adaptor *espat.Device) *ClientOptions {
|
||||
return &ClientOptions{Adaptor: adaptor, ProtocolVersion: 4}
|
||||
}
|
||||
|
||||
// AddBroker adds a broker URI to the list of brokers to be used. The format should be
|
||||
// scheme://host:port
|
||||
// Where "scheme" is one of "tcp", "ssl", or "ws", "host" is the ip-address (or hostname)
|
||||
// and "port" is the port on which the broker is accepting connections.
|
||||
//
|
||||
// Default values for hostname is "127.0.0.1", for schema is "tcp://".
|
||||
//
|
||||
// An example broker URI would look like: tcp://foobar.com:1883
|
||||
func (o *ClientOptions) AddBroker(server string) *ClientOptions {
|
||||
if len(server) > 0 && server[0] == ':' {
|
||||
server = "127.0.0.1" + server
|
||||
}
|
||||
if !strings.Contains(server, "://") {
|
||||
server = "tcp://" + server
|
||||
}
|
||||
|
||||
o.Servers = server
|
||||
return o
|
||||
}
|
||||
|
||||
// SetClientID will set the client id to be used by this client when
|
||||
// connecting to the MQTT broker. According to the MQTT v3.1 specification,
|
||||
// a client id mus be no longer than 23 characters.
|
||||
func (o *ClientOptions) SetClientID(id string) *ClientOptions {
|
||||
o.ClientID = id
|
||||
return o
|
||||
}
|
||||
|
||||
// SetUsername will set the username to be used by this client when connecting
|
||||
// to the MQTT broker. Note: without the use of SSL/TLS, this information will
|
||||
// be sent in plaintext accross the wire.
|
||||
func (o *ClientOptions) SetUsername(u string) *ClientOptions {
|
||||
o.Username = u
|
||||
return o
|
||||
}
|
||||
|
||||
// SetPassword will set the password to be used by this client when connecting
|
||||
// to the MQTT broker. Note: without the use of SSL/TLS, this information will
|
||||
// be sent in plaintext accross the wire.
|
||||
func (o *ClientOptions) SetPassword(p string) *ClientOptions {
|
||||
o.Password = p
|
||||
return o
|
||||
}
|
||||
@@ -0,0 +1,391 @@
|
||||
// package net is intended to provide compatible interfaces with the
|
||||
// Go standard library's net package.
|
||||
package net
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/espat"
|
||||
)
|
||||
|
||||
// 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 DialUDP(network string, laddr, raddr *UDPAddr) (*UDPSerialConn, error) {
|
||||
addr := raddr.IP.String()
|
||||
sendport := strconv.Itoa(raddr.Port)
|
||||
listenport := strconv.Itoa(laddr.Port)
|
||||
|
||||
// disconnect any old socket
|
||||
espat.ActiveDevice.DisconnectSocket()
|
||||
|
||||
// connect new socket
|
||||
espat.ActiveDevice.ConnectUDPSocket(addr, sendport, listenport)
|
||||
|
||||
return &UDPSerialConn{SerialConn: SerialConn{Adaptor: espat.ActiveDevice}, laddr: laddr, raddr: raddr}, nil
|
||||
}
|
||||
|
||||
// ListenUDP listens for UDP connections on the port listed in laddr.
|
||||
func ListenUDP(network string, laddr *UDPAddr) (*UDPSerialConn, error) {
|
||||
addr := "0"
|
||||
sendport := "0"
|
||||
listenport := strconv.Itoa(laddr.Port)
|
||||
|
||||
// disconnect any old socket
|
||||
espat.ActiveDevice.DisconnectSocket()
|
||||
|
||||
// connect new socket
|
||||
espat.ActiveDevice.ConnectUDPSocket(addr, sendport, listenport)
|
||||
|
||||
return &UDPSerialConn{SerialConn: SerialConn{Adaptor: espat.ActiveDevice}, laddr: laddr}, nil
|
||||
}
|
||||
|
||||
// DialTCP makes a TCP 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 DialTCP(network string, laddr, raddr *TCPAddr) (*TCPSerialConn, error) {
|
||||
addr := raddr.IP.String()
|
||||
sendport := strconv.Itoa(raddr.Port)
|
||||
|
||||
// disconnect any old socket
|
||||
espat.ActiveDevice.DisconnectSocket()
|
||||
|
||||
// connect new socket
|
||||
espat.ActiveDevice.ConnectTCPSocket(addr, sendport)
|
||||
|
||||
return &TCPSerialConn{SerialConn: SerialConn{Adaptor: espat.ActiveDevice}, laddr: laddr, raddr: raddr}, nil
|
||||
}
|
||||
|
||||
// Dial connects to the address on the named network.
|
||||
// It tries to provide a mostly compatible interface
|
||||
// to net.Dial().
|
||||
func Dial(network, address string) (Conn, error) {
|
||||
switch network {
|
||||
case "tcp":
|
||||
raddr, err := ResolveTCPAddr(network, address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c, e := DialTCP(network, &TCPAddr{}, raddr)
|
||||
return c.opConn(), e
|
||||
case "udp":
|
||||
raddr, err := ResolveUDPAddr(network, address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c, e := DialUDP(network, &UDPAddr{}, raddr)
|
||||
return c.opConn(), e
|
||||
default:
|
||||
return nil, errors.New("invalid network for dial")
|
||||
}
|
||||
}
|
||||
|
||||
// SerialConn is a loosely net.Conn compatible implementation
|
||||
type SerialConn struct {
|
||||
Adaptor *espat.Device
|
||||
}
|
||||
|
||||
// UDPSerialConn is a loosely net.Conn compatible intended to support
|
||||
// UDP over serial.
|
||||
type UDPSerialConn struct {
|
||||
SerialConn
|
||||
laddr *UDPAddr
|
||||
raddr *UDPAddr
|
||||
}
|
||||
|
||||
// NewUDPSerialConn returns a new UDPSerialConn/
|
||||
func NewUDPSerialConn(c SerialConn, laddr, raddr *UDPAddr) *UDPSerialConn {
|
||||
return &UDPSerialConn{SerialConn: c, raddr: raddr}
|
||||
}
|
||||
|
||||
// TCPSerialConn is a loosely net.Conn compatible intended to support
|
||||
// TCP over serial.
|
||||
type TCPSerialConn struct {
|
||||
SerialConn
|
||||
laddr *TCPAddr
|
||||
raddr *TCPAddr
|
||||
}
|
||||
|
||||
// NewTCPSerialConn returns a new TCPSerialConn/
|
||||
func NewTCPSerialConn(c SerialConn, laddr, raddr *TCPAddr) *TCPSerialConn {
|
||||
return &TCPSerialConn{SerialConn: c, raddr: raddr}
|
||||
}
|
||||
|
||||
// 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 *UDPSerialConn) LocalAddr() Addr {
|
||||
return c.laddr.opAddr()
|
||||
}
|
||||
|
||||
// RemoteAddr returns the remote network address.
|
||||
func (c *UDPSerialConn) RemoteAddr() Addr {
|
||||
return c.laddr.opAddr()
|
||||
}
|
||||
|
||||
func (c *UDPSerialConn) opConn() Conn {
|
||||
if c == nil {
|
||||
return nil
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
// LocalAddr returns the local network address.
|
||||
func (c *TCPSerialConn) LocalAddr() Addr {
|
||||
return c.laddr.opAddr()
|
||||
}
|
||||
|
||||
// RemoteAddr returns the remote network address.
|
||||
func (c *TCPSerialConn) RemoteAddr() Addr {
|
||||
return c.laddr.opAddr()
|
||||
}
|
||||
|
||||
func (c *TCPSerialConn) opConn() Conn {
|
||||
if c == nil {
|
||||
return nil
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// ResolveTCPAddr returns an address of TCP end point.
|
||||
//
|
||||
// The network must be a TCP network name.
|
||||
//
|
||||
func ResolveTCPAddr(network, address string) (*TCPAddr, error) {
|
||||
// TODO: make sure network is 'tcp'
|
||||
// separate domain from port, if any
|
||||
r := strings.Split(address, ":")
|
||||
addr, err := espat.ActiveDevice.GetDNS(r[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ip := IP(addr)
|
||||
if len(r) > 1 {
|
||||
port, e := strconv.Atoi(r[1])
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
return &TCPAddr{IP: ip, Port: port}, nil
|
||||
}
|
||||
return &TCPAddr{IP: ip}, nil
|
||||
}
|
||||
|
||||
// ResolveUDPAddr returns an address of UDP end point.
|
||||
//
|
||||
// The network must be a UDP network name.
|
||||
//
|
||||
func ResolveUDPAddr(network, address string) (*UDPAddr, error) {
|
||||
// TODO: make sure network is 'udp'
|
||||
// separate domain from port, if any
|
||||
r := strings.Split(address, ":")
|
||||
addr, err := espat.ActiveDevice.GetDNS(r[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ip := IP(addr)
|
||||
if len(r) > 1 {
|
||||
port, e := strconv.Atoi(r[1])
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
return &UDPAddr{IP: ip, Port: port}, nil
|
||||
}
|
||||
|
||||
return &UDPAddr{IP: ip}, 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
|
||||
}
|
||||
|
||||
// Network returns the address's network name, "udp".
|
||||
func (a *UDPAddr) Network() string { return "udp" }
|
||||
|
||||
func (a *UDPAddr) String() string {
|
||||
if a == nil {
|
||||
return "<nil>"
|
||||
}
|
||||
if a.Port != 0 {
|
||||
return a.IP.String() + ":" + strconv.Itoa(a.Port)
|
||||
}
|
||||
return a.IP.String()
|
||||
}
|
||||
|
||||
func (a *UDPAddr) opAddr() Addr {
|
||||
if a == nil {
|
||||
return nil
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// TCPAddr here to serve as compatible type. until TinyGo can compile the net package.
|
||||
type TCPAddr struct {
|
||||
IP IP
|
||||
Port int
|
||||
Zone string // IPv6 scoped addressing zone
|
||||
}
|
||||
|
||||
// Network returns the address's network name, "tcp".
|
||||
func (a *TCPAddr) Network() string { return "tcp" }
|
||||
|
||||
func (a *TCPAddr) String() string {
|
||||
if a == nil {
|
||||
return "<nil>"
|
||||
}
|
||||
if a.Port != 0 {
|
||||
return a.IP.String() + ":" + strconv.Itoa(a.Port)
|
||||
}
|
||||
return a.IP.String()
|
||||
}
|
||||
|
||||
func (a *TCPAddr) opAddr() Addr {
|
||||
if a == nil {
|
||||
return nil
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
// Conn is a generic stream-oriented network connection.
|
||||
// This interface is from the Go standard library.
|
||||
type Conn interface {
|
||||
// Read reads data from the connection.
|
||||
// Read can be made to time out and return an Error with Timeout() == true
|
||||
// after a fixed time limit; see SetDeadline and SetReadDeadline.
|
||||
Read(b []byte) (n int, err error)
|
||||
|
||||
// Write writes data to the connection.
|
||||
// Write can be made to time out and return an Error with Timeout() == true
|
||||
// after a fixed time limit; see SetDeadline and SetWriteDeadline.
|
||||
Write(b []byte) (n int, err error)
|
||||
|
||||
// Close closes the connection.
|
||||
// Any blocked Read or Write operations will be unblocked and return errors.
|
||||
Close() error
|
||||
|
||||
// LocalAddr returns the local network address.
|
||||
LocalAddr() Addr
|
||||
|
||||
// RemoteAddr returns the remote network address.
|
||||
RemoteAddr() Addr
|
||||
|
||||
// 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.
|
||||
SetDeadline(t time.Time) error
|
||||
|
||||
// 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.
|
||||
SetReadDeadline(t time.Time) error
|
||||
|
||||
// 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.
|
||||
SetWriteDeadline(t time.Time) error
|
||||
}
|
||||
|
||||
// Addr represents a network end point address.
|
||||
type Addr interface {
|
||||
Network() string // name of the network (for example, "tcp", "udp")
|
||||
String() string // string form of address (for example, "192.0.2.1:25", "[2001:db8::1]:80")
|
||||
}
|
||||
+125
@@ -0,0 +1,125 @@
|
||||
package espat
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
TCPMuxSingle = 0
|
||||
TCPMuxMultiple = 1
|
||||
|
||||
TCPTransferModeNormal = 0
|
||||
TCPTransferModeUnvarnished = 1
|
||||
)
|
||||
|
||||
// GetDNS returns the IP address for a domain name.
|
||||
func (d *Device) GetDNS(domain string) (string, error) {
|
||||
d.Set(TCPDNSLookup, "\""+domain+"\"")
|
||||
r := strings.Split(string(d.Response(1000)), ":")
|
||||
if len(r) != 2 {
|
||||
return "", errors.New("Invalid domain lookup result")
|
||||
}
|
||||
res := strings.Split(r[1], "\r\n")
|
||||
return res[0], nil
|
||||
}
|
||||
|
||||
// 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 + ",120"
|
||||
d.Set(TCPConnect, val)
|
||||
r := d.Response(1000)
|
||||
if strings.Contains(string(r), "OK") {
|
||||
return nil
|
||||
}
|
||||
return errors.New("ConnectTCPSocket error:" + string(r))
|
||||
}
|
||||
|
||||
// ConnectUDPSocket creates a new UDP connection for the ESP8266/ESP32.
|
||||
func (d *Device) ConnectUDPSocket(addr, sendport, listenport string) error {
|
||||
protocol := "UDP"
|
||||
val := "\"" + protocol + "\",\"" + addr + "\"," + sendport + "," + listenport + ",2"
|
||||
d.Set(TCPConnect, val)
|
||||
r := d.Response(pause)
|
||||
if strings.Contains(string(r), "OK") {
|
||||
return nil
|
||||
}
|
||||
return errors.New("ConnectUDPSocket error:" + string(r))
|
||||
}
|
||||
|
||||
// ConnectSSLSocket creates a new SSL socket connection for the ESP8266/ESP32.
|
||||
// Currently only supports single connection mode.
|
||||
func (d *Device) ConnectSSLSocket(addr, port string) error {
|
||||
protocol := "SSL"
|
||||
val := "\"" + protocol + "\",\"" + addr + "\"," + port + ",120"
|
||||
d.Set(TCPConnect, val)
|
||||
// this operation takes longer, so wait up to 6 seconds to complete.
|
||||
r := d.Response(6000)
|
||||
if strings.Contains(string(r), "CONNECT") {
|
||||
return nil
|
||||
}
|
||||
return errors.New("ConnectSSLSocket error:" + string(r))
|
||||
}
|
||||
|
||||
// DisconnectSocket disconnects the ESP8266/ESP32 from the current TCP/UDP connection.
|
||||
func (d *Device) DisconnectSocket() error {
|
||||
d.Execute(TCPClose)
|
||||
d.Response(pause)
|
||||
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)
|
||||
d.Response(pause)
|
||||
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(pause), 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)
|
||||
d.Response(pause)
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetTCPTransferMode returns the ESP8266/ESP32 current client TCP/UDP transfer mode.
|
||||
func (d *Device) GetTCPTransferMode() []byte {
|
||||
d.Query(TransmissionMode)
|
||||
return d.Response(pause)
|
||||
}
|
||||
|
||||
// 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)
|
||||
|
||||
// when ">" is received, it indicates
|
||||
// ready to receive data
|
||||
r := d.Response(pause)
|
||||
if strings.Contains(string(r), ">") {
|
||||
return nil
|
||||
}
|
||||
return errors.New("StartSocketSend error:" + string(r))
|
||||
}
|
||||
|
||||
// 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("+++"))
|
||||
|
||||
d.Response(pause)
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
// Package tls is intended to provide a minimal set of compatible interfaces with the
|
||||
// Go standard library's tls package.
|
||||
package tls
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"tinygo.org/x/drivers/espat"
|
||||
"tinygo.org/x/drivers/espat/net"
|
||||
)
|
||||
|
||||
// Dial makes a TLS network connection. It tries to provide a mostly compatible interface
|
||||
// to tls.Dial().
|
||||
// Dial connects to the given network address.
|
||||
func Dial(network, address string, config *Config) (*net.TCPSerialConn, error) {
|
||||
raddr, err := net.ResolveTCPAddr(network, address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
addr := raddr.IP.String()
|
||||
sendport := strconv.Itoa(raddr.Port)
|
||||
|
||||
// disconnect any old socket
|
||||
espat.ActiveDevice.DisconnectSocket()
|
||||
|
||||
// connect new socket
|
||||
err = espat.ActiveDevice.ConnectSSLSocket(addr, sendport)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return net.NewTCPSerialConn(net.SerialConn{Adaptor: espat.ActiveDevice}, nil, raddr), nil
|
||||
}
|
||||
|
||||
// Config is a placeholder for future compatibility with
|
||||
// tls.Config.
|
||||
type Config struct {
|
||||
}
|
||||
+144
@@ -0,0 +1,144 @@
|
||||
package espat
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
)
|
||||
|
||||
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(100)
|
||||
}
|
||||
|
||||
// SetWifiMode sets the ESP8266/ESP32 wifi mode.
|
||||
func (d *Device) SetWifiMode(mode int) error {
|
||||
val := strconv.Itoa(mode)
|
||||
d.Set(WifiMode, val)
|
||||
d.Response(pause)
|
||||
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(100)
|
||||
}
|
||||
|
||||
// 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)
|
||||
d.Response(ws * 1000)
|
||||
return nil
|
||||
}
|
||||
|
||||
// DisconnectFromAP disconnects the ESP8266/ESP32 from the current access point.
|
||||
func (d *Device) DisconnectFromAP() error {
|
||||
d.Execute(Disconnect)
|
||||
d.Response(1000)
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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(100))
|
||||
}
|
||||
|
||||
// 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)
|
||||
d.Response(500)
|
||||
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(100))
|
||||
}
|
||||
|
||||
// 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)
|
||||
d.Response(1000)
|
||||
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(100))
|
||||
}
|
||||
|
||||
// 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(100))
|
||||
}
|
||||
|
||||
// SetAPIP sets the ESP8266/ESP32 current IP addess when configured as an Access Point.
|
||||
func (d *Device) SetAPIP(ipaddr string) error {
|
||||
val := "\"" + ipaddr + "\""
|
||||
d.Set(SetSoftAPIPCurrent, val)
|
||||
d.Response(500)
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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(100))
|
||||
}
|
||||
|
||||
// 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)
|
||||
d.Response(1000)
|
||||
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(100))
|
||||
}
|
||||
|
||||
// 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)
|
||||
d.Response(500)
|
||||
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,40 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/buzzer"
|
||||
)
|
||||
|
||||
type note struct {
|
||||
tone float64
|
||||
duration float64
|
||||
}
|
||||
|
||||
func main() {
|
||||
speaker := machine.PA30
|
||||
speaker.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
speaker.Set(true)
|
||||
|
||||
bzrPin := machine.A0
|
||||
bzrPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
|
||||
bzr := buzzer.New(bzrPin)
|
||||
|
||||
song := []note{
|
||||
{buzzer.C3, buzzer.Quarter},
|
||||
{buzzer.D3, buzzer.Quarter},
|
||||
{buzzer.E3, buzzer.Quarter},
|
||||
{buzzer.F3, buzzer.Quarter},
|
||||
{buzzer.G3, buzzer.Quarter},
|
||||
{buzzer.A3, buzzer.Quarter},
|
||||
{buzzer.B3, buzzer.Quarter},
|
||||
{buzzer.C3, buzzer.Quarter},
|
||||
}
|
||||
|
||||
for _, val := range song {
|
||||
bzr.Tone(val.tone, val.duration)
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
@@ -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,127 @@
|
||||
// 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])
|
||||
|
||||
// display response
|
||||
console.Write(adaptor.Response(100))
|
||||
|
||||
// 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() {
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
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,105 @@
|
||||
// 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"
|
||||
"tinygo.org/x/drivers/espat/net"
|
||||
)
|
||||
|
||||
// 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
|
||||
|
||||
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() {
|
||||
println("Connected to wifi adaptor.")
|
||||
adaptor.Echo(false)
|
||||
|
||||
if actAsAP {
|
||||
provideAP()
|
||||
} else {
|
||||
connectToAP()
|
||||
}
|
||||
} else {
|
||||
println("Unable to connect to wifi adaptor.")
|
||||
return
|
||||
}
|
||||
|
||||
// now make UDP connection
|
||||
laddr := &net.UDPAddr{Port: 2222}
|
||||
println("Loading UDP listener...")
|
||||
conn, _ := net.ListenUDP("UDP", laddr)
|
||||
|
||||
println("Waiting for data...")
|
||||
data := make([]byte, 50)
|
||||
blink := true
|
||||
for {
|
||||
n, _ := conn.Read(data)
|
||||
if n > 0 {
|
||||
println(string(data[: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...
|
||||
println("Disconnecting UDP...")
|
||||
conn.Close()
|
||||
println("Done.")
|
||||
}
|
||||
|
||||
// connect to access point
|
||||
func connectToAP() {
|
||||
println("Connecting to wifi network...")
|
||||
adaptor.SetWifiMode(espat.WifiModeClient)
|
||||
adaptor.ConnectToAP(ssid, pass, 10)
|
||||
println("Connected.")
|
||||
println(adaptor.GetClientIP())
|
||||
}
|
||||
|
||||
// provide access point
|
||||
func provideAP() {
|
||||
println("Starting wifi network as access point:")
|
||||
println(ssid)
|
||||
adaptor.SetWifiMode(espat.WifiModeAP)
|
||||
adaptor.SetAPConfig(ssid, pass, 7, espat.WifiAPSecurityWPA2_PSK)
|
||||
println("Ready.")
|
||||
println(adaptor.GetAPIP())
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
// 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"
|
||||
"tinygo.org/x/drivers/espat/net"
|
||||
)
|
||||
|
||||
// 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
|
||||
|
||||
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() {
|
||||
println("Connected to wifi adaptor.")
|
||||
adaptor.Echo(false)
|
||||
|
||||
connectToAP()
|
||||
} else {
|
||||
println("Unable to connect to wifi adaptor.")
|
||||
return
|
||||
}
|
||||
|
||||
// now make UDP connection
|
||||
ip := net.ParseIP(hubIP)
|
||||
raddr := &net.UDPAddr{IP: ip, Port: 2222}
|
||||
laddr := &net.UDPAddr{Port: 2222}
|
||||
|
||||
println("Dialing UDP connection...")
|
||||
conn, _ := net.DialUDP("udp", laddr, raddr)
|
||||
|
||||
for {
|
||||
// send data
|
||||
println("Sending data...")
|
||||
conn.Write([]byte("hello\r\n"))
|
||||
|
||||
time.Sleep(1000 * time.Millisecond)
|
||||
}
|
||||
|
||||
// Right now this code is never reached. Need a way to trigger it...
|
||||
println("Disconnecting UDP...")
|
||||
conn.Close()
|
||||
println("Done.")
|
||||
}
|
||||
|
||||
// connect to access point
|
||||
func connectToAP() {
|
||||
println("Connecting to wifi network...")
|
||||
adaptor.SetWifiMode(espat.WifiModeClient)
|
||||
adaptor.ConnectToAP(ssid, pass, 10)
|
||||
println("Connected.")
|
||||
println(adaptor.GetClientIP())
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
// This is a sensor station that uses a ESP8266 or ESP32 running on the device UART1.
|
||||
// It creates an MQTT connection that publishes a message every second
|
||||
// to an MQTT broker.
|
||||
//
|
||||
// In other words:
|
||||
// Your computer <--> UART0 <--> MCU <--> UART1 <--> ESP8266 <--> Internet <--> MQTT broker.
|
||||
//
|
||||
// You must install the Paho MQTT package to build this program:
|
||||
//
|
||||
// go get -u github.com/eclipse/paho.mqtt.golang
|
||||
//
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
"math/rand"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/espat"
|
||||
"tinygo.org/x/drivers/espat/mqtt"
|
||||
)
|
||||
|
||||
// access point info
|
||||
const ssid = "YOURSSID"
|
||||
const pass = "YOURPASS"
|
||||
|
||||
// IP address of the MQTT broker to use. Replace with your own info.
|
||||
//const server = "tcp://test.mosquitto.org:1883"
|
||||
const server = "ssl://test.mosquitto.org:8883"
|
||||
|
||||
// change these to connect to a different UART or pins for the ESP8266/ESP32
|
||||
var (
|
||||
uart = machine.UART1
|
||||
tx = machine.D10
|
||||
rx = machine.D11
|
||||
|
||||
console = machine.UART0
|
||||
|
||||
adaptor *espat.Device
|
||||
topic = "tinygo"
|
||||
)
|
||||
|
||||
func main() {
|
||||
time.Sleep(3000 * time.Millisecond)
|
||||
|
||||
uart.Configure(machine.UARTConfig{TX: tx, RX: rx})
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
|
||||
// Init esp8266/esp32
|
||||
adaptor = espat.New(uart)
|
||||
adaptor.Configure()
|
||||
|
||||
// first check if connected
|
||||
if connectToESP() {
|
||||
println("Connected to wifi adaptor.")
|
||||
adaptor.Echo(false)
|
||||
|
||||
connectToAP()
|
||||
} else {
|
||||
println("")
|
||||
failMessage("Unable to connect to wifi adaptor.")
|
||||
return
|
||||
}
|
||||
|
||||
opts := mqtt.NewClientOptions(adaptor)
|
||||
opts.AddBroker(server).SetClientID("tinygo-client-" + randomString(10))
|
||||
|
||||
println("Connectng to MQTT...")
|
||||
cl := mqtt.NewClient(opts)
|
||||
if token := cl.Connect(); token.Wait() && token.Error() != nil {
|
||||
failMessage(token.Error().Error())
|
||||
}
|
||||
|
||||
for {
|
||||
println("Publishing MQTT message...")
|
||||
data := []byte("{\"e\":[{ \"n\":\"hello\", \"v\":101 }]}")
|
||||
token := cl.Publish(topic, 0, false, data)
|
||||
token.Wait()
|
||||
if token.Error() != nil {
|
||||
println(token.Error().Error())
|
||||
}
|
||||
|
||||
time.Sleep(1000 * time.Millisecond)
|
||||
}
|
||||
|
||||
// Right now this code is never reached. Need a way to trigger it...
|
||||
println("Disconnecting MQTT...")
|
||||
cl.Disconnect(100)
|
||||
|
||||
println("Done.")
|
||||
}
|
||||
|
||||
// connect to ESP8266/ESP32
|
||||
func connectToESP() bool {
|
||||
for i := 0; i < 5; i++ {
|
||||
println("Connecting to wifi adaptor...")
|
||||
if adaptor.Connected() {
|
||||
return true
|
||||
}
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// connect to access point
|
||||
func connectToAP() {
|
||||
println("Connecting to wifi network...")
|
||||
|
||||
adaptor.SetWifiMode(espat.WifiModeClient)
|
||||
adaptor.ConnectToAP(ssid, pass, 10)
|
||||
|
||||
println("Connected.")
|
||||
println(adaptor.GetClientIP())
|
||||
}
|
||||
|
||||
// Returns an int >= min, < max
|
||||
func randomInt(min, max int) int {
|
||||
return min + rand.Intn(max-min)
|
||||
}
|
||||
|
||||
// Generate a random string of A-Z chars with len = l
|
||||
func randomString(len int) string {
|
||||
bytes := make([]byte, len)
|
||||
for i := 0; i < len; i++ {
|
||||
bytes[i] = byte(randomInt(65, 90))
|
||||
}
|
||||
return string(bytes)
|
||||
}
|
||||
|
||||
func failMessage(msg string) {
|
||||
for {
|
||||
println(msg)
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
// 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"
|
||||
"tinygo.org/x/drivers/espat/net"
|
||||
)
|
||||
|
||||
// access point info
|
||||
const ssid = "YOURSSID"
|
||||
const pass = "YOURPASS"
|
||||
|
||||
// IP address of the server aka "hub". Replace with your own info.
|
||||
const serverIP = "0.0.0.0"
|
||||
|
||||
// change these to connect to a different UART or pins for the ESP8266/ESP32
|
||||
var (
|
||||
uart = machine.UART1
|
||||
tx = machine.PA22
|
||||
rx = machine.PA23
|
||||
|
||||
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() {
|
||||
println("Connected to wifi adaptor.")
|
||||
adaptor.Echo(false)
|
||||
|
||||
connectToAP()
|
||||
} else {
|
||||
println("Unable to connect to wifi adaptor.")
|
||||
return
|
||||
}
|
||||
|
||||
// now make TCP connection
|
||||
ip := net.ParseIP(serverIP)
|
||||
raddr := &net.TCPAddr{IP: ip, Port: 8080}
|
||||
laddr := &net.TCPAddr{Port: 8080}
|
||||
|
||||
println("Dialing TCP connection...")
|
||||
conn, _ := net.DialTCP("tcp", laddr, raddr)
|
||||
|
||||
for {
|
||||
// send data
|
||||
println("Sending data...")
|
||||
conn.Write([]byte("hello\r\n"))
|
||||
|
||||
time.Sleep(1000 * time.Millisecond)
|
||||
}
|
||||
|
||||
// Right now this code is never reached. Need a way to trigger it...
|
||||
println("Disconnecting TCP...")
|
||||
conn.Close()
|
||||
println("Done.")
|
||||
}
|
||||
|
||||
// connect to access point
|
||||
func connectToAP() {
|
||||
println("Connecting to wifi network...")
|
||||
adaptor.SetWifiMode(espat.WifiModeClient)
|
||||
adaptor.ConnectToAP(ssid, pass, 10)
|
||||
println("Connected.")
|
||||
println(adaptor.GetClientIP())
|
||||
}
|
||||
@@ -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,37 @@
|
||||
// Example using the i2s hardware interface on the Adafruit Circuit Playground Express
|
||||
// to read data from the onboard MEMS microphone.
|
||||
//
|
||||
// Uses ideas from the https://github.com/adafruit/Adafruit_CircuitPlayground repo.
|
||||
//
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
|
||||
"tinygo.org/x/drivers/microphone"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultSampleRate = 22000
|
||||
quantizeSteps = 64
|
||||
msForSPLSample = 50
|
||||
defaultSampleCountForSPL = (defaultSampleRate / 1000) * msForSPLSample
|
||||
)
|
||||
|
||||
func main() {
|
||||
machine.I2S0.Configure(machine.I2SConfig{
|
||||
Mode: machine.I2SModePDM,
|
||||
AudioFrequency: defaultSampleRate * quantizeSteps / 16,
|
||||
ClockSource: machine.I2SClockSourceExternal,
|
||||
Stereo: true,
|
||||
})
|
||||
|
||||
mic := microphone.New(machine.I2S0)
|
||||
mic.SampleCountForSPL = defaultSampleCountForSPL
|
||||
mic.Configure()
|
||||
|
||||
for {
|
||||
spl, maxval := mic.GetSoundPressure()
|
||||
println("C", spl, "max", maxval)
|
||||
}
|
||||
}
|
||||
@@ -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 main
|
||||
|
||||
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 main
|
||||
|
||||
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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
// Package microphone implements a driver for a PDM microphone.
|
||||
// For example, the Adafruit PDM MEMS breakout board (https://www.adafruit.com/product/3492)
|
||||
//
|
||||
// Datasheet: https://cdn-learn.adafruit.com/assets/assets/000/049/977/original/MP34DT01-M.pdf
|
||||
//
|
||||
package microphone // import "tinygo.org/x/drivers/microphone"
|
||||
|
||||
import (
|
||||
"machine"
|
||||
"math"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultSampleRate = 22000
|
||||
quantizeSteps = 64
|
||||
msForSPLSample = 50
|
||||
defaultSampleCountForSPL = (defaultSampleRate / 1000) * msForSPLSample
|
||||
defaultGain = 9.0
|
||||
defaultRefLevel = 0.00002
|
||||
)
|
||||
|
||||
// Device wraps an I2S connection to a PDM microphone device.
|
||||
type Device struct {
|
||||
bus machine.I2S
|
||||
|
||||
// data buffer used for SPL sound pressure level samples
|
||||
data []int32
|
||||
|
||||
// buf buffer used for sinc filter
|
||||
buf []uint32
|
||||
|
||||
// SampleCountForSPL is number of samples aka size of data buffer to be used
|
||||
// for sound pressure level measurement.
|
||||
// Once Configure() is called, changing this value has no effect.
|
||||
SampleCountForSPL int
|
||||
|
||||
// Gain setting used to calculate sound pressure level
|
||||
Gain float64
|
||||
|
||||
// ReferenceLevel setting used to calculate sound pressure level.
|
||||
ReferenceLevel float64
|
||||
}
|
||||
|
||||
// New creates a new microphone connection. The I2S bus must already be
|
||||
// configured.
|
||||
//
|
||||
// This function only creates the Device object, it does not touch the device.
|
||||
func New(bus machine.I2S) Device {
|
||||
return Device{
|
||||
bus: bus,
|
||||
SampleCountForSPL: defaultSampleCountForSPL,
|
||||
Gain: defaultGain,
|
||||
ReferenceLevel: defaultRefLevel,
|
||||
}
|
||||
}
|
||||
|
||||
// Configure the microphone.
|
||||
func (d *Device) Configure() {
|
||||
d.data = make([]int32, d.SampleCountForSPL)
|
||||
d.buf = make([]uint32, (quantizeSteps / 16))
|
||||
}
|
||||
|
||||
// Read the raw microphone data.
|
||||
func (d *Device) Read(r []int32) (int, error) {
|
||||
count := len(r)
|
||||
|
||||
// get the next group of samples
|
||||
machine.I2S0.Read(d.buf)
|
||||
|
||||
if len(r) > len(d.buf) {
|
||||
count = len(d.buf)
|
||||
}
|
||||
for i := 0; i < count; i++ {
|
||||
r[i] = int32(d.buf[i])
|
||||
}
|
||||
|
||||
return count, nil
|
||||
}
|
||||
|
||||
// ReadWithFilter reads the microphone and filters the buffer using the sinc filter.
|
||||
func (d *Device) ReadWithFilter(r []int32) (int, error) {
|
||||
// read/filter the samples
|
||||
var sum uint16
|
||||
for i := 0; i < len(r); i++ {
|
||||
|
||||
// get the next group of samples
|
||||
machine.I2S0.Read(d.buf)
|
||||
|
||||
// filter
|
||||
sum = applySincFilter(d.buf)
|
||||
|
||||
// adjust to 10 bit value
|
||||
s := int32(sum >> 6)
|
||||
|
||||
// make it close to 0-offset signed
|
||||
s -= 512
|
||||
|
||||
r[i] = s
|
||||
}
|
||||
|
||||
return len(r), nil
|
||||
}
|
||||
|
||||
// GetSoundPressure returns the sound pressure in milli-decibels.
|
||||
func (d *Device) GetSoundPressure() (int32, int32) {
|
||||
// read/filter the samples
|
||||
d.ReadWithFilter(d.data)
|
||||
|
||||
// remove offset
|
||||
var avg int32
|
||||
for i := 0; i < len(d.data); i++ {
|
||||
avg += d.data[i]
|
||||
}
|
||||
avg /= int32(len(d.data))
|
||||
|
||||
for i := 0; i < len(d.data); i++ {
|
||||
d.data[i] -= avg
|
||||
}
|
||||
|
||||
// get max value
|
||||
var maxval int32
|
||||
for i := 0; i < len(d.data); i++ {
|
||||
v := d.data[i]
|
||||
if v < 0 {
|
||||
v = -v
|
||||
}
|
||||
if maxval < v {
|
||||
maxval = v
|
||||
}
|
||||
}
|
||||
|
||||
// calculate SPL
|
||||
spl := float64(maxval) / 1023.0 * d.Gain
|
||||
spl = 20 * math.Log10(spl/d.ReferenceLevel)
|
||||
|
||||
return int32(spl * 1000), maxval
|
||||
}
|
||||
|
||||
// sinc filter for 44 khz with 64 samples
|
||||
// each value matches the corresponding bit in the 8-bit value
|
||||
// for that sample.
|
||||
//
|
||||
// For more information: https://en.wikipedia.org/wiki/Sinc_filter
|
||||
//
|
||||
var sincfilter = [quantizeSteps]uint16{
|
||||
0, 2, 9, 21, 39, 63, 94, 132,
|
||||
179, 236, 302, 379, 467, 565, 674, 792,
|
||||
920, 1055, 1196, 1341, 1487, 1633, 1776, 1913,
|
||||
2042, 2159, 2263, 2352, 2422, 2474, 2506, 2516,
|
||||
2506, 2474, 2422, 2352, 2263, 2159, 2042, 1913,
|
||||
1776, 1633, 1487, 1341, 1196, 1055, 920, 792,
|
||||
674, 565, 467, 379, 302, 236, 179, 132,
|
||||
94, 63, 39, 21, 9, 2, 0, 0,
|
||||
}
|
||||
|
||||
// applySincFilter uses the sinc filter to process a single set of sample values.
|
||||
func applySincFilter(samples []uint32) (result uint16) {
|
||||
var sample uint16
|
||||
pos := 0
|
||||
for j := 0; j < len(samples); j++ {
|
||||
// takes only the low order 16-bits
|
||||
sample = uint16(samples[j] & 0xffff)
|
||||
for i := 0; i < 16; i++ {
|
||||
if (sample & 0x1) > 0 {
|
||||
result += sincfilter[pos]
|
||||
pos++
|
||||
}
|
||||
sample >>= 1
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
+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
|
||||
)
|
||||
|
||||
+42
-17
@@ -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,39 +22,62 @@ 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) {
|
||||
// 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) {
|
||||
data := make([]byte, 6)
|
||||
d.bus.ReadRegister(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]))
|
||||
d.bus.ReadRegister(uint8(d.Address), ACCEL_XOUT_H, data)
|
||||
// Now do two things:
|
||||
// 1. merge the two values to a 16-bit number (and cast to a 32-bit integer)
|
||||
// 2. scale the value to bring it in the -1000000..1000000 range.
|
||||
// This is done with a trick. What we do here is essentially multiply by
|
||||
// 1000000 and divide by 16384 to get the original scale, but to avoid
|
||||
// overflow we do it at 1/64 of the value:
|
||||
// 1000000 / 64 = 15625
|
||||
// 16384 / 64 = 256
|
||||
x = int32(int16((uint16(data[0])<<8)|uint16(data[1]))) * 15625 / 256
|
||||
y = int32(int16((uint16(data[2])<<8)|uint16(data[3]))) * 15625 / 256
|
||||
z = int32(int16((uint16(data[4])<<8)|uint16(data[5]))) * 15625 / 256
|
||||
return
|
||||
}
|
||||
|
||||
// ReadRotation reads the current rotation from the device and returns it.
|
||||
func (d Device) ReadRotation() (x int16, y int16, z int16) {
|
||||
// ReadRotation reads the current rotation from the device and returns it in
|
||||
// µ°/s (micro-degrees/sec). This means that if you were to do a complete
|
||||
// rotation along one axis and while doing so integrate all values over time,
|
||||
// you would get a value close to 360000000.
|
||||
func (d Device) ReadRotation() (x int32, y int32, z int32) {
|
||||
data := make([]byte, 6)
|
||||
d.bus.ReadRegister(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]))
|
||||
d.bus.ReadRegister(uint8(d.Address), GYRO_XOUT_H, data)
|
||||
// First the value is converted from a pair of bytes to a signed 16-bit
|
||||
// value and then to a signed 32-bit value to avoid integer overflow.
|
||||
// Then the value is scaled to µ°/s (micro-degrees per second).
|
||||
// This is done in the following steps:
|
||||
// 1. Multiply by 250 * 1000_000
|
||||
// 2. Divide by 32768
|
||||
// The following calculation (x * 15625 / 2048 * 1000) is essentially the
|
||||
// same but avoids overflow. First both operations are divided by 16 leading
|
||||
// to multiply by 15625000 and divide by 2048, and then part of the multiply
|
||||
// is done after the divide instead of before.
|
||||
x = int32(int16((uint16(data[0])<<8)|uint16(data[1]))) * 15625 / 2048 * 1000
|
||||
y = int32(int16((uint16(data[2])<<8)|uint16(data[3]))) * 15625 / 2048 * 1000
|
||||
z = int32(int16((uint16(data[4])<<8)|uint16(data[5]))) * 15625 / 2048 * 1000
|
||||
return
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user