mirror of
https://github.com/tinygo-org/drivers.git
synced 2026-08-04 15:07:46 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 349b5ca87e |
@@ -11,13 +11,10 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
container: ghcr.io/tinygo-org/tinygo-dev:latest
|
container: tinygo/tinygo-dev
|
||||||
steps:
|
steps:
|
||||||
- name: Work around CVE-2022-24765
|
|
||||||
# We're not on a multi-user machine, so this is safe.
|
|
||||||
run: git config --global --add safe.directory "$GITHUB_WORKSPACE"
|
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v2
|
||||||
- name: TinyGo version check
|
- name: TinyGo version check
|
||||||
run: tinygo version
|
run: tinygo version
|
||||||
- name: Enforce Go Formatted Code
|
- name: Enforce Go Formatted Code
|
||||||
|
|||||||
-193
@@ -1,196 +1,3 @@
|
|||||||
0.26.0
|
|
||||||
---
|
|
||||||
- **core**
|
|
||||||
- i2c iface refactor: Resolve #559
|
|
||||||
- fix uses of legacy i2c WriteRegister calls
|
|
||||||
- add correct Tx implementation for mock I2C interfaces
|
|
||||||
- bump golang.org/x/net version
|
|
||||||
|
|
||||||
- **new devices**
|
|
||||||
- **bma42x**
|
|
||||||
- add new BMA421/BMA425 driver
|
|
||||||
- **ndir**
|
|
||||||
- add Sandbox Electronics NDIR CO2 sensor driver (#580)
|
|
||||||
- **mpu9150**
|
|
||||||
- implement driver for Mpu9150 (#596)
|
|
||||||
- **sht4x**
|
|
||||||
- implement driver for sht4x (#597)
|
|
||||||
- **pcf8523**
|
|
||||||
- implement driver for pcf8523 (#599)
|
|
||||||
|
|
||||||
- **enhancements**
|
|
||||||
- **ssd1306**
|
|
||||||
- improve bus error handling
|
|
||||||
|
|
||||||
- **bugfixes**
|
|
||||||
- **st7789**
|
|
||||||
- fix scrolling when rotated by 180°
|
|
||||||
- **st7789**
|
|
||||||
- fix incorrect Rotation configuration
|
|
||||||
- fix SetScrollArea
|
|
||||||
- **ili9341**
|
|
||||||
- fix SetScrollArea
|
|
||||||
|
|
||||||
- **build**
|
|
||||||
- use latest tag of tinygo-dev container for running tests
|
|
||||||
|
|
||||||
|
|
||||||
0.25.0
|
|
||||||
---
|
|
||||||
|
|
||||||
- **core**
|
|
||||||
- add Sensor interface and Measurement type
|
|
||||||
- **delay**
|
|
||||||
- add new package for cycle-accurate delays
|
|
||||||
|
|
||||||
- **new devices**
|
|
||||||
- **AS560x**
|
|
||||||
- Add support for ams AS560x on-axis magnetic rotary position sensors
|
|
||||||
- **onewire**
|
|
||||||
- first implementation of 1-wire protocol (#505)
|
|
||||||
- **mpu6886**
|
|
||||||
- initial implementation
|
|
||||||
- **ttp229**
|
|
||||||
- initial support for ttp229 (BSF)
|
|
||||||
|
|
||||||
- **enhancements**
|
|
||||||
- **gps**
|
|
||||||
- make the date available in addition to the time (#532)
|
|
||||||
- **i2csoft**
|
|
||||||
- use cycle counting for delays
|
|
||||||
- **ili9341**
|
|
||||||
- add EnableTEOutput to be able to sync drawing with VSYNC
|
|
||||||
- add sleep mode
|
|
||||||
- unify rotation support
|
|
||||||
- **st7735**
|
|
||||||
- add DrawRGBBitmap8 method to draw raw RGB565 buffers
|
|
||||||
- add sleep mode
|
|
||||||
- unify rotation support
|
|
||||||
- **st7789**
|
|
||||||
- added DrawRGBBitmap8 (same as ili9341 & st7735)
|
|
||||||
- allow changing the color format using COLMOD
|
|
||||||
- make it possible to configure gamma
|
|
||||||
- support the chip select pin
|
|
||||||
- update saved rotation in SetRotation
|
|
||||||
- add sleep mode
|
|
||||||
- unify rotation support
|
|
||||||
- **sx126x/sx127x**
|
|
||||||
- Reduce spi buffer size, add missing select when using channels
|
|
||||||
- Remove heap alloc in interrupt, add non blocking channel send/receive, and other cleanups
|
|
||||||
- **wifinina**
|
|
||||||
- add generated strings, improved debugging system and messages
|
|
||||||
- add ResetIsHigh to control the behavior of the RESET pin for boards like the Arduino MKR 1010
|
|
||||||
- only add generated strings when using wifidebug tag
|
|
||||||
|
|
||||||
- **bugfixes**
|
|
||||||
- **ds3231**
|
|
||||||
- Document incorrect leap year 2100
|
|
||||||
- Fix negative temperature conversion
|
|
||||||
- **ili9341**
|
|
||||||
- fix Size() for mirrored rotation
|
|
||||||
- **st7789**
|
|
||||||
- avoid heap allocations after the driver is created
|
|
||||||
- **net**
|
|
||||||
- Revert "(#501) make IP.String() method return something sensible"
|
|
||||||
- **wifinina**
|
|
||||||
- small timing adjustments in Configure() to better ensure device reset
|
|
||||||
|
|
||||||
- **examples**
|
|
||||||
- **sdcard**
|
|
||||||
- remove tinyfs example and replace with link to tinyfs repo in docs
|
|
||||||
- **wifinina**
|
|
||||||
- improve connectToAP() and other needed minor corrections
|
|
||||||
|
|
||||||
- **build**
|
|
||||||
- switch to ghcr.io for docker container
|
|
||||||
- run smoke tests in parallel
|
|
||||||
- **Makefile**
|
|
||||||
- add XTENSA=0 flag to skip Xtensa tests
|
|
||||||
- remove AVR=0 flag
|
|
||||||
|
|
||||||
- **docs**
|
|
||||||
- remove full list of devices from README, better to keep it on the tinygo.org site
|
|
||||||
- update LICENSE year
|
|
||||||
|
|
||||||
|
|
||||||
0.24.0
|
|
||||||
---
|
|
||||||
- **new devices**
|
|
||||||
- **lora**
|
|
||||||
- created shared RadioEvent
|
|
||||||
- move shared config for sx126x/sx127x to single package
|
|
||||||
- **lorawan**
|
|
||||||
- add initial LoRaWAN stack support
|
|
||||||
- Basic implementation of Lorawan Regional Settings and EU868/AU915 regions
|
|
||||||
- **qmi8658c**
|
|
||||||
- Add support for the QMI8658C sensor (#467)
|
|
||||||
- **sh1106**
|
|
||||||
- add support for SH1106 display driver
|
|
||||||
- **sx127x**
|
|
||||||
- Driver for Semtech sx127x radio modules
|
|
||||||
|
|
||||||
- **enhancements**
|
|
||||||
- **bme280**
|
|
||||||
- improve config support
|
|
||||||
- add ReadAltitude() function copied from BMP280 driver
|
|
||||||
- **buzzer**
|
|
||||||
- make all note durations float64
|
|
||||||
- no tone during rest
|
|
||||||
- **dht22**
|
|
||||||
- update DHT22 receive to use runtime/interrupt
|
|
||||||
- **gps**
|
|
||||||
- add support for GLL sentence type, add original sentence to gps errors
|
|
||||||
- improve error handling
|
|
||||||
- improve parsing and add tests to verify
|
|
||||||
- **microbitmatrix**
|
|
||||||
- add link to schema for microbit V2
|
|
||||||
- add smoke test for microbitmatrix with microbit-v2
|
|
||||||
- add support for brightness of led pixels
|
|
||||||
- harmonize v1 and v2 implementation
|
|
||||||
- move Size() to version agnostic part
|
|
||||||
- **mpu6050**
|
|
||||||
- add functions to configure clock, and scaling for accelerometer and gyroscope
|
|
||||||
- **net/http**
|
|
||||||
- add PostForm()
|
|
||||||
- **sx126x**
|
|
||||||
- add Reset() and needed pin
|
|
||||||
- move RadioController into separate file for clarity
|
|
||||||
- pre-define all errors to avoid heap allocations
|
|
||||||
- refactor to RadioController interface to more easily handle non-STM32WL boards and remove duplicated code
|
|
||||||
|
|
||||||
- **vl53l1x**
|
|
||||||
- Add getter for the effective SPAD count
|
|
||||||
- **wifinina**
|
|
||||||
- add support for http server (#480)
|
|
||||||
|
|
||||||
- **bugfixes**
|
|
||||||
- **lsm303agr**
|
|
||||||
- fix I2C address auto increment for multi data read
|
|
||||||
- **net**
|
|
||||||
- (#501) make IP.String() method return something sensible
|
|
||||||
- **mpu6050**
|
|
||||||
- return I2C error when configuring fails
|
|
||||||
- **sx126x**
|
|
||||||
- fix in SetBandwidth function
|
|
||||||
- actually set the frequency when calling SetFrequency()
|
|
||||||
- correct RX/TX pin mapping for TheThingsIndustries GNSE board
|
|
||||||
|
|
||||||
- **examples**
|
|
||||||
- **LoRaWAN**
|
|
||||||
- example with LoRaWAN AT command set implementation
|
|
||||||
- basic example
|
|
||||||
- update all remaining examples for refactored API
|
|
||||||
- **sx126x**
|
|
||||||
- fix bandwidth,tx power in lora//lora_continuous example
|
|
||||||
- **sx127x**
|
|
||||||
- rx/tx example
|
|
||||||
|
|
||||||
- **build**
|
|
||||||
- remove older format build tags
|
|
||||||
- update to actions/checkout@v3
|
|
||||||
- work around for CVE-2022-24765
|
|
||||||
|
|
||||||
|
|
||||||
0.23.0
|
0.23.0
|
||||||
---
|
---
|
||||||
- **new devices**
|
- **new devices**
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
Copyright (c) 2018-2023 The TinyGo Authors. All rights reserved.
|
Copyright (c) 2018-2022 The TinyGo Authors. All rights reserved.
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
Redistribution and use in source and binary forms, with or without
|
||||||
modification, are permitted provided that the following conditions are
|
modification, are permitted provided that the following conditions are
|
||||||
|
|||||||
@@ -7,11 +7,248 @@ FMT_PATHS = ./
|
|||||||
fmt-check:
|
fmt-check:
|
||||||
@unformatted=$$(gofmt -l $(FMT_PATHS)); [ -z "$$unformatted" ] && exit 0; echo "Unformatted:"; for fn in $$unformatted; do echo " $$fn"; done; exit 1
|
@unformatted=$$(gofmt -l $(FMT_PATHS)); [ -z "$$unformatted" ] && exit 0; echo "Unformatted:"; for fn in $$unformatted; do echo " $$fn"; done; exit 1
|
||||||
|
|
||||||
XTENSA ?= 1
|
|
||||||
smoke-test:
|
smoke-test:
|
||||||
@mkdir -p build
|
@mkdir -p build
|
||||||
@go run ./smoketest.go -xtensa=$(XTENSA) smoketest.sh
|
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/adt7410/main.go
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/adxl345/main.go
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=pybadge ./examples/amg88xx
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/apa102/main.go
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=nano-33-ble ./examples/apds9960/proximity/main.go
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/apa102/itsybitsy-m0/main.go
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/at24cx/main.go
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/bh1750/main.go
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/blinkm/main.go
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/bmi160/main.go
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/bmp180/main.go
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/bmp280/main.go
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=trinket-m0 ./examples/bmp388/main.go
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=bluepill ./examples/ds1307/sram/main.go
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=bluepill ./examples/ds1307/time/main.go
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/ds3231/main.go
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/easystepper/main.go
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/espat/espconsole/main.go
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/espat/esphub/main.go
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/espat/espstation/main.go
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/flash/console/spi
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=pyportal ./examples/flash/console/qspi
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/gc9a01/main.go
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=feather-m0 ./examples/gps/i2c/main.go
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=feather-m0 ./examples/gps/uart/main.go
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/hcsr04/main.go
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/hd44780/customchar/main.go
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/hd44780/text/main.go
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/hd44780i2c/main.go
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=nano-33-ble ./examples/hts221/main.go
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/hub75/main.go
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=pyportal ./examples/ili9341/basic
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=xiao ./examples/ili9341/basic
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=pyportal ./examples/ili9341/pyportal_boing
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=pyportal ./examples/ili9341/scroll
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=xiao ./examples/ili9341/scroll
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=pyportal ./examples/ili9341/slideshow
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=circuitplay-express ./examples/lis3dh/main.go
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=nano-33-ble ./examples/lps22hb/main.go
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/lsm303agr/main.go
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/lsm6ds3/main.go
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/mag3110/main.go
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/mcp23017/main.go
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/mcp23017-multiple/main.go
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/mcp3008/main.go
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/mcp2515/main.go
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/microbitmatrix/main.go
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=microbit-v2 ./examples/microbitmatrix/main.go
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/mma8653/main.go
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/mpu6050/main.go
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=p1am-100 ./examples/p1am/main.go
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=pico ./examples/pca9685/main.go
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/pcd8544/setbuffer/main.go
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/pcd8544/setpixel/main.go
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=arduino ./examples/servo
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=pybadge ./examples/shifter/main.go
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/sht3x/main.go
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/shtc3/main.go
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/ssd1306/i2c_128x32/main.go
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/ssd1306/spi_128x64/main.go
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/ssd1331/main.go
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/st7735/main.go
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/st7789/main.go
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=circuitplay-express ./examples/thermistor/main.go
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=circuitplay-bluefruit ./examples/tone
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/tm1637/main.go
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=pyportal ./examples/touch/resistive/fourwire/main.go
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=pyportal ./examples/touch/resistive/pyportal_touchpaint/main.go
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/vl53l1x/main.go
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/vl6180x/main.go
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/waveshare-epd/epd2in13/main.go
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/waveshare-epd/epd2in13x/main.go
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/waveshare-epd/epd4in2/main.go
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/wifinina/ntpclient/main.go
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/wifinina/udpstation/main.go
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/wifinina/tcpclient/main.go
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/wifinina/webclient/main.go
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=circuitplay-express ./examples/ws2812
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.bin -target=m5stamp-c3 ./examples/ws2812
|
||||||
|
@md5sum ./build/test.bin
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=feather-nrf52840 ./examples/is31fl3731/main.go
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
ifneq ($(AVR), 0)
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=arduino ./examples/ws2812
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=digispark ./examples/ws2812
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
endif
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=trinket-m0 ./examples/bme280/main.go
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=circuitplay-express ./examples/microphone/main.go
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=circuitplay-express ./examples/buzzer/main.go
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=trinket-m0 ./examples/veml6070/main.go
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/l293x/simple/main.go
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/l293x/speed/main.go
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/l9110x/simple/main.go
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/l9110x/speed/main.go
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=nucleo-f103rb ./examples/shiftregister/main.go
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=hifive1b ./examples/ssd1351/main.go
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=circuitplay-express ./examples/lis2mdl/main.go
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/max72xx/main.go
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=feather-m0 ./examples/dht/main.go
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
# tinygo build -size short -o ./build/test.hex -target=arduino ./examples/keypad4x4/main.go
|
||||||
|
# @md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=xiao ./examples/pcf8563/alarm/
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=xiao ./examples/pcf8563/clkout/
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=xiao ./examples/pcf8563/time/
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=xiao ./examples/pcf8563/timer/
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=pico ./examples/qmi8658c/main.go
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=feather-m0 ./examples/ina260/main.go
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=nucleo-l432kc ./examples/aht20/main.go
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=feather-m4 ./examples/sdcard/console/
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=feather-m4 ./examples/sdcard/tinyfs/
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=wioterminal ./examples/rtl8720dn/webclient/
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=wioterminal ./examples/rtl8720dn/webserver/
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=wioterminal ./examples/rtl8720dn/mqttsub/
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=feather-m4 ./examples/i2csoft/adt7410/
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.elf -target=wioterminal ./examples/axp192/m5stack-core2-blinky/
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.uf2 -target=pico ./examples/xpt2046/main.go
|
||||||
|
@md5sum ./build/test.uf2
|
||||||
|
tinygo build -size short -o ./build/test.elf -target=m5stack-core2 ./examples/ft6336/basic/
|
||||||
|
@md5sum ./build/test.elf
|
||||||
|
tinygo build -size short -o ./build/test.elf -target=m5stack-core2 ./examples/ft6336/touchpaint/
|
||||||
|
@md5sum ./build/test.elf
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=nucleo-wl55jc ./examples/sx126x/lora_rxtx/
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.uf2 -target=pico ./examples/ssd1289/main.go
|
||||||
|
@md5sum ./build/test.uf2
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=pico ./examples/irremote/main.go
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=badger2040 ./examples/uc8151/main.go
|
||||||
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.uf2 -target=pico ./examples/scd4x/main.go
|
||||||
|
@md5sum ./build/test.uf2
|
||||||
|
tinygo build -size short -o ./build/test.uf2 -target=circuitplay-express ./examples/makeybutton/main.go
|
||||||
|
@md5sum ./build/test.uf2
|
||||||
|
|
||||||
# rwildcard is a recursive version of $(wildcard)
|
# rwildcard is a recursive version of $(wildcard)
|
||||||
# https://blog.jgc.org/2011/07/gnu-make-recursive-wildcard-function.html
|
# https://blog.jgc.org/2011/07/gnu-make-recursive-wildcard-function.html
|
||||||
|
|||||||
-233
@@ -1,233 +0,0 @@
|
|||||||
### Table of Contents
|
|
||||||
|
|
||||||
- ["net" Package](#net-package)
|
|
||||||
- [Using "net" Package](#using-net-package)
|
|
||||||
- [Using "net/http" Package](#using-nethttp-package)
|
|
||||||
- [Using "crypto/tls" Package](#using-cryptotls-package)
|
|
||||||
- [Using Sockets](#using-sockets)
|
|
||||||
|
|
||||||
## "net" Package
|
|
||||||
|
|
||||||
TinyGo's "net" package is ported from Go. The port offers a subset of Go's
|
|
||||||
"net" package. The subset maintains Go 1 compatiblity guarantee. A Go
|
|
||||||
application that uses "net" will most-likey just work on TinyGo if the usage is
|
|
||||||
within the subset offered. (There may be external constraints such as limited
|
|
||||||
SRAM on some targets that may limit full "net" functionality).
|
|
||||||
|
|
||||||
Continue below for details on using "net" and "net/http" packages.
|
|
||||||
|
|
||||||
See src/net/READMD.md in the TinyGo repo for more details on maintaining
|
|
||||||
TinyGo's "net" package.
|
|
||||||
|
|
||||||
## Using "net" Package
|
|
||||||
|
|
||||||
Ideally, TinyGo's "net" package would be Go's "net" package and applications
|
|
||||||
using "net" would just work, as-is. TinyGo's net package is a partial port of
|
|
||||||
Go's net package, so some things may not work because they have not been
|
|
||||||
ported.
|
|
||||||
|
|
||||||
There are a few features excluded during the porting process, in particular:
|
|
||||||
|
|
||||||
- No IPv6 support
|
|
||||||
- No DualStack support
|
|
||||||
|
|
||||||
Run ```go doc -all ./src/net``` in TinyGo repo to see full listing of what has
|
|
||||||
been ported. Here is a list of things known to work. You can find examples
|
|
||||||
of these at [examples/net](examples/net/).
|
|
||||||
|
|
||||||
### What is Known to Work
|
|
||||||
|
|
||||||
(These are all IPv4 only).
|
|
||||||
|
|
||||||
- TCP client and server
|
|
||||||
- UDP client
|
|
||||||
- TLS client
|
|
||||||
- HTTP client and server
|
|
||||||
- HTTPS client
|
|
||||||
- NTP client (UDP)
|
|
||||||
- MQTT client (paho & natiu)
|
|
||||||
- WebSocket client and server
|
|
||||||
|
|
||||||
Multiple sockets can be opened in a single app. For example, the app could run
|
|
||||||
as an http server listen on port :80 and also use NTP to get the current time
|
|
||||||
or send something over MQTT. There is a practical limit to the number of
|
|
||||||
active sockets per app, around 8 or 10, so don't go crazy.
|
|
||||||
|
|
||||||
Applications using Go's net package will need a few setup steps to work with
|
|
||||||
TinyGo's net package. The steps are required before using "net".
|
|
||||||
|
|
||||||
### Step 1: Probe to Load Network Driver
|
|
||||||
|
|
||||||
Call Probe() to load the correct network driver for your target. Probe()
|
|
||||||
allows the app to work on multiple targets.
|
|
||||||
|
|
||||||
```go
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"tinygo.org/x/drivers/netlink/probe"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
|
|
||||||
// load network driver for target
|
|
||||||
link, dev := probe.Probe()
|
|
||||||
|
|
||||||
...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Probe() will load the driver with default configuration for the target. For
|
|
||||||
custom configuration, the app can open code Probe() for the target
|
|
||||||
requirements.
|
|
||||||
|
|
||||||
Probe() returns a [Netlinker](netlink/README.md) and a
|
|
||||||
[Netdever](netdev/README.md), interfaces implemented by the network driver.
|
|
||||||
Next, we'll use the Netlinker interface to connect the target to an IP network.
|
|
||||||
|
|
||||||
### Step 2: Connect to an IP Network
|
|
||||||
|
|
||||||
Before the net package is fully functional, we need to connect the target to an
|
|
||||||
IP network.
|
|
||||||
|
|
||||||
```go
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"tinygo.org/x/drivers/netlink"
|
|
||||||
"tinygo.org/x/drivers/netlink/probe"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
|
|
||||||
// load network driver for target
|
|
||||||
link, _ := probe.Probe()
|
|
||||||
|
|
||||||
// Connect target to IP network
|
|
||||||
link.NetConnect(&netlink.ConnectParams{
|
|
||||||
Ssid: "my SSID",
|
|
||||||
Passphrase: "my passphrase",
|
|
||||||
})
|
|
||||||
|
|
||||||
// OK to use "net" from here on
|
|
||||||
...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Optionally, get notified of IP network connects and disconnects:
|
|
||||||
|
|
||||||
```go
|
|
||||||
link.Notify(func(e netlink.Event) {
|
|
||||||
switch e {
|
|
||||||
case netlink.EventNetUp: println("Network UP")
|
|
||||||
case netlink.EventNetDown: println("Network DOWN")
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
Here is an example of an http server listening on port :8080:
|
|
||||||
|
|
||||||
```go
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"tinygo.org/x/drivers/netlink"
|
|
||||||
"tinygo.org/x/drivers/netlink/probe"
|
|
||||||
)
|
|
||||||
|
|
||||||
func HelloServer(w http.ResponseWriter, r *http.Request) {
|
|
||||||
fmt.Fprintf(w, "Hello, %s!", r.URL.Path[1:])
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
|
|
||||||
// load network driver for target
|
|
||||||
link, _ := probe.Probe()
|
|
||||||
|
|
||||||
// Connect target to IP network
|
|
||||||
link.NetConnect(&netlink.ConnectParams{
|
|
||||||
Ssid: "my SSID",
|
|
||||||
Passphrase: "my passphrase",
|
|
||||||
})
|
|
||||||
|
|
||||||
// Serve it up
|
|
||||||
http.HandleFunc("/", HelloServer)
|
|
||||||
http.ListenAndServe(":8080", nil)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Using "net/http" Package
|
|
||||||
|
|
||||||
TinyGo's net/http package is a partial port of Go's net/http package, providing
|
|
||||||
a subset of the full net/http package. There are a few features excluded
|
|
||||||
during the porting process, in particular:
|
|
||||||
|
|
||||||
- No HTTP/2 support
|
|
||||||
- No TLS support for HTTP servers (no https servers)
|
|
||||||
- HTTP client request can't be reused
|
|
||||||
|
|
||||||
HTTP client methods (http.Get, http.Head, http.Post, and http.PostForm) are
|
|
||||||
functional. Dial clients support both HTTP and HTTPS URLs.
|
|
||||||
|
|
||||||
HTTP server methods and objects are mostly ported, but for HTTP only; HTTPS
|
|
||||||
servers are not supported.
|
|
||||||
|
|
||||||
HTTP request and response handling code is mostly ported, so most the intricacy
|
|
||||||
of parsing and writing headers is handled as in the full net/http package.
|
|
||||||
|
|
||||||
Run ```go doc -all ./src/net/http``` in TinyGo repo to see full listing.
|
|
||||||
|
|
||||||
## Using "crypto/tls" Package
|
|
||||||
|
|
||||||
TinyGo's TLS support (crypto/tls) relies on hardware offload of the TLS
|
|
||||||
protocol. This is different from Go's crypto/tls package which handles the TLS
|
|
||||||
protocol in software.
|
|
||||||
|
|
||||||
TinyGo's TLS support is only available for client applications. You can
|
|
||||||
http.Get() to an https:// address, but you cannot http.ListenAndServeTLS() an
|
|
||||||
https server.
|
|
||||||
|
|
||||||
The offloading hardware has pre-defined TLS certificates built-in.
|
|
||||||
|
|
||||||
## Using Sockets
|
|
||||||
|
|
||||||
The Netdever interface is a BSD socket-like interface so an application can make direct
|
|
||||||
socket calls, bypassing the "net" package for the lowest overhead.
|
|
||||||
|
|
||||||
Here is a simple TCP client application using direct sockets:
|
|
||||||
|
|
||||||
```go
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net" // only need to parse IP address
|
|
||||||
|
|
||||||
"tinygo.org/x/drivers/netdev"
|
|
||||||
"tinygo.org/x/drivers/netlink"
|
|
||||||
"tinygo.org/x/drivers/netlink/probe"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
|
|
||||||
// load network driver for target
|
|
||||||
link, dev := probe.Probe()
|
|
||||||
|
|
||||||
// Connect target to IP network
|
|
||||||
link.NetConnect(&netlink.ConnectParams{
|
|
||||||
Ssid: "my SSID",
|
|
||||||
Passphrase: "my passphrase",
|
|
||||||
})
|
|
||||||
|
|
||||||
// omit error handling
|
|
||||||
|
|
||||||
sock, _ := dev.Socket(netdev.AF_INET, netdev.SOCK_STREAM, netdev.IPPROTO_TCP)
|
|
||||||
|
|
||||||
dev.Connect(sock, "", net.ParseIP("10.0.0.100"), 8080)
|
|
||||||
dev.Send(sock, []bytes("hello"), 0, 0)
|
|
||||||
|
|
||||||
dev.Close(sock)
|
|
||||||
link.NetDisconnect()
|
|
||||||
}
|
|
||||||
```
|
|
||||||
@@ -3,10 +3,7 @@
|
|||||||
[](https://pkg.go.dev/tinygo.org/x/drivers) [](https://github.com/tinygo-org/drivers/actions/workflows/build.yml)
|
[](https://pkg.go.dev/tinygo.org/x/drivers) [](https://github.com/tinygo-org/drivers/actions/workflows/build.yml)
|
||||||
|
|
||||||
|
|
||||||
This package provides a collection of 101 different hardware drivers for devices such as sensors and displays that can be used together with [TinyGo](https://tinygo.org).
|
This package provides a collection of hardware drivers for devices such as sensors and displays that can be used together with [TinyGo](https://tinygo.org).
|
||||||
|
|
||||||
For the complete list, please see:
|
|
||||||
https://tinygo.org/docs/reference/devices/
|
|
||||||
|
|
||||||
## Installing
|
## Installing
|
||||||
|
|
||||||
@@ -53,6 +50,103 @@ func main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Currently supported devices
|
||||||
|
|
||||||
|
The following 90 devices are supported.
|
||||||
|
|
||||||
|
| Device Name | Interface Type |
|
||||||
|
|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------|
|
||||||
|
| [ADT7410 I2C Temperature Sensor](https://www.analog.com/media/en/technical-documentation/data-sheets/ADT7410.pdf) | I2C |
|
||||||
|
| [ADXL345 accelerometer](http://www.analog.com/media/en/technical-documentation/data-sheets/ADXL345.pdf) | I2C |
|
||||||
|
| [AHT20 I2C Temperature and Humidity Sensor](http://www.aosong.com/userfiles/files/media/AHT20%20%E8%8B%B1%E6%96%87%E7%89%88%E8%AF%B4%E6%98%8E%E4%B9%A6%20A0%2020201222.pdf) | I2C |
|
||||||
|
| [AMG88xx 8x8 Thermal camera sensor](https://cdn-learn.adafruit.com/assets/assets/000/043/261/original/Grid-EYE_SPECIFICATIONS%28Reference%29.pdf) | I2C |
|
||||||
|
| [APA102 RGB LED](https://cdn-shop.adafruit.com/product-files/2343/APA102C.pdf) | SPI |
|
||||||
|
| [APDS9960 Digital proximity, ambient light, RGB and gesture sensor](https://cdn.sparkfun.com/assets/learn_tutorials/3/2/1/Avago-APDS-9960-datasheet.pdf) | I2C |
|
||||||
|
| [AT24CX 2-wire serial EEPROM](https://www.openimpulse.com/blog/wp-content/uploads/wpsc/downloadables/24C32-Datasheet.pdf) | I2C |
|
||||||
|
| [AXP192 single Cell Li-Battery and Power System Management](https://github.com/m5stack/M5-Schematic/blob/master/Core/AXP192%20Datasheet_v1.1_en_draft_2211.pdf) | I2C |
|
||||||
|
| [BBC micro:bit LED matrix](https://github.com/bbcmicrobit/hardware/blob/master/SCH_BBC-Microbit_V1.3B.pdf) | GPIO |
|
||||||
|
| [BH1750 ambient light sensor](https://www.mouser.com/ds/2/348/bh1750fvi-e-186247.pdf) | I2C |
|
||||||
|
| [BlinkM RGB LED](http://thingm.com/fileadmin/thingm/downloads/BlinkM_datasheet.pdf) | I2C |
|
||||||
|
| [BME280 humidity/pressure sensor](https://cdn-shop.adafruit.com/datasheets/BST-BME280_DS001-10.pdf) | I2C |
|
||||||
|
| [BMI160 accelerometer/gyroscope](https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bmi160-ds000.pdf) | SPI |
|
||||||
|
| [BMP180 barometer](https://cdn-shop.adafruit.com/datasheets/BST-BMP180-DS000-09.pdf) | I2C |
|
||||||
|
| [BMP280 temperature/barometer](https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bmp280-ds001.pdf) | I2C |
|
||||||
|
| [BMP388 pressure sensor](https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bmp388-ds001.pdf) | I2C |
|
||||||
|
| [Buzzer](https://en.wikipedia.org/wiki/Buzzer#Piezoelectric) | GPIO |
|
||||||
|
| [DHTXX thermometer and humidity sensor](https://cdn-shop.adafruit.com/datasheets/Digital+humidity+and+temperature+sensor+AM2302.pdf) | GPIO |
|
||||||
|
| [DS1307 real time clock](https://datasheets.maximintegrated.com/en/ds/DS1307.pdf) | I2C |
|
||||||
|
| [DS3231 real time clock](https://datasheets.maximintegrated.com/en/ds/DS3231.pdf) | I2C |
|
||||||
|
| [ESP32 as WiFi Coprocessor with Arduino nina-fw](https://github.com/arduino/nina-fw) | SPI |
|
||||||
|
| [ESP8266/ESP32 AT Command set for WiFi/TCP/UDP](https://github.com/espressif/esp32-at) | UART |
|
||||||
|
| [FT6336 touch controller](https://focuslcds.com/content/FT6236.pdf) | I2C |
|
||||||
|
| [GPS module](https://www.u-blox.com/en/product/neo-6-series) | I2C/UART |
|
||||||
|
| [HC-SR04 Ultrasonic distance sensor](https://cdn.sparkfun.com/datasheets/Sensors/Proximity/HCSR04.pdf) | GPIO |
|
||||||
|
| [HD44780 LCD controller](https://www.sparkfun.com/datasheets/LCD/HD44780.pdf) | GPIO/I2C |
|
||||||
|
| [HTS221 digital humidity and temperature sensor](https://www.st.com/resource/en/datasheet/hts221.pdf) | I2C |
|
||||||
|
| [HUB75 RGB led matrix](https://cdn-learn.adafruit.com/downloads/pdf/32x16-32x32-rgb-led-matrix.pdf) | SPI |
|
||||||
|
| [software I2C driver](https://www.ti.com/lit/an/slva704/slva704.pdf) | GPIO |
|
||||||
|
| [ILI9341 TFT color display](https://cdn-shop.adafruit.com/datasheets/ILI9341.pdf) | SPI |
|
||||||
|
| [INA260 Volt/Amp/Power meter](https://www.ti.com/lit/ds/symlink/ina260.pdf) | I2C |
|
||||||
|
| [Infrared remote control](https://en.wikipedia.org/wiki/Consumer_IR) | GPIO |
|
||||||
|
| [IS31FL3731 matrix LED driver](https://www.lumissil.com/assets/pdf/core/IS31FL3731_DS.pdf) | I2C |
|
||||||
|
| [4x4 Membrane Keypad](https://cdn.sparkfun.com/assets/f/f/a/5/0/DS-16038.pdf) | GPIO |
|
||||||
|
| [L293x motor driver](https://www.ti.com/lit/ds/symlink/l293d.pdf) | GPIO/PWM |
|
||||||
|
| [L9110x motor driver](https://www.elecrow.com/download/datasheet-l9110.pdf) | GPIO/PWM |
|
||||||
|
| [LIS2MDL magnetometer](https://www.st.com/resource/en/datasheet/lis2mdl.pdf) | I2C |
|
||||||
|
| [LIS3DH accelerometer](https://www.st.com/resource/en/datasheet/lis3dh.pdf) | I2C |
|
||||||
|
| [LPS22HB MEMS nano pressure sensor](https://www.st.com/resource/en/datasheet/dm00140895.pdf) | I2C |
|
||||||
|
| [LSM6DS3 accelerometer](https://www.st.com/resource/en/datasheet/lsm6ds3.pdf) | I2C |
|
||||||
|
| [LSM6DSOX accelerometer](https://www.st.com/resource/en/datasheet/lsm6dsox.pdf) | I2C |
|
||||||
|
| [LSM6DS3TR accelerometer](https://www.st.com/resource/en/datasheet/lsm6ds3tr.pdf) | I2C |
|
||||||
|
| [LSM303AGR accelerometer](https://www.st.com/resource/en/datasheet/lsm303agr.pdf) | I2C |
|
||||||
|
| [LSM9DS1 accelerometer](https://www.st.com/resource/en/datasheet/lsm9ds1.pdf) | I2C |
|
||||||
|
| [Makey Button](https://makeymakey.com/) | GPIO |
|
||||||
|
| [MAG3110 magnetometer](https://www.nxp.com/docs/en/data-sheet/MAG3110.pdf) | I2C |
|
||||||
|
| [MAX7219 & MAX7221 display driver](https://datasheets.maximintegrated.com/en/ds/MAX7219-MAX7221.pdf) | SPI |
|
||||||
|
| [MCP2515 Stand-Alone CAN Controller with SPI Interface](https://ww1.microchip.com/downloads/en/DeviceDoc/MCP2515-Family-Data-Sheet-DS20001801K.pdf) | SPI |
|
||||||
|
| [MCP3008 analog to digital converter (ADC)](http://ww1.microchip.com/downloads/en/DeviceDoc/21295d.pdf) | SPI |
|
||||||
|
| [MCP23017 port expander](https://ww1.microchip.com/downloads/en/DeviceDoc/20001952C.pdf) | I2C |
|
||||||
|
| [Microphone - PDM](https://cdn-learn.adafruit.com/assets/assets/000/049/977/original/MP34DT01-M.pdf) | I2S/PDM |
|
||||||
|
| [MMA8653 accelerometer](https://www.nxp.com/docs/en/data-sheet/MMA8653FC.pdf) | I2C |
|
||||||
|
| [MPU6050 accelerometer/gyroscope](https://store.invensense.com/datasheets/invensense/MPU-6050_DataSheet_V3%204.pdf) | I2C |
|
||||||
|
| [P1AM-100 Base Controller](https://facts-engineering.github.io/modules/P1AM-100/P1AM-100.html) | SPI |
|
||||||
|
| [PCD8544 display](http://eia.udg.edu/~forest/PCD8544_1.pdf) | SPI |
|
||||||
|
| [PCF8563 real time clock](https://www.nxp.com/docs/en/data-sheet/PCF8563.pdf) | I2C |
|
||||||
|
| [QMI8658C accelerometer/gyroscope](https://www.qstcorp.com/upload/pdf/202202/%EF%BC%88%E5%B7%B2%E4%BC%A0%EF%BC%89QMI8658C%20datasheet%20rev%200.9.pdf) | I2C |
|
||||||
|
| [Resistive Touchscreen (4-wire)](http://ww1.microchip.com/downloads/en/Appnotes/doc8091.pdf) | GPIO |
|
||||||
|
| [RTL8720DN 2.4G/5G Dual Bands Wireless and BLE5.0](https://www.seeedstudio.com/Realtek8720DN-2-4G-5G-Dual-Bands-Wireless-and-BLE5-0-Combo-Module-p-4442.html) | UART |
|
||||||
|
| [SCD4x CO2 Sensor](https://sensirion.com/media/documents/C4B87CE6/627C2DCD/CD_DS_SCD40_SCD41_Datasheet_D1.pdf) | I2C |
|
||||||
|
| [Semihosting](https://wiki.segger.com/Semihosting) | Debug |
|
||||||
|
| [Servo](https://learn.sparkfun.com/tutorials/hobby-servo-tutorial/all) | PWM |
|
||||||
|
| [Shift register (PISO)](https://en.wikipedia.org/wiki/Shift_register#Parallel-in_serial-out_\(PISO\)) | GPIO |
|
||||||
|
| [Shift registers (SIPO)](https://en.wikipedia.org/wiki/Shift_register#Serial-in_parallel-out_(SIPO)) | GPIO |
|
||||||
|
| [SHT3x Digital Humidity Sensor](https://www.sensirion.com/fileadmin/user_upload/customers/sensirion/Dokumente/2_Humidity_Sensors/Datasheets/Sensirion_Humidity_Sensors_SHT3x_Datasheet_digital.pdf) | I2C |
|
||||||
|
| [SHTC3 Digital Humidity Sensor (RH/T)](https://www.sensirion.com/fileadmin/user_upload/customers/sensirion/Dokumente/2_Humidity_Sensors/Datasheets/Sensirion_Humidity_Sensors_SHTC3_Datasheet.pdf) | I2C |
|
||||||
|
| [SPI NOR Flash Memory](https://en.wikipedia.org/wiki/Flash_memory#NOR_flash) | SPI/QSPI |
|
||||||
|
| [SPI SDCARD/MMC](https://en.wikipedia.org/wiki/SD_card) | SPI |
|
||||||
|
| [SSD1306 OLED display](https://cdn-shop.adafruit.com/datasheets/SSD1306.pdf) | I2C / SPI |
|
||||||
|
| [SSD1331 TFT color display](https://www.crystalfontz.com/controllers/SolomonSystech/SSD1331/381/) | SPI |
|
||||||
|
| [SSD1351 OLED display](https://download.mikroe.com/documents/datasheets/ssd1351-revision-1.3.pdf) | SPI |
|
||||||
|
| [ST7735 TFT color display](https://www.crystalfontz.com/controllers/Sitronix/ST7735R/319/) | SPI |
|
||||||
|
| [ST7789 TFT color display](https://cdn-shop.adafruit.com/product-files/3787/3787_tft_QT154H2201__________20190228182902.pdf) | SPI |
|
||||||
|
| [Stepper motor "Easystepper" controller](https://en.wikipedia.org/wiki/Stepper_motor) | GPIO |
|
||||||
|
| [Thermistor](https://www.farnell.com/datasheets/33552.pdf) | ADC |
|
||||||
|
| [TM1637 7-segment LED display](https://www.mcielectronics.cl/website_MCI/static/documents/Datasheet_TM1637.pdf) | I2C |
|
||||||
|
| [TMP102 I2C Temperature Sensor](https://download.mikroe.com/documents/datasheets/tmp102-data-sheet.pdf) | I2C |
|
||||||
|
| [UC8151 All-in-one driver IC for ESL](https://www.buydisplay.com/download/ic/UC8151C.pdf) | I2C |
|
||||||
|
| [VEML6070 UV light sensor](https://www.vishay.com/docs/84277/veml6070.pdf) | I2C |
|
||||||
|
| [VL53L1X time-of-flight distance sensor](https://www.st.com/resource/en/datasheet/vl53l1x.pdf) | I2C |
|
||||||
|
| [VL6180X time-of-flight distance sensor](https://www.st.com/resource/en/datasheet/vl6180x.pdf) | I2C |
|
||||||
|
| [Waveshare 2.13" (B & C) e-paper display](https://www.waveshare.com/w/upload/d/d3/2.13inch-e-paper-b-Specification.pdf) | SPI |
|
||||||
|
| [Waveshare 2.13" e-paper display](https://www.waveshare.com/w/upload/e/e6/2.13inch_e-Paper_Datasheet.pdf) | SPI |
|
||||||
|
| [Waveshare 2.9" e-paper display (V1)](https://www.waveshare.com/w/upload/e/e6/2.9inch_e-Paper_Datasheet.pdf) | SPI |
|
||||||
|
| [Waveshare 4.2" e-paper B/W display](https://www.waveshare.com/w/upload/6/6a/4.2inch-e-paper-specification.pdf) | SPI |
|
||||||
|
| [Waveshare GC9A01 TFT round display](https://www.waveshare.com/w/upload/5/5e/GC9A01A.pdf) | SPI |
|
||||||
|
| [WS2812 RGB LED](https://cdn-shop.adafruit.com/datasheets/WS2812.pdf) | GPIO |
|
||||||
|
| [XPT2046 touch controller](http://grobotronics.com/images/datasheets/xpt2046-datasheet.pdf) | GPIO |
|
||||||
|
| [Semtech SX126x Lora](https://www.semtech.com/products/wireless-rf/lora-transceiv-ers/sx1261) | SPI |
|
||||||
|
| [SSD1289 TFT color display](http://aitendo3.sakura.ne.jp/aitendo_data/product_img/lcd/tft2/M032C1289TP/3.2-SSD1289.pdf) | GPIO |
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
Your contributions are welcome!
|
Your contributions are welcome!
|
||||||
|
|||||||
@@ -1,196 +0,0 @@
|
|||||||
// Package adafruit4650 implements a driver for the Adafruit FeatherWing OLED - 128x64 OLED display.
|
|
||||||
// The display is backed itself by a SH1107 driver chip.
|
|
||||||
//
|
|
||||||
// Store: https://www.adafruit.com/product/4650
|
|
||||||
//
|
|
||||||
// Documentation: https://learn.adafruit.com/adafruit-128x64-oled-featherwing
|
|
||||||
package adafruit4650
|
|
||||||
|
|
||||||
import (
|
|
||||||
"image/color"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"tinygo.org/x/drivers"
|
|
||||||
)
|
|
||||||
|
|
||||||
const DefaultAddress = 0x3c
|
|
||||||
|
|
||||||
const (
|
|
||||||
commandSetLowColumn = 0x00
|
|
||||||
commandSetHighColumn = 0x10
|
|
||||||
commandSetPage = 0xb0
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
width = 128
|
|
||||||
height = 64
|
|
||||||
)
|
|
||||||
|
|
||||||
// Device represents an Adafruit 4650 device
|
|
||||||
type Device struct {
|
|
||||||
bus drivers.I2C
|
|
||||||
Address uint8
|
|
||||||
buffer []byte
|
|
||||||
width int16
|
|
||||||
height int16
|
|
||||||
}
|
|
||||||
|
|
||||||
// New creates a new device, not configuring anything yet.
|
|
||||||
func New(bus drivers.I2C) Device {
|
|
||||||
return Device{
|
|
||||||
bus: bus,
|
|
||||||
Address: DefaultAddress,
|
|
||||||
width: width,
|
|
||||||
height: height,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Configure initializes the display with default configuration
|
|
||||||
func (d *Device) Configure() error {
|
|
||||||
|
|
||||||
bufferSize := d.width * d.height / 8
|
|
||||||
d.buffer = make([]byte, bufferSize)
|
|
||||||
|
|
||||||
// This sequence is an amalgamation of the datasheet, official Arduino driver, CircuitPython driver and other drivers
|
|
||||||
initSequence := []byte{
|
|
||||||
0xae, // display off, sleep mode
|
|
||||||
//0xd5, 0x41, // set display clock divider (from original datasheet)
|
|
||||||
0xd5, 0x51, // set display clock divider (from Adafruit driver)
|
|
||||||
0xd9, 0x22, // pre-charge/dis-charge period mode: 2 DCLKs/2 DCLKs (POR)
|
|
||||||
0x20, // memory mode
|
|
||||||
0x81, 0x4f, // contrast setting = 0x4f
|
|
||||||
0xad, 0x8a, // set dc/dc pump
|
|
||||||
0xa0, // segment remap, flip-x
|
|
||||||
0xc0, // common output scan direction
|
|
||||||
0xdc, 0x00, // set display start line 0 (POR=0)
|
|
||||||
0xa8, 0x3f, // multiplex ratio, height - 1 = 0x3f
|
|
||||||
0xd3, 0x60, // set display offset mode = 0x60
|
|
||||||
0xdb, 0x35, // VCOM deselect level = 0.770 (POR)
|
|
||||||
0xa4, // entire display off, retain RAM, normal status (POR)
|
|
||||||
0xa6, // normal (not reversed) display
|
|
||||||
0xaf, // display on
|
|
||||||
}
|
|
||||||
|
|
||||||
err := d.writeCommands(initSequence)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// recommended in the datasheet, same in other drivers
|
|
||||||
time.Sleep(100 * time.Millisecond)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ClearDisplay clears the image buffer as well as the actual display
|
|
||||||
func (d *Device) ClearDisplay() error {
|
|
||||||
d.ClearBuffer()
|
|
||||||
return d.Display()
|
|
||||||
}
|
|
||||||
|
|
||||||
// ClearBuffer clears the buffer
|
|
||||||
func (d *Device) ClearBuffer() {
|
|
||||||
bzero(d.buffer)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetPixel modifies the internal buffer. Since this display has a bit-depth of 1 bit any non-zero
|
|
||||||
// color component will be treated as 'on', otherwise 'off'.
|
|
||||||
func (d *Device) SetPixel(x int16, y int16, c color.RGBA) {
|
|
||||||
if x < 0 || x >= d.width || y < 0 || y >= d.height {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// RAM layout
|
|
||||||
// *-----> y
|
|
||||||
// |
|
|
||||||
// x| col0 col1 ... col63
|
|
||||||
// v p0 a0 b0 ..
|
|
||||||
// a1 b1 ..
|
|
||||||
// .. .. ..
|
|
||||||
// a7 b7 ..
|
|
||||||
// p1 a0 b0
|
|
||||||
// a1 b1
|
|
||||||
//
|
|
||||||
|
|
||||||
//flip y - so the display orientation matches the silk screen labeling etc.
|
|
||||||
y = d.height - y - 1
|
|
||||||
|
|
||||||
page := x / 8
|
|
||||||
bytesPerPage := d.height
|
|
||||||
byteIndex := y + bytesPerPage*page
|
|
||||||
bit := x % 8
|
|
||||||
if (c.R | c.G | c.B) != 0 {
|
|
||||||
d.buffer[byteIndex] |= 1 << uint8(bit)
|
|
||||||
} else {
|
|
||||||
d.buffer[byteIndex] &^= 1 << uint8(bit)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Display sends the whole buffer to the screen
|
|
||||||
func (d *Device) Display() error {
|
|
||||||
|
|
||||||
bytesPerPage := d.height
|
|
||||||
|
|
||||||
pages := (d.width + 7) / 8
|
|
||||||
for page := int16(0); page < pages; page++ {
|
|
||||||
|
|
||||||
err := d.setRAMPosition(uint8(page), 0)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
offset := page * bytesPerPage
|
|
||||||
err = d.writeRAM(d.buffer[offset : offset+bytesPerPage])
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// setRAMPosition updates the device's current page and column position
|
|
||||||
func (d *Device) setRAMPosition(page uint8, column uint8) error {
|
|
||||||
if page > 15 {
|
|
||||||
panic("page out of bounds")
|
|
||||||
}
|
|
||||||
if column > 127 {
|
|
||||||
panic("column out of bounds")
|
|
||||||
}
|
|
||||||
setPage := commandSetPage | (page & 0xF)
|
|
||||||
|
|
||||||
lo := column & 0xF
|
|
||||||
setLowColumn := commandSetLowColumn | lo
|
|
||||||
|
|
||||||
hi := (column >> 4) & 0x7
|
|
||||||
setHighColumn := commandSetHighColumn | hi
|
|
||||||
|
|
||||||
cmds := []byte{
|
|
||||||
setPage,
|
|
||||||
setLowColumn,
|
|
||||||
setHighColumn,
|
|
||||||
}
|
|
||||||
|
|
||||||
return d.writeCommands(cmds)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Size returns the current size of the display.
|
|
||||||
func (d *Device) Size() (w, h int16) {
|
|
||||||
return d.width, d.height
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *Device) writeCommands(commands []byte) error {
|
|
||||||
onlyCommandsFollowing := byte(0x00)
|
|
||||||
return d.bus.Tx(uint16(d.Address), append([]byte{onlyCommandsFollowing}, commands...), nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *Device) writeRAM(data []byte) error {
|
|
||||||
onlyRAMFollowing := byte(0x40)
|
|
||||||
return d.bus.Tx(uint16(d.Address), append([]byte{onlyRAMFollowing}, data...), nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func bzero(buf []byte) {
|
|
||||||
for i := range buf {
|
|
||||||
buf[i] = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,176 +0,0 @@
|
|||||||
package adafruit4650
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
_ "embed"
|
|
||||||
"encoding/hex"
|
|
||||||
"fmt"
|
|
||||||
"image"
|
|
||||||
"image/color"
|
|
||||||
"image/draw"
|
|
||||||
"image/png"
|
|
||||||
"os"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
"tinygo.org/x/drivers"
|
|
||||||
"tinygo.org/x/tinyfont"
|
|
||||||
"tinygo.org/x/tinyfont/freemono"
|
|
||||||
)
|
|
||||||
|
|
||||||
//go:embed expected_hello_world.png
|
|
||||||
var expectedHelloWorld []byte
|
|
||||||
|
|
||||||
// mockBus mocks a fake i2c device adafruit4650 display.
|
|
||||||
// The memory layout assumes that clients set up the device in a particular way and always send complete
|
|
||||||
// pages to the device buffer.
|
|
||||||
type mockBus struct {
|
|
||||||
img draw.Image
|
|
||||||
line int
|
|
||||||
addr uint8
|
|
||||||
currentPage int
|
|
||||||
currentColumn int
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *mockBus) Tx(addr uint16, w, r []byte) error {
|
|
||||||
if addr != uint16(m.addr) {
|
|
||||||
panic("unexpected address")
|
|
||||||
}
|
|
||||||
if r != nil {
|
|
||||||
panic("mock does not support reads")
|
|
||||||
}
|
|
||||||
|
|
||||||
if w[0] == 0x00 {
|
|
||||||
if w[1]&0xf0 == 0xb0 {
|
|
||||||
m.currentPage = int(w[1] & 0x0f)
|
|
||||||
|
|
||||||
lo := w[2] & 0x0f
|
|
||||||
hi := w[2] & 0x07
|
|
||||||
m.currentColumn = int(hi<<4 | lo)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if w[0] != 0x40 {
|
|
||||||
panic("unexpected first byte: " + hex.EncodeToString(w[0:1]))
|
|
||||||
}
|
|
||||||
|
|
||||||
return m.writeRAM(w[1:])
|
|
||||||
}
|
|
||||||
|
|
||||||
func newMock() *mockBus {
|
|
||||||
|
|
||||||
m := image.NewRGBA(image.Rect(0, 0, width, height))
|
|
||||||
return &mockBus{img: m, addr: DefaultAddress, currentPage: -1, currentColumn: -1}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *mockBus) writeRAM(data []byte) error {
|
|
||||||
|
|
||||||
// RAM layout
|
|
||||||
// *-----> y
|
|
||||||
// |
|
|
||||||
// x| col0 col1 ... col63
|
|
||||||
// v p0 a0 b0 ..
|
|
||||||
// a1 b1 ..
|
|
||||||
// .. .. ..
|
|
||||||
// a7 b7 ..
|
|
||||||
// p1 a0 b0
|
|
||||||
// a1 b1
|
|
||||||
//
|
|
||||||
|
|
||||||
fmt.Printf("writing page %d\n", m.currentPage)
|
|
||||||
// assuming entire pages will be written
|
|
||||||
for x := 0; x < 8; x++ {
|
|
||||||
for y := 0; y < height; y++ {
|
|
||||||
|
|
||||||
col := data[y]
|
|
||||||
|
|
||||||
c := color.Black
|
|
||||||
if col&(1<<x) != 0 {
|
|
||||||
c = color.White
|
|
||||||
}
|
|
||||||
|
|
||||||
m.img.Set(x+m.currentPage*8, height-y-1, c)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *mockBus) toImage() *image.RGBA {
|
|
||||||
|
|
||||||
container := image.NewRGBA(m.img.Bounds().Inset(-1))
|
|
||||||
draw.Draw(container, container.Bounds(), image.NewUniform(color.RGBA{G: 255, A: 255}), image.Point{}, draw.Over)
|
|
||||||
draw.Draw(container, m.img.Bounds(), m.img, image.Point{}, draw.Over)
|
|
||||||
return container
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDevice_Display(t *testing.T) {
|
|
||||||
|
|
||||||
bus := newMock()
|
|
||||||
dev := New(bus)
|
|
||||||
|
|
||||||
dev.Configure()
|
|
||||||
|
|
||||||
drawPlus(&dev)
|
|
||||||
drawHellowWorld(&dev)
|
|
||||||
|
|
||||||
//when
|
|
||||||
dev.Display()
|
|
||||||
|
|
||||||
//then
|
|
||||||
actual := bus.toImage()
|
|
||||||
|
|
||||||
expected, err := png.Decode(bytes.NewReader(expectedHelloWorld))
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
assertEqualImages(t, actual, expected)
|
|
||||||
}
|
|
||||||
|
|
||||||
func drawPlus(d drivers.Displayer) {
|
|
||||||
for i := int16(0); i < 128; i++ {
|
|
||||||
d.SetPixel(i, 32, color.RGBA{R: 1})
|
|
||||||
}
|
|
||||||
for i := int16(0); i < 64; i++ {
|
|
||||||
d.SetPixel(64, i, color.RGBA{R: 1})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func drawHellowWorld(d drivers.Displayer) {
|
|
||||||
tinyfont.WriteLine(d, &freemono.Regular9pt7b, 0, 32, "Hello World!", color.RGBA{R: 0xff, G: 0xff, B: 0xff, A: 0xff})
|
|
||||||
}
|
|
||||||
|
|
||||||
func assertEqualImages(t testing.TB, actual, expected image.Image) {
|
|
||||||
|
|
||||||
if actual.Bounds().Dx() != expected.Bounds().Dx() || actual.Bounds().Dy() != expected.Bounds().Dy() {
|
|
||||||
f := writeImage(actual)
|
|
||||||
t.Fatalf("differing size: was %v, expected %v, saved actual to %s", actual.Bounds(), expected.Bounds(), f)
|
|
||||||
}
|
|
||||||
|
|
||||||
bb := expected.Bounds()
|
|
||||||
for x := bb.Min.X; x < bb.Max.X; x++ {
|
|
||||||
for y := bb.Min.Y; y < bb.Max.Y; y++ {
|
|
||||||
actualBB := actual.Bounds()
|
|
||||||
if actual.At(x+actualBB.Min.X, y+actualBB.Min.Y) != expected.At(x, y) {
|
|
||||||
f := writeImage(actual)
|
|
||||||
t.Fatalf("different pixel at %d/%d: %v != %v, saved actual at %s", x, y, actual.At(x, y), expected.At(x, y), f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func writeImage(img image.Image) string {
|
|
||||||
|
|
||||||
fn := fmt.Sprintf("%d.png", time.Now().Unix())
|
|
||||||
f, err := os.OpenFile(fn, os.O_RDWR|os.O_CREATE, 0644)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
|
|
||||||
err = png.Encode(f, img)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return fn
|
|
||||||
}
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 449 B |
+3
-4
@@ -7,7 +7,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"tinygo.org/x/drivers"
|
"tinygo.org/x/drivers"
|
||||||
"tinygo.org/x/drivers/internal/legacy"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Error uint8
|
type Error uint8
|
||||||
@@ -55,7 +54,7 @@ func (d *Device) Configure() (err error) {
|
|||||||
// Connected returns whether sensor has been found.
|
// Connected returns whether sensor has been found.
|
||||||
func (d *Device) Connected() bool {
|
func (d *Device) Connected() bool {
|
||||||
data := []byte{0}
|
data := []byte{0}
|
||||||
legacy.ReadRegister(d.bus, uint8(d.Address), RegID, data)
|
d.bus.ReadRegister(uint8(d.Address), RegID, data)
|
||||||
return data[0]&0xF8 == 0xC8
|
return data[0]&0xF8 == 0xC8
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,11 +81,11 @@ func (d *Device) writeByte(reg uint8, data byte) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *Device) readByte(reg uint8) byte {
|
func (d *Device) readByte(reg uint8) byte {
|
||||||
legacy.ReadRegister(d.bus, d.Address, reg, d.buf)
|
d.bus.ReadRegister(d.Address, reg, d.buf)
|
||||||
return d.buf[0]
|
return d.buf[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Device) readUint16(reg uint8) uint16 {
|
func (d *Device) readUint16(reg uint8) uint16 {
|
||||||
legacy.ReadRegister(d.bus, d.Address, reg, d.buf)
|
d.bus.ReadRegister(d.Address, reg, d.buf)
|
||||||
return uint16(d.buf[0])<<8 | uint16(d.buf[1])
|
return uint16(d.buf[0])<<8 | uint16(d.buf[1])
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-13
@@ -5,10 +5,7 @@
|
|||||||
// Datasheet JP: http://www.analog.com/media/jp/technical-documentation/data-sheets/ADXL345_jp.pdf
|
// Datasheet JP: http://www.analog.com/media/jp/technical-documentation/data-sheets/ADXL345_jp.pdf
|
||||||
package adxl345 // import "tinygo.org/x/drivers/adxl345"
|
package adxl345 // import "tinygo.org/x/drivers/adxl345"
|
||||||
|
|
||||||
import (
|
import "tinygo.org/x/drivers"
|
||||||
"tinygo.org/x/drivers"
|
|
||||||
"tinygo.org/x/drivers/internal/legacy"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Range uint8
|
type Range uint8
|
||||||
type Rate uint8
|
type Rate uint8
|
||||||
@@ -71,21 +68,21 @@ func New(bus drivers.I2C) Device {
|
|||||||
|
|
||||||
// Configure sets up the device for communication
|
// Configure sets up the device for communication
|
||||||
func (d *Device) Configure() {
|
func (d *Device) Configure() {
|
||||||
legacy.WriteRegister(d.bus, uint8(d.Address), REG_BW_RATE, []byte{d.bwRate.toByte()})
|
d.bus.WriteRegister(uint8(d.Address), REG_BW_RATE, []byte{d.bwRate.toByte()})
|
||||||
legacy.WriteRegister(d.bus, uint8(d.Address), REG_POWER_CTL, []byte{d.powerCtl.toByte()})
|
d.bus.WriteRegister(uint8(d.Address), REG_POWER_CTL, []byte{d.powerCtl.toByte()})
|
||||||
legacy.WriteRegister(d.bus, uint8(d.Address), REG_DATA_FORMAT, []byte{d.dataFormat.toByte()})
|
d.bus.WriteRegister(uint8(d.Address), REG_DATA_FORMAT, []byte{d.dataFormat.toByte()})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Halt stops the sensor, values will not updated
|
// Halt stops the sensor, values will not updated
|
||||||
func (d *Device) Halt() {
|
func (d *Device) Halt() {
|
||||||
d.powerCtl.measure = 0
|
d.powerCtl.measure = 0
|
||||||
legacy.WriteRegister(d.bus, uint8(d.Address), REG_POWER_CTL, []byte{d.powerCtl.toByte()})
|
d.bus.WriteRegister(uint8(d.Address), REG_POWER_CTL, []byte{d.powerCtl.toByte()})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Restart makes reading the sensor working again after a halt
|
// Restart makes reading the sensor working again after a halt
|
||||||
func (d *Device) Restart() {
|
func (d *Device) Restart() {
|
||||||
d.powerCtl.measure = 1
|
d.powerCtl.measure = 1
|
||||||
legacy.WriteRegister(d.bus, uint8(d.Address), REG_POWER_CTL, []byte{d.powerCtl.toByte()})
|
d.bus.WriteRegister(uint8(d.Address), REG_POWER_CTL, []byte{d.powerCtl.toByte()})
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadAcceleration reads the current acceleration from the device and returns
|
// ReadAcceleration reads the current acceleration from the device and returns
|
||||||
@@ -106,7 +103,7 @@ func (d *Device) ReadAcceleration() (x int32, y int32, z int32, err error) {
|
|||||||
// from the adxl345.
|
// from the adxl345.
|
||||||
func (d *Device) ReadRawAcceleration() (x int32, y int32, z int32) {
|
func (d *Device) ReadRawAcceleration() (x int32, y int32, z int32) {
|
||||||
data := []byte{0, 0, 0, 0, 0, 0}
|
data := []byte{0, 0, 0, 0, 0, 0}
|
||||||
legacy.ReadRegister(d.bus, uint8(d.Address), REG_DATAX0, data)
|
d.bus.ReadRegister(uint8(d.Address), REG_DATAX0, data)
|
||||||
|
|
||||||
x = readIntLE(data[0], data[1])
|
x = readIntLE(data[0], data[1])
|
||||||
y = readIntLE(data[2], data[3])
|
y = readIntLE(data[2], data[3])
|
||||||
@@ -122,20 +119,20 @@ func (d *Device) UseLowPower(power bool) {
|
|||||||
} else {
|
} else {
|
||||||
d.bwRate.lowPower = 0
|
d.bwRate.lowPower = 0
|
||||||
}
|
}
|
||||||
legacy.WriteRegister(d.bus, uint8(d.Address), REG_BW_RATE, []byte{d.bwRate.toByte()})
|
d.bus.WriteRegister(uint8(d.Address), REG_BW_RATE, []byte{d.bwRate.toByte()})
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetRate change the current rate of the sensor
|
// SetRate change the current rate of the sensor
|
||||||
func (d *Device) SetRate(rate Rate) bool {
|
func (d *Device) SetRate(rate Rate) bool {
|
||||||
d.bwRate.rate = rate & 0x0F
|
d.bwRate.rate = rate & 0x0F
|
||||||
legacy.WriteRegister(d.bus, uint8(d.Address), REG_BW_RATE, []byte{d.bwRate.toByte()})
|
d.bus.WriteRegister(uint8(d.Address), REG_BW_RATE, []byte{d.bwRate.toByte()})
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetRange change the current range of the sensor
|
// SetRange change the current range of the sensor
|
||||||
func (d *Device) SetRange(sensorRange Range) bool {
|
func (d *Device) SetRange(sensorRange Range) bool {
|
||||||
d.dataFormat.sensorRange = sensorRange & 0x03
|
d.dataFormat.sensorRange = sensorRange & 0x03
|
||||||
legacy.WriteRegister(d.bus, uint8(d.Address), REG_DATA_FORMAT, []byte{d.dataFormat.toByte()})
|
d.bus.WriteRegister(uint8(d.Address), REG_DATA_FORMAT, []byte{d.dataFormat.toByte()})
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+16
-17
@@ -8,7 +8,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"tinygo.org/x/drivers"
|
"tinygo.org/x/drivers"
|
||||||
"tinygo.org/x/drivers/internal/legacy"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Device wraps an I2C connection to a AMG88xx device.
|
// Device wraps an I2C connection to a AMG88xx device.
|
||||||
@@ -49,7 +48,7 @@ func (d *Device) Configure(cfg Config) {
|
|||||||
|
|
||||||
// ReadPixels returns the 64 values (8x8 grid) of the sensor converted to millicelsius
|
// ReadPixels returns the 64 values (8x8 grid) of the sensor converted to millicelsius
|
||||||
func (d *Device) ReadPixels(buffer *[64]int16) {
|
func (d *Device) ReadPixels(buffer *[64]int16) {
|
||||||
legacy.ReadRegister(d.bus, uint8(d.Address), PIXEL_OFFSET, d.data)
|
d.bus.ReadRegister(uint8(d.Address), PIXEL_OFFSET, d.data)
|
||||||
for i := 0; i < 64; i++ {
|
for i := 0; i < 64; i++ {
|
||||||
buffer[i] = int16((uint16(d.data[2*i+1]) << 8) | uint16(d.data[2*i]))
|
buffer[i] = int16((uint16(d.data[2*i+1]) << 8) | uint16(d.data[2*i]))
|
||||||
if (buffer[i] & (1 << 11)) > 0 { // temperature negative
|
if (buffer[i] & (1 << 11)) > 0 { // temperature negative
|
||||||
@@ -62,17 +61,17 @@ func (d *Device) ReadPixels(buffer *[64]int16) {
|
|||||||
|
|
||||||
// SetPCTL sets the PCTL
|
// SetPCTL sets the PCTL
|
||||||
func (d *Device) SetPCTL(pctl uint8) {
|
func (d *Device) SetPCTL(pctl uint8) {
|
||||||
legacy.WriteRegister(d.bus, uint8(d.Address), PCTL, []byte{pctl})
|
d.bus.WriteRegister(uint8(d.Address), PCTL, []byte{pctl})
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetReset sets the reset value
|
// SetReset sets the reset value
|
||||||
func (d *Device) SetReset(rst uint8) {
|
func (d *Device) SetReset(rst uint8) {
|
||||||
legacy.WriteRegister(d.bus, uint8(d.Address), RST, []byte{rst})
|
d.bus.WriteRegister(uint8(d.Address), RST, []byte{rst})
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetFrameRate configures the frame rate
|
// SetFrameRate configures the frame rate
|
||||||
func (d *Device) SetFrameRate(framerate uint8) {
|
func (d *Device) SetFrameRate(framerate uint8) {
|
||||||
legacy.WriteRegister(d.bus, uint8(d.Address), FPSC, []byte{framerate & 0x01})
|
d.bus.WriteRegister(uint8(d.Address), FPSC, []byte{framerate & 0x01})
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetMovingAverageMode sets the moving average mode
|
// SetMovingAverageMode sets the moving average mode
|
||||||
@@ -81,7 +80,7 @@ func (d *Device) SetMovingAverageMode(mode bool) {
|
|||||||
if mode {
|
if mode {
|
||||||
value = 1
|
value = 1
|
||||||
}
|
}
|
||||||
legacy.WriteRegister(d.bus, uint8(d.Address), AVE, []byte{value << 5})
|
d.bus.WriteRegister(uint8(d.Address), AVE, []byte{value << 5})
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetInterruptLevels sets the interrupt levels
|
// SetInterruptLevels sets the interrupt levels
|
||||||
@@ -98,8 +97,8 @@ func (d *Device) SetInterruptLevelsHysteresis(high int16, low int16, hysteresis
|
|||||||
if high > 4095 {
|
if high > 4095 {
|
||||||
high = 4095
|
high = 4095
|
||||||
}
|
}
|
||||||
legacy.WriteRegister(d.bus, uint8(d.Address), INTHL, []byte{uint8(high & 0xFF)})
|
d.bus.WriteRegister(uint8(d.Address), INTHL, []byte{uint8(high & 0xFF)})
|
||||||
legacy.WriteRegister(d.bus, uint8(d.Address), INTHL, []byte{uint8((high & 0xFF) >> 4)})
|
d.bus.WriteRegister(uint8(d.Address), INTHL, []byte{uint8((high & 0xFF) >> 4)})
|
||||||
|
|
||||||
low = low / PIXEL_TEMP_CONVERSION
|
low = low / PIXEL_TEMP_CONVERSION
|
||||||
if low < -4095 {
|
if low < -4095 {
|
||||||
@@ -108,8 +107,8 @@ func (d *Device) SetInterruptLevelsHysteresis(high int16, low int16, hysteresis
|
|||||||
if low > 4095 {
|
if low > 4095 {
|
||||||
low = 4095
|
low = 4095
|
||||||
}
|
}
|
||||||
legacy.WriteRegister(d.bus, uint8(d.Address), INTHL, []byte{uint8(low & 0xFF)})
|
d.bus.WriteRegister(uint8(d.Address), INTHL, []byte{uint8(low & 0xFF)})
|
||||||
legacy.WriteRegister(d.bus, uint8(d.Address), INTHL, []byte{uint8((low & 0xFF) >> 4)})
|
d.bus.WriteRegister(uint8(d.Address), INTHL, []byte{uint8((low & 0xFF) >> 4)})
|
||||||
|
|
||||||
hysteresis = hysteresis / PIXEL_TEMP_CONVERSION
|
hysteresis = hysteresis / PIXEL_TEMP_CONVERSION
|
||||||
if hysteresis < -4095 {
|
if hysteresis < -4095 {
|
||||||
@@ -118,32 +117,32 @@ func (d *Device) SetInterruptLevelsHysteresis(high int16, low int16, hysteresis
|
|||||||
if hysteresis > 4095 {
|
if hysteresis > 4095 {
|
||||||
hysteresis = 4095
|
hysteresis = 4095
|
||||||
}
|
}
|
||||||
legacy.WriteRegister(d.bus, uint8(d.Address), INTHL, []byte{uint8(hysteresis & 0xFF)})
|
d.bus.WriteRegister(uint8(d.Address), INTHL, []byte{uint8(hysteresis & 0xFF)})
|
||||||
legacy.WriteRegister(d.bus, uint8(d.Address), INTHL, []byte{uint8((hysteresis & 0xFF) >> 4)})
|
d.bus.WriteRegister(uint8(d.Address), INTHL, []byte{uint8((hysteresis & 0xFF) >> 4)})
|
||||||
}
|
}
|
||||||
|
|
||||||
// EnableInterrupt enables the interrupt pin on the device
|
// EnableInterrupt enables the interrupt pin on the device
|
||||||
func (d *Device) EnableInterrupt() {
|
func (d *Device) EnableInterrupt() {
|
||||||
d.interruptEnable = 1
|
d.interruptEnable = 1
|
||||||
legacy.WriteRegister(d.bus, uint8(d.Address), INTC, []byte{((uint8(d.interruptMode) << 1) | d.interruptEnable) & 0x03})
|
d.bus.WriteRegister(uint8(d.Address), INTC, []byte{((uint8(d.interruptMode) << 1) | d.interruptEnable) & 0x03})
|
||||||
}
|
}
|
||||||
|
|
||||||
// DisableInterrupt disables the interrupt pin on the device
|
// DisableInterrupt disables the interrupt pin on the device
|
||||||
func (d *Device) DisableInterrupt() {
|
func (d *Device) DisableInterrupt() {
|
||||||
d.interruptEnable = 0
|
d.interruptEnable = 0
|
||||||
legacy.WriteRegister(d.bus, uint8(d.Address), INTC, []byte{((uint8(d.interruptMode) << 1) | d.interruptEnable) & 0x03})
|
d.bus.WriteRegister(uint8(d.Address), INTC, []byte{((uint8(d.interruptMode) << 1) | d.interruptEnable) & 0x03})
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetInterruptMode sets the interrupt mode
|
// SetInterruptMode sets the interrupt mode
|
||||||
func (d *Device) SetInterruptMode(mode InterruptMode) {
|
func (d *Device) SetInterruptMode(mode InterruptMode) {
|
||||||
d.interruptMode = mode
|
d.interruptMode = mode
|
||||||
legacy.WriteRegister(d.bus, uint8(d.Address), INTC, []byte{((uint8(d.interruptMode) << 1) | d.interruptEnable) & 0x03})
|
d.bus.WriteRegister(uint8(d.Address), INTC, []byte{((uint8(d.interruptMode) << 1) | d.interruptEnable) & 0x03})
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetInterrupt reads the state of the triggered interrupts
|
// GetInterrupt reads the state of the triggered interrupts
|
||||||
func (d *Device) GetInterrupt() []uint8 {
|
func (d *Device) GetInterrupt() []uint8 {
|
||||||
data := make([]uint8, 8)
|
data := make([]uint8, 8)
|
||||||
legacy.ReadRegister(d.bus, uint8(d.Address), INT_OFFSET, data)
|
d.bus.ReadRegister(uint8(d.Address), INT_OFFSET, data)
|
||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -155,6 +154,6 @@ func (d *Device) ClearInterrupt() {
|
|||||||
// ReadThermistor reads the onboard thermistor
|
// ReadThermistor reads the onboard thermistor
|
||||||
func (d *Device) ReadThermistor() int16 {
|
func (d *Device) ReadThermistor() int16 {
|
||||||
data := make([]uint8, 2)
|
data := make([]uint8, 2)
|
||||||
legacy.ReadRegister(d.bus, uint8(d.Address), TTHL, data)
|
d.bus.ReadRegister(uint8(d.Address), TTHL, data)
|
||||||
return (int16((uint16(data[1])<<8)|uint16(data[0])) * THERMISTOR_CONVERSION) / 10
|
return (int16((uint16(data[1])<<8)|uint16(data[0])) * THERMISTOR_CONVERSION) / 10
|
||||||
}
|
}
|
||||||
|
|||||||
+25
-26
@@ -8,7 +8,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"tinygo.org/x/drivers"
|
"tinygo.org/x/drivers"
|
||||||
"tinygo.org/x/drivers/internal/legacy"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Device wraps an I2C connection to a APDS-9960 device.
|
// Device wraps an I2C connection to a APDS-9960 device.
|
||||||
@@ -69,7 +68,7 @@ func New(bus drivers.I2C) Device {
|
|||||||
// It does a "who am I" request and checks the response.
|
// It does a "who am I" request and checks the response.
|
||||||
func (d *Device) Connected() bool {
|
func (d *Device) Connected() bool {
|
||||||
data := []byte{0}
|
data := []byte{0}
|
||||||
legacy.ReadRegister(d.bus, d.Address, APDS9960_ID_REG, data)
|
d.bus.ReadRegister(d.Address, APDS9960_ID_REG, data)
|
||||||
return data[0] == 0xAB
|
return data[0] == 0xAB
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,7 +80,7 @@ func (d *Device) GetMode() uint8 {
|
|||||||
// DisableAll turns off the device and all functions
|
// DisableAll turns off the device and all functions
|
||||||
func (d *Device) DisableAll() {
|
func (d *Device) DisableAll() {
|
||||||
d.enable(enableConfig{})
|
d.enable(enableConfig{})
|
||||||
legacy.WriteRegister(d.bus, d.Address, APDS9960_GCONF4_REG, []byte{0x00})
|
d.bus.WriteRegister(d.Address, APDS9960_GCONF4_REG, []byte{0x00})
|
||||||
d.mode = MODE_NONE
|
d.mode = MODE_NONE
|
||||||
d.gesture.detected = GESTURE_NONE
|
d.gesture.detected = GESTURE_NONE
|
||||||
}
|
}
|
||||||
@@ -89,13 +88,13 @@ func (d *Device) DisableAll() {
|
|||||||
// SetProximityPulse sets proximity pulse length (4, 8, 16, 32) and count (1~64)
|
// SetProximityPulse sets proximity pulse length (4, 8, 16, 32) and count (1~64)
|
||||||
// default: 16, 64
|
// default: 16, 64
|
||||||
func (d *Device) SetProximityPulse(length, count uint8) {
|
func (d *Device) SetProximityPulse(length, count uint8) {
|
||||||
legacy.WriteRegister(d.bus, d.Address, APDS9960_PPULSE_REG, []byte{getPulseLength(length)<<6 | getPulseCount(count)})
|
d.bus.WriteRegister(d.Address, APDS9960_PPULSE_REG, []byte{getPulseLength(length)<<6 | getPulseCount(count)})
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetGesturePulse sets gesture pulse length (4, 8, 16, 32) and count (1~64)
|
// SetGesturePulse sets gesture pulse length (4, 8, 16, 32) and count (1~64)
|
||||||
// default: 16, 64
|
// default: 16, 64
|
||||||
func (d *Device) SetGesturePulse(length, count uint8) {
|
func (d *Device) SetGesturePulse(length, count uint8) {
|
||||||
legacy.WriteRegister(d.bus, d.Address, APDS9960_GPULSE_REG, []byte{getPulseLength(length)<<6 | getPulseCount(count)})
|
d.bus.WriteRegister(d.Address, APDS9960_GPULSE_REG, []byte{getPulseLength(length)<<6 | getPulseCount(count)})
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetADCIntegrationCycles sets ALS/color ADC internal integration cycles (1~256, 1 cycle = 2.78 ms)
|
// SetADCIntegrationCycles sets ALS/color ADC internal integration cycles (1~256, 1 cycle = 2.78 ms)
|
||||||
@@ -104,14 +103,14 @@ func (d *Device) SetADCIntegrationCycles(cycles uint16) {
|
|||||||
if cycles > 256 {
|
if cycles > 256 {
|
||||||
cycles = 256
|
cycles = 256
|
||||||
}
|
}
|
||||||
legacy.WriteRegister(d.bus, d.Address, APDS9960_ATIME_REG, []byte{uint8(256 - cycles)})
|
d.bus.WriteRegister(d.Address, APDS9960_ATIME_REG, []byte{uint8(256 - cycles)})
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetGains sets proximity/gesture gain (1, 2, 4, 8x) and ALS/color gain (1, 4, 16, 64x)
|
// SetGains sets proximity/gesture gain (1, 2, 4, 8x) and ALS/color gain (1, 4, 16, 64x)
|
||||||
// default: 1, 1, 4
|
// default: 1, 1, 4
|
||||||
func (d *Device) SetGains(proximityGain, gestureGain, colorGain uint8) {
|
func (d *Device) SetGains(proximityGain, gestureGain, colorGain uint8) {
|
||||||
legacy.WriteRegister(d.bus, d.Address, APDS9960_CONTROL_REG, []byte{getProximityGain(proximityGain)<<2 | getALSGain(colorGain)})
|
d.bus.WriteRegister(d.Address, APDS9960_CONTROL_REG, []byte{getProximityGain(proximityGain)<<2 | getALSGain(colorGain)})
|
||||||
legacy.WriteRegister(d.bus, d.Address, APDS9960_GCONF2_REG, []byte{getProximityGain(gestureGain) << 5})
|
d.bus.WriteRegister(d.Address, APDS9960_GCONF2_REG, []byte{getProximityGain(gestureGain) << 5})
|
||||||
}
|
}
|
||||||
|
|
||||||
// LEDBoost sets proximity and gesture LED current level (100, 150, 200, 300 (%))
|
// LEDBoost sets proximity and gesture LED current level (100, 150, 200, 300 (%))
|
||||||
@@ -128,7 +127,7 @@ func (d *Device) LEDBoost(percent uint16) {
|
|||||||
case 300:
|
case 300:
|
||||||
v = 3
|
v = 3
|
||||||
}
|
}
|
||||||
legacy.WriteRegister(d.bus, d.Address, APDS9960_CONFIG2_REG, []byte{0x01 | v<<4})
|
d.bus.WriteRegister(d.Address, APDS9960_CONFIG2_REG, []byte{0x01 | v<<4})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setthreshold sets threshold (0~255) for detecting gestures
|
// Setthreshold sets threshold (0~255) for detecting gestures
|
||||||
@@ -169,7 +168,7 @@ func (d *Device) ReadProximity() (proximity int32) {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
data := []byte{0}
|
data := []byte{0}
|
||||||
legacy.ReadRegister(d.bus, d.Address, APDS9960_PDATA_REG, data)
|
d.bus.ReadRegister(d.Address, APDS9960_PDATA_REG, data)
|
||||||
return 255 - int32(data[0])
|
return 255 - int32(data[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -196,14 +195,14 @@ func (d *Device) ReadColor() (r int32, g int32, b int32, clear int32) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
data := []byte{0, 0, 0, 0, 0, 0, 0, 0}
|
data := []byte{0, 0, 0, 0, 0, 0, 0, 0}
|
||||||
legacy.ReadRegister(d.bus, d.Address, APDS9960_CDATAL_REG, data[:1])
|
d.bus.ReadRegister(d.Address, APDS9960_CDATAL_REG, data[:1])
|
||||||
legacy.ReadRegister(d.bus, d.Address, APDS9960_CDATAH_REG, data[1:2])
|
d.bus.ReadRegister(d.Address, APDS9960_CDATAH_REG, data[1:2])
|
||||||
legacy.ReadRegister(d.bus, d.Address, APDS9960_RDATAL_REG, data[2:3])
|
d.bus.ReadRegister(d.Address, APDS9960_RDATAL_REG, data[2:3])
|
||||||
legacy.ReadRegister(d.bus, d.Address, APDS9960_RDATAH_REG, data[3:4])
|
d.bus.ReadRegister(d.Address, APDS9960_RDATAH_REG, data[3:4])
|
||||||
legacy.ReadRegister(d.bus, d.Address, APDS9960_GDATAL_REG, data[4:5])
|
d.bus.ReadRegister(d.Address, APDS9960_GDATAL_REG, data[4:5])
|
||||||
legacy.ReadRegister(d.bus, d.Address, APDS9960_GDATAH_REG, data[5:6])
|
d.bus.ReadRegister(d.Address, APDS9960_GDATAH_REG, data[5:6])
|
||||||
legacy.ReadRegister(d.bus, d.Address, APDS9960_BDATAL_REG, data[6:7])
|
d.bus.ReadRegister(d.Address, APDS9960_BDATAL_REG, data[6:7])
|
||||||
legacy.ReadRegister(d.bus, d.Address, APDS9960_BDATAH_REG, data[7:])
|
d.bus.ReadRegister(d.Address, APDS9960_BDATAH_REG, data[7:])
|
||||||
clear = int32(uint16(data[1])<<8 | uint16(data[0]))
|
clear = int32(uint16(data[1])<<8 | uint16(data[0]))
|
||||||
r = int32(uint16(data[3])<<8 | uint16(data[2]))
|
r = int32(uint16(data[3])<<8 | uint16(data[2]))
|
||||||
g = int32(uint16(data[5])<<8 | uint16(data[4]))
|
g = int32(uint16(data[5])<<8 | uint16(data[4]))
|
||||||
@@ -235,13 +234,13 @@ func (d *Device) GestureAvailable() bool {
|
|||||||
data := []byte{0, 0, 0, 0}
|
data := []byte{0, 0, 0, 0}
|
||||||
|
|
||||||
// check GVALID
|
// check GVALID
|
||||||
legacy.ReadRegister(d.bus, d.Address, APDS9960_GSTATUS_REG, data[:1])
|
d.bus.ReadRegister(d.Address, APDS9960_GSTATUS_REG, data[:1])
|
||||||
if data[0]&0x01 == 0 {
|
if data[0]&0x01 == 0 {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// get number of data sets available in FIFO
|
// get number of data sets available in FIFO
|
||||||
legacy.ReadRegister(d.bus, d.Address, APDS9960_GFLVL_REG, data[:1])
|
d.bus.ReadRegister(d.Address, APDS9960_GFLVL_REG, data[:1])
|
||||||
availableDataSets := data[0]
|
availableDataSets := data[0]
|
||||||
if availableDataSets == 0 {
|
if availableDataSets == 0 {
|
||||||
return false
|
return false
|
||||||
@@ -250,10 +249,10 @@ func (d *Device) GestureAvailable() bool {
|
|||||||
// read up, down, left and right proximity data from FIFO
|
// read up, down, left and right proximity data from FIFO
|
||||||
var dataSets [32][4]uint8
|
var dataSets [32][4]uint8
|
||||||
for i := uint8(0); i < availableDataSets; i++ {
|
for i := uint8(0); i < availableDataSets; i++ {
|
||||||
legacy.ReadRegister(d.bus, d.Address, APDS9960_GFIFO_U_REG, data[:1])
|
d.bus.ReadRegister(d.Address, APDS9960_GFIFO_U_REG, data[:1])
|
||||||
legacy.ReadRegister(d.bus, d.Address, APDS9960_GFIFO_D_REG, data[1:2])
|
d.bus.ReadRegister(d.Address, APDS9960_GFIFO_D_REG, data[1:2])
|
||||||
legacy.ReadRegister(d.bus, d.Address, APDS9960_GFIFO_L_REG, data[2:3])
|
d.bus.ReadRegister(d.Address, APDS9960_GFIFO_L_REG, data[2:3])
|
||||||
legacy.ReadRegister(d.bus, d.Address, APDS9960_GFIFO_R_REG, data[3:4])
|
d.bus.ReadRegister(d.Address, APDS9960_GFIFO_R_REG, data[3:4])
|
||||||
for j := uint8(0); j < 4; j++ {
|
for j := uint8(0); j < 4; j++ {
|
||||||
dataSets[i][j] = data[j]
|
dataSets[i][j] = data[j]
|
||||||
}
|
}
|
||||||
@@ -386,7 +385,7 @@ func (d *Device) enable(cfg enableConfig) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
data := []byte{gen<<6 | pien<<5 | aien<<4 | wen<<3 | pen<<2 | aen<<1 | pon}
|
data := []byte{gen<<6 | pien<<5 | aien<<4 | wen<<3 | pen<<2 | aen<<1 | pon}
|
||||||
legacy.WriteRegister(d.bus, d.Address, APDS9960_ENABLE_REG, data)
|
d.bus.WriteRegister(d.Address, APDS9960_ENABLE_REG, data)
|
||||||
|
|
||||||
if cfg.PON {
|
if cfg.PON {
|
||||||
time.Sleep(time.Millisecond * 10)
|
time.Sleep(time.Millisecond * 10)
|
||||||
@@ -395,7 +394,7 @@ func (d *Device) enable(cfg enableConfig) {
|
|||||||
|
|
||||||
func (d *Device) readStatus(param string) bool {
|
func (d *Device) readStatus(param string) bool {
|
||||||
data := []byte{0}
|
data := []byte{0}
|
||||||
legacy.ReadRegister(d.bus, d.Address, APDS9960_STATUS_REG, data)
|
d.bus.ReadRegister(d.Address, APDS9960_STATUS_REG, data)
|
||||||
|
|
||||||
switch param {
|
switch param {
|
||||||
case "CPSAT":
|
case "CPSAT":
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
//go:build !nano_33_ble
|
//go:build !nano_33_ble
|
||||||
|
// +build !nano_33_ble
|
||||||
|
|
||||||
package apds9960
|
package apds9960
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
//go:build nano_33_ble
|
//go:build nano_33_ble
|
||||||
|
// +build nano_33_ble
|
||||||
|
|
||||||
package apds9960
|
package apds9960
|
||||||
|
|
||||||
|
|||||||
@@ -1,172 +0,0 @@
|
|||||||
// Product: https://ams.com/as5600
|
|
||||||
// Datasheet: https://ams.com/documents/20143/36005/AS5600_DS000365_5-00.pdf
|
|
||||||
|
|
||||||
package as560x // import tinygo.org/x/drivers/ams560x
|
|
||||||
|
|
||||||
import (
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"tinygo.org/x/drivers"
|
|
||||||
)
|
|
||||||
|
|
||||||
// AS5600 includes MPOS & MANG in addition to ZPOS to set a 'narrower angle range'
|
|
||||||
// ZPOS enables setting the 'zero position' of the device to any RAW_ANGLE value.
|
|
||||||
// MPOS ('max position') & MANG 'max angle' enable a 'partial range' on the AS5600.
|
|
||||||
// The value in ANGLE is scaled & adjusted by the device according to ZPOS and MPOS/MANG.
|
|
||||||
// The entire 12-bit range is 'compressed' into the RAW_ANGLE range of ZPOS->MPOS
|
|
||||||
// (or ZPOS->ZPOS+MANG) thus enabling a higher resolution for a partial range.
|
|
||||||
// if ZPOS > MPOS (or ZPOS + MANG > 4095) i.e. the incremental range 'crosses zero'
|
|
||||||
// then the device will automatically compensate for the correct range.
|
|
||||||
// For RAW_ANGLE values outside of the partial range, ANGLE will be 'capped' at either
|
|
||||||
// 0 or 4095, depending on 'which end of the partial range is closer.'
|
|
||||||
|
|
||||||
// AS5600Device represents an ams AS5600 device driver accessed over I2C
|
|
||||||
type AS5600Device struct {
|
|
||||||
// promote BaseDevice
|
|
||||||
BaseDevice
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewAS5600 creates a new AS5600Device given an I2C bus
|
|
||||||
func NewAS5600(bus drivers.I2C) AS5600Device {
|
|
||||||
// Create base device
|
|
||||||
baseDev := newBaseDevice(bus)
|
|
||||||
// Add AS5600 specific registers
|
|
||||||
baseDev.registers[MPOS] = newI2CRegister(MPOS, 0, 0xfff, 2, reg_read|reg_write|reg_program)
|
|
||||||
baseDev.registers[MANG] = newI2CRegister(MANG, 0, 0xfff, 2, reg_read|reg_write|reg_program)
|
|
||||||
// Add AS5600 specific 'virtual registers'
|
|
||||||
conf, ok := baseDev.registers[CONF]
|
|
||||||
if ok {
|
|
||||||
baseDev.registers[PWMF] = newVirtualRegister(conf, 6, 0b11)
|
|
||||||
baseDev.registers[OUTS] = newVirtualRegister(conf, 4, 0b11)
|
|
||||||
}
|
|
||||||
// Return the device
|
|
||||||
return AS5600Device{baseDev}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Configure sets up the AMS AS5600 sensor device with the given configuration.
|
|
||||||
func (d *AS5600Device) Configure(cfg Config) error {
|
|
||||||
// Call the BaseDevice method to do the actual Configure
|
|
||||||
d.BaseDevice.Configure(cfg)
|
|
||||||
// For AS5600 devices we need to calculate the maxAngle on startup from ZPOS/MPOS/MANG
|
|
||||||
// These could have been permanently BURN'ed (by writing BURN register with BURN_ANGLE/BURN_SETTING)
|
|
||||||
// or may have already been written in previous runs without a power cycle since.
|
|
||||||
mpos, err := d.ReadRegister(MPOS)
|
|
||||||
if nil != err {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
mang, err := d.ReadRegister(MANG)
|
|
||||||
if nil != err {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// Read ZPOS for side effect of caching only so that next calculateEffectiveMaxAngle() can't fail
|
|
||||||
if _, err = d.ReadRegister(ZPOS); nil != err {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if mpos != 0 {
|
|
||||||
// If MPOS is set, use MPOS regardless of MANG
|
|
||||||
err = d.calculateEffectiveMaxAngle(MPOS, mpos)
|
|
||||||
} else if mang != 0 {
|
|
||||||
// If MANG is set and MPOS == 0, use MANG
|
|
||||||
err = d.calculateEffectiveMaxAngle(MANG, mang)
|
|
||||||
} else {
|
|
||||||
// if neither is set, we have no narrow range
|
|
||||||
d.maxAngle = NATIVE_ANGLE_RANGE
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// calculateEffectiveMaxAngle calculates d.maxAngle after one of ZPOS/MPOS/MANG have been written
|
|
||||||
func (d *AS5600Device) calculateEffectiveMaxAngle(register uint8, value uint16) error {
|
|
||||||
|
|
||||||
var zpos, mpos uint16 = 0, 0
|
|
||||||
var err error = nil
|
|
||||||
|
|
||||||
switch register {
|
|
||||||
case MANG:
|
|
||||||
d.maxAngle = value // The easy case
|
|
||||||
return nil
|
|
||||||
case ZPOS:
|
|
||||||
zpos = value
|
|
||||||
mpos, err = d.ReadRegister(MPOS)
|
|
||||||
case MPOS:
|
|
||||||
mpos = value
|
|
||||||
zpos, err = d.ReadRegister(ZPOS)
|
|
||||||
default:
|
|
||||||
panic("calculateEffectiveMaxAngle() can only work from ZPOS, MPOS or MANG")
|
|
||||||
}
|
|
||||||
|
|
||||||
if nil != err {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// MANG is effectively MPOS-ZPOS
|
|
||||||
mang := int(mpos) - int(zpos)
|
|
||||||
// correct for mpos < zpos
|
|
||||||
if mang < 0 {
|
|
||||||
mang += NATIVE_ANGLE_RANGE
|
|
||||||
}
|
|
||||||
d.maxAngle = uint16(mang)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriteRegister writes the given value for the given register to the AS560x device via I2C
|
|
||||||
func (d *AS5600Device) WriteRegister(address uint8, value uint16) error {
|
|
||||||
// Call the BaseDevice method to do the actual write
|
|
||||||
if err := d.BaseDevice.WriteRegister(address, value); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// When either ZPOS/MANG/MPOS are set we need to recalculate maxAngle
|
|
||||||
// We also may need to invalidate some cached values for the other two registers
|
|
||||||
recalc := false
|
|
||||||
switch address {
|
|
||||||
case ZPOS:
|
|
||||||
// Setting a new ZPOS invalidates MPOS but not MANG
|
|
||||||
d.registers[MPOS].invalidate()
|
|
||||||
recalc = true
|
|
||||||
case MPOS:
|
|
||||||
// Setting a new MPOS invalidates MANG but not ZPOS
|
|
||||||
d.registers[MANG].invalidate()
|
|
||||||
recalc = true
|
|
||||||
case MANG:
|
|
||||||
// Setting a new MANG invalidates MPOS but not ZPOS
|
|
||||||
d.registers[MPOS].invalidate()
|
|
||||||
recalc = true
|
|
||||||
}
|
|
||||||
if recalc {
|
|
||||||
// Datasheet tells us to wait at least 1ms before reading back
|
|
||||||
time.Sleep(time.Millisecond * 10) // conservative wait
|
|
||||||
return d.calculateEffectiveMaxAngle(address, value)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetMaxPosition returns the 'max position' (MPOS) in different units
|
|
||||||
func (d *AS5600Device) GetMaxPosition(units AngleUnit) (uint16, float32, error) {
|
|
||||||
mpos, err := d.ReadRegister(MPOS)
|
|
||||||
if nil != err {
|
|
||||||
return 0, 0.0, err
|
|
||||||
}
|
|
||||||
// Convert to requested units
|
|
||||||
i, f := convertFromNativeAngle(mpos, NATIVE_ANGLE_RANGE, units)
|
|
||||||
return i, f, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetMaxPosition sets the 'max position' (MPOS) in different units
|
|
||||||
func (d *AS5600Device) SetMaxPosition(mpos float32, units AngleUnit) error {
|
|
||||||
return d.WriteRegister(MPOS, convertToNativeAngle(mpos, units))
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetMaxAngle returns the 'max position' (MANG) in different units
|
|
||||||
func (d *AS5600Device) GetMaxAngle(units AngleUnit) (uint16, float32, error) {
|
|
||||||
mang, err := d.ReadRegister(MANG)
|
|
||||||
if nil != err {
|
|
||||||
return 0, 0.0, err
|
|
||||||
}
|
|
||||||
// Convert to requested units
|
|
||||||
i, f := convertFromNativeAngle(mang, NATIVE_ANGLE_RANGE, units)
|
|
||||||
return i, f, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetMaxAngle sets the 'max angle' (MANG) in different units
|
|
||||||
func (d *AS5600Device) SetMaxAngle(mang float32, units AngleUnit) error {
|
|
||||||
return d.WriteRegister(MANG, convertToNativeAngle(mang, units))
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
// Product: https://ams.com/as5601
|
|
||||||
// Datasheet: https://ams.com/documents/20143/36005/AS5601_DS000395_3-00.pdf
|
|
||||||
|
|
||||||
package as560x // import tinygo.org/x/drivers/ams560x
|
|
||||||
|
|
||||||
import "tinygo.org/x/drivers"
|
|
||||||
|
|
||||||
// AS5601Device represents an ams AS5601 device driver accessed over I2C
|
|
||||||
type AS5601Device struct {
|
|
||||||
BaseDevice // promote base device
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewAS5601 creates a new AS5601Device given an I2C bus
|
|
||||||
func NewAS5601(bus drivers.I2C) AS5601Device {
|
|
||||||
// Create base device
|
|
||||||
baseDev := newBaseDevice(bus)
|
|
||||||
// Add AS5601 specific registers
|
|
||||||
baseDev.registers[ABN] = newI2CRegister(ABN, 0, 0b1111, 1, reg_read|reg_write|reg_program)
|
|
||||||
baseDev.registers[PUSHTHR] = newI2CRegister(PUSHTHR, 0, 0xff, 1, reg_read|reg_write|reg_program)
|
|
||||||
// Return the device
|
|
||||||
return AS5601Device{baseDev}
|
|
||||||
}
|
|
||||||
@@ -1,198 +0,0 @@
|
|||||||
// Package as560x implements drivers for the ams AS5600/AS5601 on-axis magnetic rotary position sensors
|
|
||||||
//
|
|
||||||
// Product Pages:
|
|
||||||
// AS5600: https://ams.com/as5600
|
|
||||||
// AS5601: https://ams.com/as5601
|
|
||||||
//
|
|
||||||
// Datasheets:
|
|
||||||
// AS5600: https://ams.com/documents/20143/36005/AS5600_DS000365_5-00.pdf
|
|
||||||
// AS5601: https://ams.com/documents/20143/36005/AS5601_DS000395_3-00.pdf
|
|
||||||
//
|
|
||||||
|
|
||||||
package as560x // import tinygo.org/x/drivers/ams560x
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
|
|
||||||
"tinygo.org/x/drivers"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Config holds the configuration for the AMS AS560x sensor devices.
|
|
||||||
type Config struct {
|
|
||||||
// Address is the I2C address of the AS560x device. If left zero this will default to 0x36
|
|
||||||
Address uint8
|
|
||||||
}
|
|
||||||
|
|
||||||
// MagnetStrength is an enum to indicate the magnetic field strength detected by the AS560x sensors.
|
|
||||||
type MagnetStrength int
|
|
||||||
|
|
||||||
const (
|
|
||||||
// MagnetTooWeak indicates that the magnet strength is too weak (AGC maximum gain overflow) - move it closer
|
|
||||||
MagnetTooWeak MagnetStrength = iota - 1
|
|
||||||
// MagnetOk indicates that the magnet strength is about right.
|
|
||||||
MagnetOk
|
|
||||||
// MagnetTooStrong indicates that the magnet strength is too strong (AGC minimum gain overflow) - move it further away
|
|
||||||
MagnetTooStrong
|
|
||||||
)
|
|
||||||
|
|
||||||
// AngleUnit is an enum to allow the use of different units when reading/writing angles from the AS560x sensors.
|
|
||||||
type AngleUnit int
|
|
||||||
|
|
||||||
const (
|
|
||||||
// ANGLE_NATIVE uses the device's native angle measurement. i.e. 12-bit integer, 0 <= angle <= 0xfff (4095)
|
|
||||||
ANGLE_NATIVE AngleUnit = iota
|
|
||||||
// ANGLE_DEGREES_INT measures angles in degrees using integer arithmetic for speed. i.e. 0 <= angle < 360
|
|
||||||
ANGLE_DEGREES_INT
|
|
||||||
// ANGLE_DEGREES_FLOAT measures angles in degrees using floating point (slower). i.e. 0.0 <= angle < 360.0
|
|
||||||
ANGLE_DEGREES_FLOAT
|
|
||||||
// ANGLE_RADIANS measures angles in radians using floating point (slower). i.e. 0.0 <= angle < 2 * PI
|
|
||||||
ANGLE_RADIANS
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// NATIVE_ANGLE_MAX is the maximum valid value for a native angle for a AS560x device
|
|
||||||
NATIVE_ANGLE_MAX = (1 << 12) - 1 + iota
|
|
||||||
// NATIVE_ANGLE_RANGE is the number of unique values for native angles for a AS560x device
|
|
||||||
NATIVE_ANGLE_RANGE
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
errRegisterNotFound = errors.New("Register not found")
|
|
||||||
errMaxBurnAngle = errors.New("Max BURN_ANGLE limit reached")
|
|
||||||
)
|
|
||||||
|
|
||||||
// BaseDevice handles the common behaviour between AS5600 & AS5601 devices
|
|
||||||
type BaseDevice struct {
|
|
||||||
bus drivers.I2C
|
|
||||||
address uint8
|
|
||||||
registers map[uint8]*i2cRegister
|
|
||||||
maxAngle uint16
|
|
||||||
}
|
|
||||||
|
|
||||||
// newBaseDevice creates a new base device given an I2C bus.
|
|
||||||
func newBaseDevice(bus drivers.I2C) BaseDevice {
|
|
||||||
// Add all 'base' registers, common to both AS5600 & AS5601
|
|
||||||
conf := newI2CRegister(CONF, 0, 0x3fff, 2, reg_read|reg_write|reg_program)
|
|
||||||
status := newI2CRegister(STATUS, 0, 0xff, 1, reg_read)
|
|
||||||
regs := map[uint8]*i2cRegister{
|
|
||||||
ZPOS: newI2CRegister(ZPOS, 0, 0xfff, 2, reg_read|reg_write|reg_program),
|
|
||||||
CONF: conf,
|
|
||||||
RAW_ANGLE: newI2CRegister(RAW_ANGLE, 0, 0xfff, 2, reg_read),
|
|
||||||
ANGLE: newI2CRegister(ANGLE, 0, 0xfff, 2, reg_read),
|
|
||||||
STATUS: status,
|
|
||||||
AGC: newI2CRegister(AGC, 0, 0xff, 1, reg_read),
|
|
||||||
MAGNITUDE: newI2CRegister(MAGNITUDE, 0, 0xfff, 2, reg_read),
|
|
||||||
BURN: newI2CRegister(BURN, 0, 0xff, 1, reg_write),
|
|
||||||
// Add common 'virtual registers' These are bitfields within the common registers above
|
|
||||||
// A virtual register provides a convenient way to access the fields of a registers
|
|
||||||
// by handling all of the necessary bitfield shifting and masking operations
|
|
||||||
WD: newVirtualRegister(conf, 13, 0b1),
|
|
||||||
FTH: newVirtualRegister(conf, 10, 0b111),
|
|
||||||
SF: newVirtualRegister(conf, 8, 0b11),
|
|
||||||
HYST: newVirtualRegister(conf, 2, 0b11),
|
|
||||||
PM: newVirtualRegister(conf, 0, 0b11),
|
|
||||||
MD: newVirtualRegister(status, 5, 0b1),
|
|
||||||
ML: newVirtualRegister(status, 4, 0b1),
|
|
||||||
MH: newVirtualRegister(status, 3, 0b1),
|
|
||||||
}
|
|
||||||
return BaseDevice{bus, DefaultAddress, regs, NATIVE_ANGLE_RANGE}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Configure sets up the AMS AS560x sensor device with the given configuration.
|
|
||||||
func (d *BaseDevice) Configure(cfg Config) {
|
|
||||||
if cfg.Address == 0 {
|
|
||||||
cfg.Address = DefaultAddress
|
|
||||||
}
|
|
||||||
d.address = cfg.Address
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadRegister reads the value for the given register from the AS560x device via I2C
|
|
||||||
func (d *BaseDevice) ReadRegister(address uint8) (uint16, error) {
|
|
||||||
reg, ok := d.registers[address]
|
|
||||||
if !ok {
|
|
||||||
return 0, errRegisterNotFound
|
|
||||||
}
|
|
||||||
return reg.read(d.bus, d.address)
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriteRegister writes the given value for the given register to the AS560x device via I2C
|
|
||||||
func (d *BaseDevice) WriteRegister(address uint8, value uint16) error {
|
|
||||||
reg, ok := d.registers[address]
|
|
||||||
if !ok {
|
|
||||||
return errRegisterNotFound
|
|
||||||
}
|
|
||||||
return reg.write(d.bus, d.address, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetZeroPosition returns the 'zero position' (ZPOS) in various units
|
|
||||||
func (d *BaseDevice) GetZeroPosition(units AngleUnit) (uint16, float32, error) {
|
|
||||||
zpos, err := d.ReadRegister(ZPOS)
|
|
||||||
if nil != err {
|
|
||||||
return 0, 0.0, err
|
|
||||||
}
|
|
||||||
// Convert to requested units
|
|
||||||
i, f := convertFromNativeAngle(zpos, NATIVE_ANGLE_RANGE, units)
|
|
||||||
return i, f, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetZeroPosition sets the 'zero position' (ZPOS) in various units
|
|
||||||
func (d *BaseDevice) SetZeroPosition(zpos float32, units AngleUnit) error {
|
|
||||||
return d.WriteRegister(ZPOS, convertToNativeAngle(zpos, units))
|
|
||||||
}
|
|
||||||
|
|
||||||
// RawAngle reads the (unscaled & unadjusted) RAW_ANGLE register in various units
|
|
||||||
func (d *BaseDevice) RawAngle(units AngleUnit) (uint16, float32, error) {
|
|
||||||
angle, err := d.ReadRegister(RAW_ANGLE)
|
|
||||||
if nil != err {
|
|
||||||
return 0, 0.0, err
|
|
||||||
}
|
|
||||||
// Convert to requested units
|
|
||||||
i, f := convertFromNativeAngle(angle, NATIVE_ANGLE_RANGE, units)
|
|
||||||
return i, f, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Angle reads the (scaled & adjusted) ANGLE register in various units
|
|
||||||
func (d *BaseDevice) Angle(units AngleUnit) (uint16, float32, error) {
|
|
||||||
// ZPOS enables setting the 'zero position' of the device to any RAW_ANGLE value
|
|
||||||
// ANGLE is RAW_ANGLE adjusted relative to ZPOS.
|
|
||||||
angle, err := d.ReadRegister(ANGLE)
|
|
||||||
if nil != err {
|
|
||||||
return 0, 0.0, err
|
|
||||||
}
|
|
||||||
// Convert to requested units
|
|
||||||
i, f := convertFromNativeAngle(angle, d.maxAngle, units)
|
|
||||||
return i, f, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// MagnetStatus reads the STATUS register and reports magnet position characteristics
|
|
||||||
func (d *BaseDevice) MagnetStatus() (detected bool, strength MagnetStrength, err error) {
|
|
||||||
status, err := d.ReadRegister(STATUS)
|
|
||||||
if nil != err {
|
|
||||||
return false, MagnetOk, err
|
|
||||||
}
|
|
||||||
detected = (status & STATUS_MD) != 0
|
|
||||||
strength = MagnetOk
|
|
||||||
if (status & STATUS_ML) != 0 {
|
|
||||||
strength = MagnetTooWeak
|
|
||||||
} else if (status & STATUS_MH) != 0 {
|
|
||||||
strength = MagnetTooStrong
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Burn is a convenience method to program the device permanently by writing to the BURN register (limited number of times use!)
|
|
||||||
func (d *BaseDevice) Burn(burnCmd BURN_CMD) error {
|
|
||||||
if BURN_ANGLE == burnCmd {
|
|
||||||
// BURN_ANGLE can only be executed up to 3 times.
|
|
||||||
// We can check this in advance by reading ZMCO before writing to the BURN register.
|
|
||||||
numBurns, err := d.ReadRegister(ZMCO)
|
|
||||||
if nil != err {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if numBurns >= BURN_ANGLE_COUNT_MAX {
|
|
||||||
// We're outta BURNs :(
|
|
||||||
return errMaxBurnAngle
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return d.WriteRegister(BURN, uint16(burnCmd))
|
|
||||||
}
|
|
||||||
@@ -1,95 +0,0 @@
|
|||||||
package as560x // import tinygo.org/x/drivers/ams560x
|
|
||||||
|
|
||||||
import "math"
|
|
||||||
|
|
||||||
// convertFromNativeAngle converts and scales an angle from the device's native 12-bit range to the requested units
|
|
||||||
func convertFromNativeAngle(angle uint16, maxAngle uint16, units AngleUnit) (uint16, float32) {
|
|
||||||
// MANG == 0 & MANG == NATIVE_ANGLE_RANGE (1 << 12) mean the same thing: use full circle range
|
|
||||||
// but the latter makes the maths/code simpler
|
|
||||||
if 0 == maxAngle {
|
|
||||||
maxAngle = NATIVE_ANGLE_RANGE
|
|
||||||
}
|
|
||||||
switch units {
|
|
||||||
case ANGLE_NATIVE:
|
|
||||||
// For native angles, scaling has already been done by the device
|
|
||||||
return angle, float32(angle)
|
|
||||||
case ANGLE_DEGREES_INT:
|
|
||||||
// Convert to degrees using integer arithmetic. Less accuracy but faster
|
|
||||||
var deg int = 0
|
|
||||||
if NATIVE_ANGLE_RANGE == maxAngle {
|
|
||||||
// Simplify the conversion when using the full range
|
|
||||||
deg = int(angle) * 360 >> 12
|
|
||||||
} else {
|
|
||||||
// Using an integer degrees scale with a narrower native range is pointless since we don't
|
|
||||||
// benefit at all from the increase in native resolution, in fact we LOSE precision.
|
|
||||||
// Alas, we have to return something
|
|
||||||
// First get maxAngle on the degrees scale
|
|
||||||
degMang, _ := convertFromNativeAngle(maxAngle, NATIVE_ANGLE_RANGE, units)
|
|
||||||
// Now scale angle
|
|
||||||
deg = int(angle) * int(degMang) / NATIVE_ANGLE_RANGE
|
|
||||||
}
|
|
||||||
return uint16(deg), float32(deg)
|
|
||||||
case ANGLE_DEGREES_FLOAT:
|
|
||||||
// Convert to degrees using floating point. More accuracy at expense of speed
|
|
||||||
var degF float32 = 0.0
|
|
||||||
if NATIVE_ANGLE_RANGE == maxAngle {
|
|
||||||
// Simplify the conversion when using the full range
|
|
||||||
degF = float32(angle) * 360.0 / NATIVE_ANGLE_RANGE
|
|
||||||
} else {
|
|
||||||
// Scale to degrees using a narrower native range
|
|
||||||
// First get maxAngle on the degrees scale
|
|
||||||
_, degMangF := convertFromNativeAngle(maxAngle, NATIVE_ANGLE_RANGE, units)
|
|
||||||
// Now scale angle
|
|
||||||
degF = float32(angle) * degMangF / NATIVE_ANGLE_RANGE
|
|
||||||
}
|
|
||||||
return uint16(degF), degF
|
|
||||||
case ANGLE_RADIANS:
|
|
||||||
// Convert to radians. Can only be done using floating point.
|
|
||||||
var rad float32 = 0.0
|
|
||||||
if NATIVE_ANGLE_RANGE == maxAngle {
|
|
||||||
// Simplify the conversion when using the full range
|
|
||||||
rad = float32(angle) * 2 * math.Pi / NATIVE_ANGLE_RANGE
|
|
||||||
} else {
|
|
||||||
// Scale to radians using a narrower native range
|
|
||||||
// First get maxAngle on the radians scale
|
|
||||||
_, radMang := convertFromNativeAngle(maxAngle, NATIVE_ANGLE_RANGE, units)
|
|
||||||
// Now scale angle
|
|
||||||
rad = float32(angle) * radMang / NATIVE_ANGLE_RANGE
|
|
||||||
}
|
|
||||||
return uint16(rad), rad
|
|
||||||
default:
|
|
||||||
panic("Unknown angle measurement unit")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// convertToNativeAngle converts an angle from the requested units to the device's native 12-bit range.
|
|
||||||
func convertToNativeAngle(angle float32, units AngleUnit) uint16 {
|
|
||||||
var pos uint16 = 0
|
|
||||||
switch units {
|
|
||||||
case ANGLE_NATIVE:
|
|
||||||
pos = uint16(angle)
|
|
||||||
case ANGLE_DEGREES_INT:
|
|
||||||
fallthrough
|
|
||||||
case ANGLE_DEGREES_FLOAT:
|
|
||||||
// Convert from degrees
|
|
||||||
angle = float32(math.Mod(float64(angle), 360.0))
|
|
||||||
if angle < 0.0 {
|
|
||||||
angle += 360.0
|
|
||||||
}
|
|
||||||
pos = uint16(math.Round(float64(angle) * NATIVE_ANGLE_RANGE / 360.0))
|
|
||||||
case ANGLE_RADIANS:
|
|
||||||
// Convert from radians
|
|
||||||
const circRad = 2.0 * math.Pi
|
|
||||||
angle = float32(math.Mod(float64(angle), circRad))
|
|
||||||
if angle < 0.0 {
|
|
||||||
angle += circRad
|
|
||||||
}
|
|
||||||
pos = uint16(math.Round(float64(angle) * NATIVE_ANGLE_RANGE / circRad))
|
|
||||||
default:
|
|
||||||
panic("Unknown angle measurement unit")
|
|
||||||
}
|
|
||||||
if pos > NATIVE_ANGLE_MAX {
|
|
||||||
pos = NATIVE_ANGLE_MAX
|
|
||||||
}
|
|
||||||
return pos
|
|
||||||
}
|
|
||||||
@@ -1,170 +0,0 @@
|
|||||||
package as560x // import tinygo.org/x/drivers/ams560x
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/binary"
|
|
||||||
"errors"
|
|
||||||
|
|
||||||
"tinygo.org/x/drivers"
|
|
||||||
"tinygo.org/x/drivers/internal/legacy"
|
|
||||||
)
|
|
||||||
|
|
||||||
// registerAttributes is a bitfield of attributes for a register
|
|
||||||
type registerAttributes uint8
|
|
||||||
|
|
||||||
const (
|
|
||||||
// reg_read indicates that the register is readable
|
|
||||||
reg_read registerAttributes = 1 << iota
|
|
||||||
// reg_write indicates that the register is writeable
|
|
||||||
reg_write
|
|
||||||
// reg_program indicates that the register can be permanently programmed ('BURNed')
|
|
||||||
reg_program
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
errRegisterNotReadable = errors.New("Register is not readable")
|
|
||||||
errRegisterNotWriteable = errors.New("Register is not writeable")
|
|
||||||
)
|
|
||||||
|
|
||||||
// i2cRegister encapsulates the address, structure and read/write logic for a register on a AS560x device
|
|
||||||
type i2cRegister struct {
|
|
||||||
// host is the 'host register' for virtual registers. Physical/root registers have this set to self
|
|
||||||
host *i2cRegister
|
|
||||||
// address is the i2c address of the register. For 2-byte (word) addresses it's the low byte which holds the MSBs
|
|
||||||
address uint8
|
|
||||||
// shift is the number of bits the value is 'left shifted' into the register byte/word (0-15)
|
|
||||||
shift uint16
|
|
||||||
// mask is a bitwise mask applied to the register AFTER 'right shifting' to mask the register value
|
|
||||||
mask uint16
|
|
||||||
// num_bytes is the width of the register in bytes, 1 or 2.
|
|
||||||
num_bytes uint8
|
|
||||||
// attributes holds the register attributes. A bitfield of REG_xyz constants
|
|
||||||
attributes registerAttributes
|
|
||||||
// cached indicates whether we are holding a cached value of the register in value
|
|
||||||
cached bool
|
|
||||||
// value can be used as a 'cache' of the register's value for writeable registers.
|
|
||||||
value uint16
|
|
||||||
}
|
|
||||||
|
|
||||||
// newI2CRegister returns a pointer to a new i2cRegister with no cached value
|
|
||||||
func newI2CRegister(address uint8, shift uint16, mask uint16, num_bytes uint8, attributes registerAttributes) *i2cRegister {
|
|
||||||
reg := &i2cRegister{
|
|
||||||
address: address,
|
|
||||||
shift: shift,
|
|
||||||
mask: mask,
|
|
||||||
num_bytes: num_bytes,
|
|
||||||
attributes: attributes,
|
|
||||||
}
|
|
||||||
// root registers host themselves
|
|
||||||
reg.host = reg
|
|
||||||
return reg
|
|
||||||
}
|
|
||||||
|
|
||||||
// newVirtualRegister returns a pointer to a new i2cRegister with the given host register and shift/mask.
|
|
||||||
func newVirtualRegister(host *i2cRegister, shift uint16, mask uint16) *i2cRegister {
|
|
||||||
return &i2cRegister{
|
|
||||||
host: host,
|
|
||||||
address: host.address,
|
|
||||||
shift: shift,
|
|
||||||
mask: mask,
|
|
||||||
num_bytes: host.num_bytes,
|
|
||||||
attributes: host.attributes,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// invalidate invalidates any cached value for the register and forces an I2C read on the next read()
|
|
||||||
func (r *i2cRegister) invalidate() {
|
|
||||||
r.host.cached = false
|
|
||||||
r.host.value = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// readShiftAndMask is an internal method to read a value for the register over the given I2C bus from the device with the given address applying the given shift and mask
|
|
||||||
func (r *i2cRegister) readShiftAndMask(bus drivers.I2C, deviceAddress uint8, shift uint16, mask uint16) (uint16, error) {
|
|
||||||
if r.host.attributes®_read == 0 {
|
|
||||||
return 0, errRegisterNotReadable
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only read over I2C if we don't have the host register value cached
|
|
||||||
var val uint16 = r.host.value
|
|
||||||
if !r.host.cached {
|
|
||||||
// To avoid an alloc we always use an array of 2 bytes
|
|
||||||
var buffer [2]byte
|
|
||||||
var buf []byte
|
|
||||||
if r.host.num_bytes < 2 {
|
|
||||||
buf = buffer[:1]
|
|
||||||
} else {
|
|
||||||
buf = buffer[:]
|
|
||||||
}
|
|
||||||
// Read the host register over I2C
|
|
||||||
err := legacy.ReadRegister(bus, deviceAddress, r.host.address, buf)
|
|
||||||
if nil != err {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
// Unpack data from I2C
|
|
||||||
if r.host.num_bytes > 1 {
|
|
||||||
val = binary.BigEndian.Uint16(buf)
|
|
||||||
} else {
|
|
||||||
val = uint16(buf[0])
|
|
||||||
}
|
|
||||||
// cache this value if the host register is writeable. Note we cache the entire buffer without applying shift/mask
|
|
||||||
if r.host.attributes®_write != 0 {
|
|
||||||
r.host.value = val
|
|
||||||
r.host.cached = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Shift and mask the value before returning
|
|
||||||
val >>= shift
|
|
||||||
val &= mask
|
|
||||||
return val, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// read reads a value for the register over the given I2C bus from the device with the given address.
|
|
||||||
func (r *i2cRegister) read(bus drivers.I2C, deviceAddress uint8) (uint16, error) {
|
|
||||||
return r.readShiftAndMask(bus, deviceAddress, r.shift, r.mask)
|
|
||||||
}
|
|
||||||
|
|
||||||
// write writes a value for the register over the given I2C bus to the device with the given address.
|
|
||||||
func (r *i2cRegister) write(bus drivers.I2C, deviceAddress uint8, value uint16) error {
|
|
||||||
if r.host.attributes®_write == 0 {
|
|
||||||
return errRegisterNotWriteable
|
|
||||||
}
|
|
||||||
var newValue uint16 = 0
|
|
||||||
// Data sheet tells us to do a read first, modify only the desired bits and then write back
|
|
||||||
// since (quote:) 'Blank fields may contain factory settings'
|
|
||||||
// We will also need to do this anyway to support virtualRegister mappings on some registers
|
|
||||||
// (e.g. CONF/STATUS)
|
|
||||||
if (r.host.attributes & reg_read) > 0 { // not all registers are readable, e.g. BURN
|
|
||||||
// read the host register's entire host byte/word, regardless of shift & mask
|
|
||||||
readValue, error := r.readShiftAndMask(bus, deviceAddress, 0, 0xffff)
|
|
||||||
if error != nil {
|
|
||||||
return error
|
|
||||||
}
|
|
||||||
// Zero-out ONLY the relevant bits in newValue we just read
|
|
||||||
readValue &= (0xffff ^ (r.mask << r.shift))
|
|
||||||
newValue = readValue
|
|
||||||
}
|
|
||||||
// Mask the new value and shift it into place
|
|
||||||
value &= r.mask
|
|
||||||
value <<= r.shift
|
|
||||||
// OR the masked & shifted value back into newValue to be written
|
|
||||||
newValue |= value
|
|
||||||
// Pack newValue into a byte buffer to write. To avoid an alloc we always use an array of 2 bytes
|
|
||||||
var buffer [2]byte
|
|
||||||
var buf []byte
|
|
||||||
if r.host.num_bytes < 2 {
|
|
||||||
buf = buffer[:1]
|
|
||||||
buf[0] = uint8(newValue & 0xff)
|
|
||||||
} else {
|
|
||||||
buf = buffer[:]
|
|
||||||
binary.BigEndian.PutUint16(buf, newValue)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write the register from the buffer over I2C
|
|
||||||
err := legacy.WriteRegister(bus, deviceAddress, r.host.address, buf)
|
|
||||||
// after successful I2C write, cache this value if the host register (if also readable)
|
|
||||||
// Note we cache the entire buffer without applying shift/mask
|
|
||||||
if nil == err && r.host.attributes®_read != 0 {
|
|
||||||
r.host.value = newValue
|
|
||||||
r.host.cached = true
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
@@ -1,208 +0,0 @@
|
|||||||
package as560x // import tinygo.org/x/drivers/ams560x
|
|
||||||
|
|
||||||
// DefaultAddress is the default I2C address of the AMS AS560x sensors (0x36).
|
|
||||||
const DefaultAddress uint8 = 0x36
|
|
||||||
|
|
||||||
// AS560x common device registers
|
|
||||||
const (
|
|
||||||
// ZMCO contains the number of times a BURN_ANGLE command has been executed (max 3 burns)
|
|
||||||
ZMCO = 0x00
|
|
||||||
// ZPOS is the zero (start) position in RAW_ANGLE terms.
|
|
||||||
ZPOS = 0x01
|
|
||||||
// CONF supports custom config. Raw 14-bit register. See datasheet for mapping or use 'virtual registers' below.
|
|
||||||
CONF = 0x07
|
|
||||||
// STATUS indicates magnet position. Encapsulates MD, ML & MH. See also 'virtual registers' below.
|
|
||||||
STATUS = 0x0b
|
|
||||||
// RAW_ANGLE is the raw unscaled & unadjusted angle (12 bit: 0-4095/0xfff)
|
|
||||||
RAW_ANGLE = 0x0c
|
|
||||||
// ANGLE is RAW_ANGLE scaled & adjusted according to ZPOS (and MPOS/MANG on AS5600). (12 bit: 0-4095/0xfff)
|
|
||||||
ANGLE = 0x0e
|
|
||||||
// AGC is the Automatic Gain Control based on temp, airgap etc. 0-255 @ 5V, 0-128 @ 3.3V.
|
|
||||||
AGC = 0x1a
|
|
||||||
// MAGNITUDE indicates the magnitude value of the internal CORDIC output. See datasheet for more info.
|
|
||||||
MAGNITUDE = 0x1b
|
|
||||||
// BURN performs permanent programming of some registers. See BURN_XYZ cmd constants below for commands.
|
|
||||||
BURN = 0xff
|
|
||||||
)
|
|
||||||
|
|
||||||
// AS5600 specific registers
|
|
||||||
const (
|
|
||||||
// MPOS is the maximum position in RAW_ANGLE terms. With ZPOS, defines a 'narrower angle' for higher resolution.
|
|
||||||
MPOS = 0x03
|
|
||||||
// MANG is the maximum angle. With ZPOS, defines a 'narrower angle' for higher resolution.
|
|
||||||
MANG = 0x05
|
|
||||||
)
|
|
||||||
|
|
||||||
// AS5601 specific registers
|
|
||||||
const (
|
|
||||||
// ABN. See datasheet for mapping
|
|
||||||
ABN = 0x09
|
|
||||||
// PUSHTHR. Configures push-button function. See datasheet and AGC
|
|
||||||
PUSHTHR = 0x0a
|
|
||||||
)
|
|
||||||
|
|
||||||
// 'Virtual Registers' (VRs) are bitfields within the registers above.
|
|
||||||
// These are not real register addresses recognized by the chip,
|
|
||||||
// but they are recognized by the driver for convenience.
|
|
||||||
|
|
||||||
// virtualRegisterStartAddress defines the start of the virtual register address range.
|
|
||||||
const virtualRegisterStartAddress = 0xa0
|
|
||||||
|
|
||||||
const (
|
|
||||||
// VRs for CONF
|
|
||||||
|
|
||||||
// WD is a Virtual Register for the Watchdog timer. See WATCHDOG_TIMER consts.
|
|
||||||
WD = iota + virtualRegisterStartAddress
|
|
||||||
// FTH is a Virtual Register for the Fast Filter Threshold. See FAST_FILTER_THRESHOLD consts.
|
|
||||||
FTH
|
|
||||||
// SF is a Virtual Register for the Slow Filter. See SLOW_FILTER_RESPONSE consts.
|
|
||||||
SF
|
|
||||||
// PWMF is a Virtual Register for PWM Frequency (AS5600 ONLY). See PWM_FREQUENCY consts.
|
|
||||||
PWMF
|
|
||||||
// OUTS is a Virtual Register for the Output Stage (AS5600 ONLY). See OUTPUT_STAGE consts.
|
|
||||||
OUTS
|
|
||||||
// HYST is a Virtual Register for Hysteresis. See HYSTERESIS consts.
|
|
||||||
HYST
|
|
||||||
// PM is a Virtual Register for the Power Mode. See POWER_MODE consts.
|
|
||||||
PM
|
|
||||||
|
|
||||||
// VRs for STATUS (0 = unset, 1 = set)
|
|
||||||
|
|
||||||
// MD is a Virtual Register for the 'Magnet was detected' flag.
|
|
||||||
MD
|
|
||||||
// ML is a Virtual Register for the 'AGC maximum gain overflow' a.k.a 'magnet too weak' flag.
|
|
||||||
ML
|
|
||||||
// MH is a Virtual Register for the 'AGC minimum gain overflow' a.k.a 'magnet too strong' flag.
|
|
||||||
MH
|
|
||||||
)
|
|
||||||
|
|
||||||
// POWER_MODE values for the PM component of CONF (and the PM VR)
|
|
||||||
const (
|
|
||||||
// PM_NOM is the normal 'always on' power mode. No polling, max 6.5mA current
|
|
||||||
PM_NOM = iota
|
|
||||||
// PM_LPM1 is Low Power Mode 1. 5ms polling, max 3.4mA current
|
|
||||||
PM_LPM1
|
|
||||||
// PM_LPM2 is Low Power Mode 2. 20ms polling, max 1.8mA current
|
|
||||||
PM_LPM2
|
|
||||||
// PM_LPM3 is Low Power Mode 3. 100ms polling, max 1.5mA current
|
|
||||||
PM_LPM3
|
|
||||||
)
|
|
||||||
|
|
||||||
// HYSTERESIS values for the HYST component of CONF (and the HYST VR)
|
|
||||||
const (
|
|
||||||
// HYST_OFF disables any hysteresis of the output
|
|
||||||
HYST_OFF = iota
|
|
||||||
// HYST_1LSB enables output hysteresis using 1 LSB
|
|
||||||
HYST_1LSB
|
|
||||||
// HYST_2LSB enables output hysteresis using 2 LSBs
|
|
||||||
HYST_2LSB
|
|
||||||
// HYST_3LSB enables output hysteresis using 3 LSBs
|
|
||||||
HYST_3LSB
|
|
||||||
)
|
|
||||||
|
|
||||||
// OUTPUT_STAGE values for the OUTS component of CONF (and the OUTS VR - AS5600 ONLY)
|
|
||||||
const (
|
|
||||||
// OS_ANALOG_FULL_RANGE enables analog output with full range (0%-100% VDD)
|
|
||||||
OS_ANALOG_FULL_RANGE = iota
|
|
||||||
// OS_ANALOG_REDUCED_RANGE enables analog output with reduced range (10%-90% VDD)
|
|
||||||
OS_ANALOG_REDUCED_RANGE
|
|
||||||
// OS_DIGITAL_PWM enables digital PWM output. Frequency determined by PWMF
|
|
||||||
OS_DIGITAL_PWM
|
|
||||||
)
|
|
||||||
|
|
||||||
// PWM_FREQUENCY values for the PWMF component of CONF (and the PWMF VR - ASS5600 ONLY)
|
|
||||||
const (
|
|
||||||
// PWMF_115_HZ enables PWM at 115 Hz
|
|
||||||
PWMF_115_HZ = iota
|
|
||||||
// PWMF_230_HZ enables PWM at 230 Hz
|
|
||||||
PWMF_230_HZ
|
|
||||||
// PWMF_460_HZ enables PWM at 460 Hz
|
|
||||||
PWMF_460_HZ
|
|
||||||
// PWMF_920_HZ enables PWM at 920 Hz
|
|
||||||
PWMF_920_HZ
|
|
||||||
)
|
|
||||||
|
|
||||||
// SLOW_FILTER_RESPONSE values for the SF (slow filter) component of CONF (and the SF VR)
|
|
||||||
const (
|
|
||||||
// SF_16X enables a 16x Slow Filter step response
|
|
||||||
SF_16X = iota
|
|
||||||
// SF_8X enables a 8x Slow Filter step response
|
|
||||||
SF_8X
|
|
||||||
// SF_4X enables a 4x Slow Filter step response
|
|
||||||
SF_4X
|
|
||||||
// SF_2X enables a 2x Slow Filter step response
|
|
||||||
SF_2X
|
|
||||||
)
|
|
||||||
|
|
||||||
// FAST_FILTER_THRESHOLD values for the FTH (fast filter threshold) component of CONF (and the FTH VR)
|
|
||||||
const (
|
|
||||||
// FTH_NONE disables the fast filter (slow filter only)
|
|
||||||
FTH_NONE = iota
|
|
||||||
// FTH_6LSB enables a fast filter threshold with 6 LSBs
|
|
||||||
FTH_6LSB
|
|
||||||
// FTH_7LSB enables a fast filter threshold with 7 LSBs
|
|
||||||
FTH_7LSB
|
|
||||||
// FTH_9LSB enables a fast filter threshold with 9 LSBs
|
|
||||||
FTH_9LSB
|
|
||||||
// FTH_18LSB enables a fast filter threshold with 18 LSBs
|
|
||||||
FTH_18LSB
|
|
||||||
// FTH_21LSB enables a fast filter threshold with 21 LSBs
|
|
||||||
FTH_21LSB
|
|
||||||
// FTH_24LSB enables a fast filter threshold with 24 LSBs
|
|
||||||
FTH_24SB
|
|
||||||
// FTH_10LSB enables a fast filter threshold with 10 LSBs
|
|
||||||
FTH_10LSB
|
|
||||||
)
|
|
||||||
|
|
||||||
// WATCHDOG_TIMER values for the WD component of CONF (and the WD VR)
|
|
||||||
const (
|
|
||||||
// WD_OFF disables the Watchdog Timer
|
|
||||||
WD_OFF = iota
|
|
||||||
// WD_ON enables the Watchdog Timer (automatic entry into LPM3 low-power mode enabled)
|
|
||||||
WD_ON
|
|
||||||
)
|
|
||||||
|
|
||||||
// constants for the raw STATUS register bitfield value.
|
|
||||||
const (
|
|
||||||
// STATUS_MH is set in STATUS when the magnet field is too strong (AGC minimum gain overflow)
|
|
||||||
STATUS_MH = 1 << (iota + 3)
|
|
||||||
// STATUS_ML is set in STATUS when the magnet field is too weak (AGC maximum gain overflow)
|
|
||||||
STATUS_ML
|
|
||||||
// STATUS_MD is set n STATUS when the magnet is detected. Doesn't seem to work with some units.
|
|
||||||
STATUS_MD
|
|
||||||
)
|
|
||||||
|
|
||||||
// ABN_MAPPING values for the ABN register (AS5601 ONLY)
|
|
||||||
const (
|
|
||||||
// ABN_8 configures 8 output positions (61 Hz)
|
|
||||||
ABN_8 = iota
|
|
||||||
// ABN_16 configures 16 output positions (122 Hz)
|
|
||||||
ABN_16
|
|
||||||
// ABN_32 configures 32 output positions (244 Hz)
|
|
||||||
ABN_32
|
|
||||||
// ABN_64 configures 64 output positions (488 Hz)
|
|
||||||
ABN_64
|
|
||||||
// ABN_128 configures 128 output positions (976 Hz)
|
|
||||||
ABN_128
|
|
||||||
// ABN_256 configures 256 output positions (1.95 KHz)
|
|
||||||
ABN_256
|
|
||||||
// ABN_512 configures 512 output positions (3.9 KHz)
|
|
||||||
ABN_512
|
|
||||||
// ABN_1024 configures 1024 output positions (7.8 KHz)
|
|
||||||
ABN_1024
|
|
||||||
// ABN_2048 configures 2048 output positions (15.6 KHz)
|
|
||||||
ABN_2048
|
|
||||||
)
|
|
||||||
|
|
||||||
// BURN_CMD is a command to write to the BURN register.
|
|
||||||
type BURN_CMD uint16
|
|
||||||
|
|
||||||
const (
|
|
||||||
// BURN_ANGLE is the value to write to BURN to permanently program ZPOS & MPOS (Max 3 times!)
|
|
||||||
BURN_ANGLE BURN_CMD = 0x80
|
|
||||||
// BURN_SETTING is the value to write to BURN to permanently program MANG & CONF (ONCE ONLY!)
|
|
||||||
BURN_SETTING BURN_CMD = 0x40
|
|
||||||
)
|
|
||||||
|
|
||||||
// BURN_ANGLE_COUNT_MAX is a constant for the maximum number of times a BURN_ANGLE command can be executed. Compare this with ZMCO
|
|
||||||
const BURN_ANGLE_COUNT_MAX uint16 = 3
|
|
||||||
+1
-1
@@ -11,7 +11,7 @@ import (
|
|||||||
"tinygo.org/x/drivers"
|
"tinygo.org/x/drivers"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Device wraps an I2C connection to an AT24CX device.
|
// Device wraps an I2C connection to a DS3231 device.
|
||||||
type Device struct {
|
type Device struct {
|
||||||
bus drivers.I2C
|
bus drivers.I2C
|
||||||
Address uint16
|
Address uint16
|
||||||
|
|||||||
+2
-3
@@ -7,7 +7,6 @@ package axp192 // import "tinygo.org/x/drivers/axp192"
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"tinygo.org/x/drivers"
|
"tinygo.org/x/drivers"
|
||||||
"tinygo.org/x/drivers/internal/legacy"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Error uint8
|
type Error uint8
|
||||||
@@ -249,10 +248,10 @@ func (d *Device) SetLDOEnable(number uint8, state bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *Device) write1Byte(reg, data uint8) {
|
func (d *Device) write1Byte(reg, data uint8) {
|
||||||
legacy.WriteRegister(d.bus, d.Address, reg, []byte{data})
|
d.bus.WriteRegister(d.Address, reg, []byte{data})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Device) read8bit(reg uint8) uint8 {
|
func (d *Device) read8bit(reg uint8) uint8 {
|
||||||
legacy.ReadRegister(d.bus, d.Address, reg, d.buf[:1])
|
d.bus.ReadRegister(d.Address, reg, d.buf[:1])
|
||||||
return d.buf[0]
|
return d.buf[0]
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
@@ -1,352 +0,0 @@
|
|||||||
// Package bma42x provides a driver for the BMA421 and BMA425 accelerometer
|
|
||||||
// chips.
|
|
||||||
//
|
|
||||||
// Here is a reasonably good datasheet:
|
|
||||||
// https://datasheet.lcsc.com/lcsc/1912111437_Bosch-Sensortec-BMA425_C437656.pdf
|
|
||||||
//
|
|
||||||
// This driver was originally written for the PineTime, using the datasheet as a
|
|
||||||
// guide. There is an open source C driver provided by Bosch, but unfortunately
|
|
||||||
// it needs some small modifications to work with other chips (most importantly,
|
|
||||||
// the "config file").
|
|
||||||
// The InfiniTime and Wasp-OS drivers for this accelerometer have also been used
|
|
||||||
// to figure out some driver details (especially step counting).
|
|
||||||
package bma42x
|
|
||||||
|
|
||||||
import (
|
|
||||||
_ "embed"
|
|
||||||
"errors"
|
|
||||||
"reflect"
|
|
||||||
"time"
|
|
||||||
"unsafe"
|
|
||||||
|
|
||||||
"tinygo.org/x/drivers"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Driver for BMA421 and BMA425:
|
|
||||||
// BMA421: https://files.pine64.org/doc/datasheet/pinetime/BST-BMA421-FL000.pdf
|
|
||||||
// BMA425: https://datasheet.lcsc.com/lcsc/1912111437_Bosch-Sensortec-BMA425_C437656.pdf
|
|
||||||
|
|
||||||
// This is the BMA421 firmware from the Wasp-OS project.
|
|
||||||
// It is identical to the so-called BMA423 firmware in InfiniTime, which I
|
|
||||||
// suspect to be actually a BMA421 firmware. I don't know where this firmware
|
|
||||||
// comes from or what the licensing status is.
|
|
||||||
// It has the FEATURES_IN command prepended, so that it can be written directly
|
|
||||||
// using I2C.Tx.
|
|
||||||
// Source: https://github.com/wasp-os/bma42x-upy/blob/master/BMA42X-Sensor-API/bma421.h
|
|
||||||
//
|
|
||||||
//go:embed bma421-config-waspos.bin
|
|
||||||
var bma421Firmware string
|
|
||||||
|
|
||||||
// Same as the BMA421 firmware, but for the BMA425.
|
|
||||||
// Source: https://github.com/wasp-os/bma42x-upy/blob/master/BMA42X-Sensor-API/bma425.h
|
|
||||||
//
|
|
||||||
//go:embed bma425-config-waspos.bin
|
|
||||||
var bma425Firmware string
|
|
||||||
|
|
||||||
var (
|
|
||||||
errUnknownDevice = errors.New("bma42x: unknown device")
|
|
||||||
errUnsupportedDevice = errors.New("bma42x: device not part of config")
|
|
||||||
errConfigMismatch = errors.New("bma42x: config mismatch")
|
|
||||||
errTimeout = errors.New("bma42x: timeout")
|
|
||||||
errInitFailed = errors.New("bma42x: failed to initialize")
|
|
||||||
)
|
|
||||||
|
|
||||||
const Address = 0x18 // BMA421/BMA425 address
|
|
||||||
|
|
||||||
type DeviceType uint8
|
|
||||||
|
|
||||||
const (
|
|
||||||
DeviceBMA421 DeviceType = 1 << iota
|
|
||||||
DeviceBMA425
|
|
||||||
|
|
||||||
AnyDevice = DeviceBMA421 | DeviceBMA425
|
|
||||||
noDevice DeviceType = 0
|
|
||||||
)
|
|
||||||
|
|
||||||
// Features to enable while configuring the accelerometer.
|
|
||||||
type Features uint8
|
|
||||||
|
|
||||||
const (
|
|
||||||
FeatureStepCounting = 1 << iota
|
|
||||||
)
|
|
||||||
|
|
||||||
type Config struct {
|
|
||||||
// Which devices to support (OR the device types together as needed).
|
|
||||||
Device DeviceType
|
|
||||||
|
|
||||||
// Which features to enable. With Features == 0, only the accelerometer will
|
|
||||||
// be enabled.
|
|
||||||
Features Features
|
|
||||||
}
|
|
||||||
|
|
||||||
type Device struct {
|
|
||||||
bus drivers.I2C
|
|
||||||
address uint8
|
|
||||||
accelData [6]byte
|
|
||||||
combinedTempSteps [5]uint8 // [0:3] steps, [4] temperature
|
|
||||||
dataBuf [2]byte
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewI2C(i2c drivers.I2C, address uint8) *Device {
|
|
||||||
return &Device{
|
|
||||||
bus: i2c,
|
|
||||||
address: address,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *Device) Connected() bool {
|
|
||||||
val, err := d.read1(_CHIP_ID)
|
|
||||||
return err == nil && identifyChip(val) != noDevice
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *Device) Configure(config Config) error {
|
|
||||||
if config.Device == 0 {
|
|
||||||
config.Device = AnyDevice
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check chip ID, to check the connection and to determine which BMA42x
|
|
||||||
// device we're dealing with.
|
|
||||||
chipID, err := d.read1(_CHIP_ID)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine which firmware (config file?) we'll be using.
|
|
||||||
// There is an extra check for the device before using the given firmware.
|
|
||||||
// This check will typically be optimized away if the given device is not
|
|
||||||
// configured, so that the firmware (which is 6kB in size!) won't be linked
|
|
||||||
// into the binary.
|
|
||||||
var firmware string
|
|
||||||
switch identifyChip(chipID) {
|
|
||||||
case DeviceBMA421:
|
|
||||||
if config.Device&DeviceBMA421 == 0 {
|
|
||||||
return errUnsupportedDevice
|
|
||||||
}
|
|
||||||
firmware = bma421Firmware
|
|
||||||
case DeviceBMA425:
|
|
||||||
if config.Device&DeviceBMA425 == 0 {
|
|
||||||
return errUnsupportedDevice
|
|
||||||
}
|
|
||||||
firmware = bma425Firmware
|
|
||||||
default:
|
|
||||||
return errUnknownDevice
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reset the chip, to be able to initialize it properly.
|
|
||||||
// The datasheet says a delay is needed after a SoftReset, but it doesn't
|
|
||||||
// say how long this delay should be. The bma423 driver however uses a 200ms
|
|
||||||
// delay, so that's what we'll be using.
|
|
||||||
err = d.write1(_CMD, cmdSoftReset)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
time.Sleep(200 * time.Millisecond)
|
|
||||||
|
|
||||||
// Disable power saving.
|
|
||||||
err = d.write1(_PWR_CONF, 0x00)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
time.Sleep(450 * time.Microsecond)
|
|
||||||
|
|
||||||
// Start initialization (because the datasheet says so).
|
|
||||||
err = d.write1(_INIT_CTRL, 0x00)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write "config file" (actually a firmware, I think) to the chip.
|
|
||||||
// To do this, unsafely cast the string to a byte slice to avoid putting it
|
|
||||||
// in RAM. This is safe in this case because Tx won't write to the 'w'
|
|
||||||
// slice.
|
|
||||||
err = d.bus.Tx(uint16(d.address), unsafeStringToSlice(firmware), nil)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read the config data back.
|
|
||||||
// We don't do that, as it slows down configuration and it probably isn't
|
|
||||||
// _really_ necessary with a reasonably stable I2C bus.
|
|
||||||
if false {
|
|
||||||
data := make([]byte, len(firmware)-1)
|
|
||||||
err = d.readn(_FEATURES_IN, data)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
for i, c := range data {
|
|
||||||
if firmware[i+1] != c {
|
|
||||||
return errConfigMismatch
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enable sensors.
|
|
||||||
err = d.write1(_INIT_CTRL, 0x01)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait until the device is initialized.
|
|
||||||
start := time.Now()
|
|
||||||
status := uint8(0) // busy
|
|
||||||
for status == 0 {
|
|
||||||
status, err = d.read1(_INTERNAL_STATUS)
|
|
||||||
if err != nil {
|
|
||||||
return err // I2C bus error.
|
|
||||||
}
|
|
||||||
if status > 1 {
|
|
||||||
// Expected either 0 ("not_init") or 1 ("init_ok").
|
|
||||||
return errInitFailed
|
|
||||||
}
|
|
||||||
if time.Since(start) >= 150*time.Millisecond {
|
|
||||||
// The datasheet says initialization should not take longer than
|
|
||||||
return errTimeout
|
|
||||||
}
|
|
||||||
// Don't bother the chip all the time while it's initializing.
|
|
||||||
time.Sleep(50 * time.Microsecond)
|
|
||||||
}
|
|
||||||
|
|
||||||
if config.Features&FeatureStepCounting != 0 {
|
|
||||||
// Enable step counter.
|
|
||||||
// TODO: support step counter parameters.
|
|
||||||
var buf [71]byte
|
|
||||||
buf[0] = _FEATURES_IN // prefix buf with the command
|
|
||||||
data := buf[1:]
|
|
||||||
err = d.readn(_FEATURES_IN, data)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
data[0x3A+1] |= 0x10 // enable step counting by setting a magical bit
|
|
||||||
err = d.bus.Tx(uint16(d.address), buf[:], nil)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enable the accelerometer.
|
|
||||||
err = d.write1(_PWR_CTRL, 0x04)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Configure accelerometer for low power usage:
|
|
||||||
// acc_perf_mode=0 (power saving enabled)
|
|
||||||
// acc_bwp=osr4_avg1 (no averaging)
|
|
||||||
// acc_odr=50Hz (50Hz sampling interval, enough for the step counter)
|
|
||||||
const accelConf = 0x00<<7 | 0x00<<4 | 0x07<<0
|
|
||||||
err = d.write1(_ACC_CONF, accelConf)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reduce current consumption.
|
|
||||||
// With power saving enabled (and the above ACC_CONF) the chip consumes only
|
|
||||||
// 14µA.
|
|
||||||
err = d.write1(_PWR_CONF, 0x03)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *Device) Update(which drivers.Measurement) error {
|
|
||||||
// TODO: combine temperature and step counter into a single read.
|
|
||||||
if which&drivers.Temperature != 0 {
|
|
||||||
val, err := d.read1(_TEMPERATURE)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
d.combinedTempSteps[4] = val
|
|
||||||
}
|
|
||||||
if which&drivers.Acceleration != 0 {
|
|
||||||
// The acceleration data is stored in DATA8 through DATA13 as 3 12-bit
|
|
||||||
// values.
|
|
||||||
err := d.readn(_DATA_8, d.accelData[:]) // ACC_X(LSB)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = d.readn(_STEP_COUNTER_0, d.combinedTempSteps[:4])
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Temperature returns the last read temperature in celsius milli degrees (1°C
|
|
||||||
// is 1000).
|
|
||||||
func (d *Device) Temperature() int32 {
|
|
||||||
// The temperature value is a two's complement number (meaning: signed) in
|
|
||||||
// units of 1 kelvin, with 0 being 23°C.
|
|
||||||
return (int32(int8(d.combinedTempSteps[4])) + 23) * 1000
|
|
||||||
}
|
|
||||||
|
|
||||||
// Acceleration returns the last read acceleration 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) Acceleration() (x, y, z int32) {
|
|
||||||
// Combine raw data from d.accelData (stored as 12-bit signed values) into a
|
|
||||||
// number (0..4095):
|
|
||||||
x = int32(d.accelData[0])>>4 | int32(d.accelData[1])<<4
|
|
||||||
y = int32(d.accelData[2])>>4 | int32(d.accelData[3])<<4
|
|
||||||
z = int32(d.accelData[4])>>4 | int32(d.accelData[5])<<4
|
|
||||||
// Sign extend this number to -2048..2047:
|
|
||||||
x = (x << 20) >> 20
|
|
||||||
y = (y << 20) >> 20
|
|
||||||
z = (z << 20) >> 20
|
|
||||||
// Scale from -512..511 to -1000_000..998_046.
|
|
||||||
// Or, at the maximum range (4g), from -2048..2047 to -2000_000..3998_046.
|
|
||||||
// The formula derived as follows (where 512 is the expected value at 1g):
|
|
||||||
// x = x * 1000_000 / 512
|
|
||||||
// x = x * (1000_000/64) / (512/64)
|
|
||||||
// x = x * 15625 / 8
|
|
||||||
x = x * 15625 / 8
|
|
||||||
y = y * 15625 / 8
|
|
||||||
z = z * 15625 / 8
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Steps returns the number of steps counted since the BMA42x sensor was
|
|
||||||
// initialized.
|
|
||||||
func (d *Device) Steps() (steps uint32) {
|
|
||||||
steps |= uint32(d.combinedTempSteps[0]) << 0
|
|
||||||
steps |= uint32(d.combinedTempSteps[1]) << 8
|
|
||||||
steps |= uint32(d.combinedTempSteps[2]) << 16
|
|
||||||
steps |= uint32(d.combinedTempSteps[3]) << 24
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *Device) read1(register uint8) (uint8, error) {
|
|
||||||
d.dataBuf[0] = register
|
|
||||||
err := d.bus.Tx(uint16(d.address), d.dataBuf[:1], d.dataBuf[1:2])
|
|
||||||
return d.dataBuf[1], err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *Device) readn(register uint8, data []byte) error {
|
|
||||||
d.dataBuf[0] = register
|
|
||||||
return d.bus.Tx(uint16(d.address), d.dataBuf[:1], data)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *Device) write1(register uint8, data uint8) error {
|
|
||||||
d.dataBuf[0] = register
|
|
||||||
d.dataBuf[1] = data
|
|
||||||
return d.bus.Tx(uint16(d.address), d.dataBuf[:2], nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func unsafeStringToSlice(s string) []byte {
|
|
||||||
// TODO: use unsafe.Slice(unsafe.StringData(...)) once we require Go 1.20.
|
|
||||||
sh := (*reflect.StringHeader)(unsafe.Pointer(&s))
|
|
||||||
return unsafe.Slice((*byte)(unsafe.Pointer(sh.Data)), len(s))
|
|
||||||
}
|
|
||||||
|
|
||||||
func identifyChip(chipID uint8) DeviceType {
|
|
||||||
switch chipID {
|
|
||||||
case 0x11:
|
|
||||||
return DeviceBMA421
|
|
||||||
case 0x13:
|
|
||||||
return DeviceBMA425
|
|
||||||
default:
|
|
||||||
return noDevice
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,73 +0,0 @@
|
|||||||
package bma42x
|
|
||||||
|
|
||||||
const (
|
|
||||||
// I2C registers
|
|
||||||
_CHIP_ID = 0x00
|
|
||||||
_ERR_REG = 0x02
|
|
||||||
_STATUS = 0x03
|
|
||||||
_DATA_0 = 0x0A
|
|
||||||
_DATA_1 = 0x0B
|
|
||||||
_DATA_2 = 0x0C
|
|
||||||
_DATA_3 = 0x0D
|
|
||||||
_DATA_4 = 0x0E
|
|
||||||
_DATA_5 = 0x0F
|
|
||||||
_DATA_6 = 0x10
|
|
||||||
_DATA_7 = 0x11
|
|
||||||
_DATA_8 = 0x12
|
|
||||||
_DATA_9 = 0x13
|
|
||||||
_DATA_10 = 0x14
|
|
||||||
_DATA_11 = 0x15
|
|
||||||
_DATA_12 = 0x16
|
|
||||||
_DATA_13 = 0x17
|
|
||||||
_SENSORTIME_0 = 0x18
|
|
||||||
_SENSORTIME_1 = 0x19
|
|
||||||
_SENSORTIME_2 = 0x1A
|
|
||||||
_EVENT = 0x1B
|
|
||||||
_INT_STATUS_0 = 0x1C
|
|
||||||
_INT_STATUS_1 = 0x1D
|
|
||||||
_STEP_COUNTER_0 = 0x1E
|
|
||||||
_STEP_COUNTER_1 = 0x1F
|
|
||||||
_STEP_COUNTER_2 = 0x20
|
|
||||||
_STEP_COUNTER_3 = 0x21
|
|
||||||
_TEMPERATURE = 0x22
|
|
||||||
_FIFO_LENGTH_0 = 0x24
|
|
||||||
_FIFO_LENGTH_1 = 0x25
|
|
||||||
_FIFO_DATA = 0x26
|
|
||||||
_ACTIVITY_TYPE = 0x27
|
|
||||||
_INTERNAL_STATUS = 0x2A
|
|
||||||
_ACC_CONF = 0x40
|
|
||||||
_ACC_RANGE = 0x41
|
|
||||||
_AUX_CONF = 0x44
|
|
||||||
_FIFO_DOWNS = 0x45
|
|
||||||
_FIFO_WTM_0 = 0x46
|
|
||||||
_FIFO_WTM_1 = 0x47
|
|
||||||
_FIFO_CONFIG_0 = 0x48
|
|
||||||
_FIFO_CONFIG_1 = 0x49
|
|
||||||
_AUX_DEV_ID = 0x4B
|
|
||||||
_AUX_IF_CONF = 0x4C
|
|
||||||
_AUX_RD_ADDR = 0x4D
|
|
||||||
_AUX_WR_ADDR = 0x4E
|
|
||||||
_AUX_WR_DATA = 0x4F
|
|
||||||
_INT1_IO_CTRL = 0x53
|
|
||||||
_INT2_IO_CTRL = 0x54
|
|
||||||
_INT_LATCH = 0x55
|
|
||||||
_INT1_MAP = 0x56
|
|
||||||
_INT2_MAP = 0x57
|
|
||||||
_INT_MAP_DATA = 0x58
|
|
||||||
_INIT_CTRL = 0x59
|
|
||||||
_FEATURES_IN = 0x5E
|
|
||||||
_INTERNAL_ERROR = 0x5F
|
|
||||||
_NVM_CONF = 0x6A
|
|
||||||
_IF_CONF = 0x6B
|
|
||||||
_ACC_SELF_TEST = 0x6D
|
|
||||||
_NV_CONF = 0x70
|
|
||||||
_OFFSET_0 = 0x71
|
|
||||||
_OFFSET_1 = 0x72
|
|
||||||
_OFFSET_2 = 0x73
|
|
||||||
_PWR_CONF = 0x7C
|
|
||||||
_PWR_CTRL = 0x7D
|
|
||||||
_CMD = 0x7E
|
|
||||||
|
|
||||||
// Commands send to regCommand.
|
|
||||||
cmdSoftReset = 0xB6
|
|
||||||
)
|
|
||||||
+11
-122
@@ -7,10 +7,8 @@ package bme280
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"math"
|
"math"
|
||||||
"time"
|
|
||||||
|
|
||||||
"tinygo.org/x/drivers"
|
"tinygo.org/x/drivers"
|
||||||
"tinygo.org/x/drivers/internal/legacy"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// calibrationCoefficients reads at startup and stores the calibration coefficients
|
// calibrationCoefficients reads at startup and stores the calibration coefficients
|
||||||
@@ -35,27 +33,11 @@ type calibrationCoefficients struct {
|
|||||||
h6 int8
|
h6 int8
|
||||||
}
|
}
|
||||||
|
|
||||||
type Oversampling byte
|
|
||||||
type Mode byte
|
|
||||||
type FilterCoefficient byte
|
|
||||||
type Period byte
|
|
||||||
|
|
||||||
// Config contains settings for filtering, sampling, and modes of operation
|
|
||||||
type Config struct {
|
|
||||||
Pressure Oversampling
|
|
||||||
Temperature Oversampling
|
|
||||||
Humidity Oversampling
|
|
||||||
Period Period
|
|
||||||
Mode Mode
|
|
||||||
IIR FilterCoefficient
|
|
||||||
}
|
|
||||||
|
|
||||||
// Device wraps an I2C connection to a BME280 device.
|
// Device wraps an I2C connection to a BME280 device.
|
||||||
type Device struct {
|
type Device struct {
|
||||||
bus drivers.I2C
|
bus drivers.I2C
|
||||||
Address uint16
|
Address uint16
|
||||||
calibrationCoefficients calibrationCoefficients
|
calibrationCoefficients calibrationCoefficients
|
||||||
Config Config
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates a new BME280 connection. The I2C bus must already be
|
// New creates a new BME280 connection. The I2C bus must already be
|
||||||
@@ -69,49 +51,24 @@ func New(bus drivers.I2C) Device {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ConfigureWithSettings sets up the device for communication and
|
// Configure sets up the device for communication and
|
||||||
// read the calibration coefficients.
|
// read the calibration coefficientes.
|
||||||
//
|
|
||||||
// The default configuration is the Indoor Navigation settings
|
|
||||||
// from the BME280 datasheet.
|
|
||||||
func (d *Device) Configure() {
|
func (d *Device) Configure() {
|
||||||
d.ConfigureWithSettings(Config{})
|
|
||||||
}
|
|
||||||
|
|
||||||
// ConfigureWithSettings sets up the device for communication and
|
|
||||||
// read the calibration coefficients.
|
|
||||||
//
|
|
||||||
// The default configuration if config is left at defaults is
|
|
||||||
// the Indoor Navigation settings from the BME280 datasheet.
|
|
||||||
func (d *Device) ConfigureWithSettings(config Config) {
|
|
||||||
d.Config = config
|
|
||||||
|
|
||||||
// If config is not initialized, use Indoor Navigation defaults.
|
|
||||||
if d.Config == (Config{}) {
|
|
||||||
d.Config = Config{
|
|
||||||
Mode: ModeNormal,
|
|
||||||
Period: Period0_5ms,
|
|
||||||
Temperature: Sampling2X,
|
|
||||||
Humidity: Sampling1X,
|
|
||||||
Pressure: Sampling16X,
|
|
||||||
IIR: Coeff16,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var data [24]byte
|
var data [24]byte
|
||||||
err := legacy.ReadRegister(d.bus, uint8(d.Address), REG_CALIBRATION, data[:])
|
err := d.bus.ReadRegister(uint8(d.Address), REG_CALIBRATION, data[:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var h1 [1]byte
|
var h1 [1]byte
|
||||||
err = legacy.ReadRegister(d.bus, uint8(d.Address), REG_CALIBRATION_H1, h1[:])
|
err = d.bus.ReadRegister(uint8(d.Address), REG_CALIBRATION_H1, h1[:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var h2lsb [7]byte
|
var h2lsb [7]byte
|
||||||
err = legacy.ReadRegister(d.bus, uint8(d.Address), REG_CALIBRATION_H2LSB, h2lsb[:])
|
err = d.bus.ReadRegister(uint8(d.Address), REG_CALIBRATION_H2LSB, h2lsb[:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -136,45 +93,23 @@ func (d *Device) ConfigureWithSettings(config Config) {
|
|||||||
d.calibrationCoefficients.h4 = 0 + (int16(h2lsb[3]) << 4) | (int16(h2lsb[4] & 0x0F))
|
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.calibrationCoefficients.h5 = 0 + (int16(h2lsb[5]) << 4) | (int16(h2lsb[4]) >> 4)
|
||||||
|
|
||||||
d.Reset()
|
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})
|
||||||
|
|
||||||
legacy.WriteRegister(d.bus, uint8(d.Address), CTRL_CONFIG, []byte{byte(d.Config.Period<<5) | byte(d.Config.IIR<<2)})
|
|
||||||
legacy.WriteRegister(d.bus, uint8(d.Address), CTRL_HUMIDITY_ADDR, []byte{byte(d.Config.Humidity)})
|
|
||||||
|
|
||||||
// Normal mode, start measuring now
|
|
||||||
if d.Config.Mode == ModeNormal {
|
|
||||||
legacy.WriteRegister(d.bus, uint8(d.Address), CTRL_MEAS_ADDR, []byte{
|
|
||||||
byte(d.Config.Temperature<<5) |
|
|
||||||
byte(d.Config.Pressure<<2) |
|
|
||||||
byte(d.Config.Mode)})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Connected returns whether a BME280 has been found.
|
// Connected returns whether a BME280 has been found.
|
||||||
// It does a "who am I" request and checks the response.
|
// It does a "who am I" request and checks the response.
|
||||||
func (d *Device) Connected() bool {
|
func (d *Device) Connected() bool {
|
||||||
data := []byte{0}
|
data := []byte{0}
|
||||||
legacy.ReadRegister(d.bus, uint8(d.Address), WHO_AM_I, data)
|
d.bus.ReadRegister(uint8(d.Address), WHO_AM_I, data)
|
||||||
return data[0] == CHIP_ID
|
return data[0] == CHIP_ID
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset the device
|
// Reset the device
|
||||||
func (d *Device) Reset() {
|
func (d *Device) Reset() {
|
||||||
legacy.WriteRegister(d.bus, uint8(d.Address), CMD_RESET, []byte{0xB6})
|
d.bus.WriteRegister(uint8(d.Address), CMD_RESET, []byte{0xB6})
|
||||||
}
|
|
||||||
|
|
||||||
// SetMode can set the device to Sleep, Normal or Forced mode
|
|
||||||
//
|
|
||||||
// Calling this method is optional, Configure can be used to set the
|
|
||||||
// initial mode if no mode change is desired. This method is most
|
|
||||||
// useful to switch between Sleep and Normal modes.
|
|
||||||
func (d *Device) SetMode(mode Mode) {
|
|
||||||
d.Config.Mode = mode
|
|
||||||
|
|
||||||
legacy.WriteRegister(d.bus, uint8(d.Address), CTRL_MEAS_ADDR, []byte{
|
|
||||||
byte(d.Config.Temperature<<5) |
|
|
||||||
byte(d.Config.Pressure<<2) |
|
|
||||||
byte(d.Config.Mode)})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadTemperature returns the temperature in celsius milli degrees (°C/1000)
|
// ReadTemperature returns the temperature in celsius milli degrees (°C/1000)
|
||||||
@@ -251,17 +186,7 @@ func readIntLE(msb byte, lsb byte) int16 {
|
|||||||
// readData does a burst read from 0xF7 to 0xF0 according to the datasheet
|
// 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
|
// 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) {
|
func (d *Device) readData() (data [8]byte, err error) {
|
||||||
if d.Config.Mode == ModeForced {
|
err = d.bus.ReadRegister(uint8(d.Address), REG_PRESSURE, data[:])
|
||||||
// Write the CTRL_MEAS register to trigger a measurement
|
|
||||||
legacy.WriteRegister(d.bus, uint8(d.Address), CTRL_MEAS_ADDR, []byte{
|
|
||||||
byte(d.Config.Temperature<<5) |
|
|
||||||
byte(d.Config.Pressure<<2) |
|
|
||||||
byte(d.Config.Mode)})
|
|
||||||
|
|
||||||
time.Sleep(d.measurementDelay())
|
|
||||||
}
|
|
||||||
|
|
||||||
err = legacy.ReadRegister(d.bus, uint8(d.Address), REG_PRESSURE, data[:])
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
println(err)
|
println(err)
|
||||||
return
|
return
|
||||||
@@ -331,39 +256,3 @@ func (d *Device) calculateHumidity(data [8]byte, tFine int32) int32 {
|
|||||||
return int32(100 * h)
|
return int32(100 * h)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// measurementDelay returns how much time each measurement will take
|
|
||||||
// on the device.
|
|
||||||
//
|
|
||||||
// This is used in forced mode to wait until a measurement is complete.
|
|
||||||
func (d *Device) measurementDelay() time.Duration {
|
|
||||||
const MeasOffset = 1250
|
|
||||||
const MeasDur = 2300
|
|
||||||
const HumMeasOffset = 575
|
|
||||||
const MeasScalingFactor = 1000
|
|
||||||
|
|
||||||
// delay is based on over-sampling rate - this table converts from
|
|
||||||
// setting to number samples
|
|
||||||
sampleRateConv := []int{0, 1, 2, 4, 8, 16}
|
|
||||||
|
|
||||||
tempOsr := 16
|
|
||||||
if d.Config.Temperature <= Sampling16X {
|
|
||||||
tempOsr = sampleRateConv[d.Config.Temperature]
|
|
||||||
}
|
|
||||||
|
|
||||||
presOsr := 16
|
|
||||||
if d.Config.Temperature <= Sampling16X {
|
|
||||||
presOsr = sampleRateConv[d.Config.Pressure]
|
|
||||||
}
|
|
||||||
|
|
||||||
humOsr := 16
|
|
||||||
if d.Config.Temperature <= Sampling16X {
|
|
||||||
humOsr = sampleRateConv[d.Config.Humidity]
|
|
||||||
}
|
|
||||||
|
|
||||||
max_delay := ((MeasOffset + (MeasDur * tempOsr) +
|
|
||||||
((MeasDur * presOsr) + HumMeasOffset) +
|
|
||||||
((MeasDur * humOsr) + HumMeasOffset)) / MeasScalingFactor)
|
|
||||||
|
|
||||||
return time.Duration(max_delay) * time.Millisecond
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -20,50 +20,6 @@ const (
|
|||||||
CHIP_ID = 0x60
|
CHIP_ID = 0x60
|
||||||
)
|
)
|
||||||
|
|
||||||
// Increasing sampling rate increases precision but also the wait time for measurements. The datasheet has a table of
|
|
||||||
// suggested values for oversampling, output data rates, and iir filter coefficients by use case.
|
|
||||||
const (
|
|
||||||
SamplingOff Oversampling = iota
|
|
||||||
Sampling1X
|
|
||||||
Sampling2X
|
|
||||||
Sampling4X
|
|
||||||
Sampling8X
|
|
||||||
Sampling16X
|
|
||||||
)
|
|
||||||
|
|
||||||
// In normal mode (the default) the sensor takes masurements periodically. In forced
|
|
||||||
// mode, the sensor takes a measurement only when requested.
|
|
||||||
//
|
|
||||||
// For use-cases with infrequent sampling, forced mode is more power efficient.
|
|
||||||
const (
|
|
||||||
ModeNormal Mode = 0x03
|
|
||||||
ModeForced Mode = 0x01
|
|
||||||
ModeSleep Mode = 0x00
|
|
||||||
)
|
|
||||||
|
|
||||||
// IIR filter coefficients, higher values means steadier measurements but slower reaction times
|
|
||||||
const (
|
|
||||||
Coeff0 FilterCoefficient = iota
|
|
||||||
Coeff2
|
|
||||||
Coeff4
|
|
||||||
Coeff8
|
|
||||||
Coeff16
|
|
||||||
)
|
|
||||||
|
|
||||||
// Period of standby in normal mode which controls how often measurements are taken
|
|
||||||
//
|
|
||||||
// Note Period10ms and Period20ms are out of sequence, but are per the datasheet
|
|
||||||
const (
|
|
||||||
Period0_5ms Period = 0b000
|
|
||||||
Period62_5ms = 0b001
|
|
||||||
Period125ms = 0b010
|
|
||||||
Period250ms = 0b011
|
|
||||||
Period500ms = 0b100
|
|
||||||
Period1000ms = 0b101
|
|
||||||
Period10ms = 0b110
|
|
||||||
Period20ms = 0b111
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
SEALEVEL_PRESSURE float32 = 1013.25 // in hPa
|
SEALEVEL_PRESSURE float32 = 1013.25 // in hPa
|
||||||
)
|
)
|
||||||
|
|||||||
+6
-23
@@ -6,11 +6,9 @@
|
|||||||
package bmp180 // import "tinygo.org/x/drivers/bmp180"
|
package bmp180 // import "tinygo.org/x/drivers/bmp180"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"math"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"tinygo.org/x/drivers"
|
"tinygo.org/x/drivers"
|
||||||
"tinygo.org/x/drivers/internal/legacy"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// OversamplingMode is the oversampling ratio of the pressure measurement.
|
// OversamplingMode is the oversampling ratio of the pressure measurement.
|
||||||
@@ -56,7 +54,7 @@ func New(bus drivers.I2C) Device {
|
|||||||
// It does a "who am I" request and checks the response.
|
// It does a "who am I" request and checks the response.
|
||||||
func (d *Device) Connected() bool {
|
func (d *Device) Connected() bool {
|
||||||
data := []byte{0}
|
data := []byte{0}
|
||||||
legacy.ReadRegister(d.bus, uint8(d.Address), WHO_AM_I, data)
|
d.bus.ReadRegister(uint8(d.Address), WHO_AM_I, data)
|
||||||
return data[0] == CHIP_ID
|
return data[0] == CHIP_ID
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,7 +62,7 @@ func (d *Device) Connected() bool {
|
|||||||
// read the calibration coefficients.
|
// read the calibration coefficients.
|
||||||
func (d *Device) Configure() {
|
func (d *Device) Configure() {
|
||||||
data := make([]byte, 22)
|
data := make([]byte, 22)
|
||||||
err := legacy.ReadRegister(d.bus, uint8(d.Address), AC1_MSB, data)
|
err := d.bus.ReadRegister(uint8(d.Address), AC1_MSB, data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -125,27 +123,12 @@ func (d *Device) ReadPressure() (pressure int32, err error) {
|
|||||||
return 1000 * (p + ((x1 + x2 + 3791) >> 4)), nil
|
return 1000 * (p + ((x1 + x2 + 3791) >> 4)), 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() (int32, error) {
|
|
||||||
mPa, err := d.ReadPressure()
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
atmP := float32(mPa) / 100000
|
|
||||||
|
|
||||||
return int32(44330.0 * (1.0 - math.Pow(float64(atmP/SEALEVEL_PRESSURE), 0.1903))), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// rawTemp returns the sensor's raw values of the temperature
|
// rawTemp returns the sensor's raw values of the temperature
|
||||||
func (d *Device) rawTemp() (int32, error) {
|
func (d *Device) rawTemp() (int32, error) {
|
||||||
legacy.WriteRegister(d.bus, uint8(d.Address), REG_CTRL, []byte{CMD_TEMP})
|
d.bus.WriteRegister(uint8(d.Address), REG_CTRL, []byte{CMD_TEMP})
|
||||||
time.Sleep(5 * time.Millisecond)
|
time.Sleep(5 * time.Millisecond)
|
||||||
data := make([]byte, 2)
|
data := make([]byte, 2)
|
||||||
err := legacy.ReadRegister(d.bus, uint8(d.Address), REG_TEMP_MSB, data)
|
err := d.bus.ReadRegister(uint8(d.Address), REG_TEMP_MSB, data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
@@ -161,10 +144,10 @@ func (d *Device) calculateB5(rawTemp int32) int32 {
|
|||||||
|
|
||||||
// rawPressure returns the sensor's raw values of the pressure
|
// rawPressure returns the sensor's raw values of the pressure
|
||||||
func (d *Device) rawPressure(mode OversamplingMode) (int32, error) {
|
func (d *Device) rawPressure(mode OversamplingMode) (int32, error) {
|
||||||
legacy.WriteRegister(d.bus, uint8(d.Address), REG_CTRL, []byte{CMD_PRESSURE + byte(mode<<6)})
|
d.bus.WriteRegister(uint8(d.Address), REG_CTRL, []byte{CMD_PRESSURE + byte(mode<<6)})
|
||||||
time.Sleep(pauseForReading(mode))
|
time.Sleep(pauseForReading(mode))
|
||||||
data := make([]byte, 3)
|
data := make([]byte, 3)
|
||||||
err := legacy.ReadRegister(d.bus, uint8(d.Address), REG_PRESSURE_MSB, data)
|
err := d.bus.ReadRegister(uint8(d.Address), REG_PRESSURE_MSB, data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,3 @@ const (
|
|||||||
// ULTRAHIGHRESOLUTION is the highest oversampling mode of the pressure measurement.
|
// ULTRAHIGHRESOLUTION is the highest oversampling mode of the pressure measurement.
|
||||||
ULTRAHIGHRESOLUTION
|
ULTRAHIGHRESOLUTION
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
SEALEVEL_PRESSURE float32 = 1013.25 // in hPa
|
|
||||||
)
|
|
||||||
|
|||||||
+8
-9
@@ -4,7 +4,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"tinygo.org/x/drivers"
|
"tinygo.org/x/drivers"
|
||||||
"tinygo.org/x/drivers/internal/legacy"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// OversamplingMode is the oversampling ratio of the temperature or pressure measurement.
|
// OversamplingMode is the oversampling ratio of the temperature or pressure measurement.
|
||||||
@@ -65,14 +64,14 @@ func New(bus drivers.I2C) Device {
|
|||||||
// It does a "who am I" request and checks the response.
|
// It does a "who am I" request and checks the response.
|
||||||
func (d *Device) Connected() bool {
|
func (d *Device) Connected() bool {
|
||||||
data := make([]byte, 1)
|
data := make([]byte, 1)
|
||||||
legacy.ReadRegister(d.bus, uint8(d.Address), REG_ID, data)
|
d.bus.ReadRegister(uint8(d.Address), REG_ID, data)
|
||||||
return data[0] == CHIP_ID
|
return data[0] == CHIP_ID
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset preforms complete power-on-reset procedure.
|
// Reset preforms complete power-on-reset procedure.
|
||||||
// It is required to call Configure afterwards.
|
// It is required to call Configure afterwards.
|
||||||
func (d *Device) Reset() {
|
func (d *Device) Reset() {
|
||||||
legacy.WriteRegister(d.bus, uint8(d.Address), REG_RESET, []byte{CMD_RESET})
|
d.bus.WriteRegister(uint8(d.Address), REG_RESET, []byte{CMD_RESET})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configure sets up the device for communication and
|
// Configure sets up the device for communication and
|
||||||
@@ -86,15 +85,15 @@ func (d *Device) Configure(standby Standby, filter Filter, temp Oversampling, pr
|
|||||||
|
|
||||||
// Write the configuration (standby, filter, spi 3 wire)
|
// Write the configuration (standby, filter, spi 3 wire)
|
||||||
config := uint(d.Standby<<5) | uint(d.Filter<<2) | 0x00
|
config := uint(d.Standby<<5) | uint(d.Filter<<2) | 0x00
|
||||||
legacy.WriteRegister(d.bus, uint8(d.Address), REG_CONFIG, []byte{byte(config)})
|
d.bus.WriteRegister(uint8(d.Address), REG_CONFIG, []byte{byte(config)})
|
||||||
|
|
||||||
// Write the control (temperature oversampling, pressure oversampling,
|
// Write the control (temperature oversampling, pressure oversampling,
|
||||||
config = uint(d.Temperature<<5) | uint(d.Pressure<<2) | uint(d.Mode)
|
config = uint(d.Temperature<<5) | uint(d.Pressure<<2) | uint(d.Mode)
|
||||||
legacy.WriteRegister(d.bus, uint8(d.Address), REG_CTRL_MEAS, []byte{byte(config)})
|
d.bus.WriteRegister(uint8(d.Address), REG_CTRL_MEAS, []byte{byte(config)})
|
||||||
|
|
||||||
// Read Calibration data
|
// Read Calibration data
|
||||||
data := make([]byte, 24)
|
data := make([]byte, 24)
|
||||||
err := legacy.ReadRegister(d.bus, uint8(d.Address), REG_CALI, data)
|
err := d.bus.ReadRegister(uint8(d.Address), REG_CALI, data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -208,18 +207,18 @@ func (d *Device) readData(register int, n int) ([]byte, error) {
|
|||||||
// After the measurement in FORCED mode, the sensor will return to SLEEP mode
|
// After the measurement in FORCED mode, the sensor will return to SLEEP mode
|
||||||
if d.Mode != MODE_NORMAL {
|
if d.Mode != MODE_NORMAL {
|
||||||
config := uint(d.Temperature<<5) | uint(d.Pressure<<2) | uint(MODE_FORCED)
|
config := uint(d.Temperature<<5) | uint(d.Pressure<<2) | uint(MODE_FORCED)
|
||||||
legacy.WriteRegister(d.bus, uint8(d.Address), REG_CTRL_MEAS, []byte{byte(config)})
|
d.bus.WriteRegister(uint8(d.Address), REG_CTRL_MEAS, []byte{byte(config)})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check STATUS register, wait if data is not available yet
|
// Check STATUS register, wait if data is not available yet
|
||||||
status := make([]byte, 1)
|
status := make([]byte, 1)
|
||||||
for legacy.ReadRegister(d.bus, uint8(d.Address), uint8(REG_STATUS), status[0:]); status[0] != 4 && status[0] != 0; legacy.ReadRegister(d.bus, uint8(d.Address), uint8(REG_STATUS), status[0:]) {
|
for d.bus.ReadRegister(uint8(d.Address), uint8(REG_STATUS), status[0:]); status[0] != 4 && status[0] != 0; d.bus.ReadRegister(uint8(d.Address), uint8(REG_STATUS), status[0:]) {
|
||||||
time.Sleep(time.Millisecond)
|
time.Sleep(time.Millisecond)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read the requested register
|
// Read the requested register
|
||||||
data := make([]byte, n)
|
data := make([]byte, n)
|
||||||
err := legacy.ReadRegister(d.bus, uint8(d.Address), uint8(register), data[:])
|
err := d.bus.ReadRegister(uint8(d.Address), uint8(register), data[:])
|
||||||
return data, err
|
return data, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-3
@@ -4,7 +4,6 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
"tinygo.org/x/drivers"
|
"tinygo.org/x/drivers"
|
||||||
"tinygo.org/x/drivers/internal/legacy"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -241,10 +240,10 @@ func (d *Device) configurationError() bool {
|
|||||||
|
|
||||||
func (d *Device) readRegister(register byte, len int) (data []byte, err error) {
|
func (d *Device) readRegister(register byte, len int) (data []byte, err error) {
|
||||||
data = make([]byte, len)
|
data = make([]byte, len)
|
||||||
err = legacy.ReadRegister(d.bus, d.Address, register, data)
|
err = d.bus.ReadRegister(d.Address, register, data)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Device) writeRegister(register byte, data byte) error {
|
func (d *Device) writeRegister(register byte, data byte) error {
|
||||||
return legacy.WriteRegister(d.bus, d.Address, register, []byte{data})
|
return d.bus.WriteRegister(d.Address, register, []byte{data})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,12 +54,6 @@ func (l *Device) Tone(hz, duration float64) (err error) {
|
|||||||
|
|
||||||
tempo := ((60 / l.BPM) * (duration * 1000))
|
tempo := ((60 / l.BPM) * (duration * 1000))
|
||||||
|
|
||||||
// no tone during rest, just let the duration pass.
|
|
||||||
if hz == Rest {
|
|
||||||
time.Sleep(time.Duration(tempo) * time.Millisecond)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 0.0; i < tempo*1000; i += tone * 2.0 {
|
for i := 0.0; i < tempo*1000; i += tone * 2.0 {
|
||||||
if err = l.On(); err != nil {
|
if err = l.On(); err != nil {
|
||||||
return
|
return
|
||||||
|
|||||||
+3
-3
@@ -1,9 +1,9 @@
|
|||||||
package buzzer
|
package buzzer
|
||||||
|
|
||||||
const (
|
const (
|
||||||
Whole = 4.0
|
Whole = 4
|
||||||
Half = 2.0
|
Half = 2
|
||||||
Quarter = 1.0
|
Quarter = 1
|
||||||
Eighth = 0.500
|
Eighth = 0.500
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -1,54 +0,0 @@
|
|||||||
#include <stdint.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
|
|
||||||
// Loop the given times, where one loop takes four CPU cycles.
|
|
||||||
bool tinygo_drivers_sleep(uint32_t cycles) {
|
|
||||||
// In this function, a [n] comment indicates the number of cycles an
|
|
||||||
// instruction or a set of instructions take. This is typically 1 for most
|
|
||||||
// arithmetic instructions, and a bit more for branches.
|
|
||||||
#if __ARM_ARCH_6M__ || __ARM_ARCH_7M__ || __ARM_ARCH_7EM__
|
|
||||||
// Inline assembly for Cortex-M0/M0+/M3/M4/M7.
|
|
||||||
// The Cortex-M0 (but not M0+) takes one more cycle, so is off by 12.5%.
|
|
||||||
// Others should be basically cycle-accurate (with a slight overhead to
|
|
||||||
// calculate the number of cycles). Unfortunately, there doesn't appear to
|
|
||||||
// be a preprocessor macro to detect the Cortex-M0 specifically (although we
|
|
||||||
// could rely on macros like NRF51).
|
|
||||||
|
|
||||||
// Each loop takes 8 cycles (5 nops, 1 sub, and 2 for the branch).
|
|
||||||
uint32_t loops = (cycles + 7) / 8;
|
|
||||||
__asm__ __volatile__(
|
|
||||||
"1:\n\t"
|
|
||||||
"nop\n\t" // [5] nops
|
|
||||||
"nop\n\t"
|
|
||||||
"nop\n\t"
|
|
||||||
"nop\n\t"
|
|
||||||
"nop\n\t"
|
|
||||||
"subs %[loops], #1\n\t" // [1]
|
|
||||||
"bne 1b" // [1-4], at least 2 cycles if taken
|
|
||||||
: [loops]"+r"(loops)
|
|
||||||
);
|
|
||||||
return true;
|
|
||||||
#elif __XTENSA__
|
|
||||||
// Inline assembly for Xtensa.
|
|
||||||
// I don't know exactly how many cycles a branch takes, so I've taken a
|
|
||||||
// conservative guess and assume it takes only one cycle. In practice, it's
|
|
||||||
// probably more than that.
|
|
||||||
uint32_t loops = (cycles + 7) / 8;
|
|
||||||
__asm__ __volatile__(
|
|
||||||
"1:\n\t"
|
|
||||||
"nop\n\t" // [6] nops
|
|
||||||
"nop\n\t"
|
|
||||||
"nop\n\t"
|
|
||||||
"nop\n\t"
|
|
||||||
"nop\n\t"
|
|
||||||
"nop\n\t"
|
|
||||||
"addi %[loops], %[loops], -1\n\t" // [1]
|
|
||||||
"bnez %[loops], 1b" // [1?]
|
|
||||||
: [loops]"+r"(loops)
|
|
||||||
);
|
|
||||||
return true;
|
|
||||||
#else
|
|
||||||
// Unknown architecture, so fall back to time.Sleep.
|
|
||||||
return false;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
package delay
|
|
||||||
|
|
||||||
import (
|
|
||||||
"machine"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
/*
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
bool tinygo_drivers_sleep(uint32_t ticks);
|
|
||||||
*/
|
|
||||||
import "C"
|
|
||||||
|
|
||||||
// Sleep for a very precise short duration by busy-waiting for the given time.
|
|
||||||
// This is not an efficient way to sleep: it will needlessly burn cycles while
|
|
||||||
// sleeping. But it is useful for sleeping for a very short duration, for
|
|
||||||
// example for bit-banged protocols.
|
|
||||||
//
|
|
||||||
// Longer durations (longer than a few milliseconds) will be handled by calling
|
|
||||||
// time.Sleep instead.
|
|
||||||
//
|
|
||||||
// This function should be called with a constant duration value, in which case
|
|
||||||
// the call will typically be fully inlined and only take up around nine
|
|
||||||
// instructions for the entire loop.
|
|
||||||
//
|
|
||||||
//go:inline
|
|
||||||
func Sleep(duration time.Duration) {
|
|
||||||
if time.Duration(uint32(duration)&0xff_ffff) != duration {
|
|
||||||
// This is a long duration (more than 16ms) which shouldn't be done by
|
|
||||||
// busy-waiting.
|
|
||||||
time.Sleep(duration)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate the number of cycles we should sleep:
|
|
||||||
// cycles = duration * freq / 1e9
|
|
||||||
// Avoiding a 64-bit division:
|
|
||||||
// cycles = duration * (freq/1000_000) / 1000
|
|
||||||
//
|
|
||||||
// This assumes:
|
|
||||||
// * The CPU frequency is a constant and can trivially be
|
|
||||||
// const-propagated, therefore the divide by 1000_000 is done at compile
|
|
||||||
// time.
|
|
||||||
// * The CPU frequency is a multiple of 1000_000, which is true for most
|
|
||||||
// chips (examples: 16MHz, 48MHz, 120MHz, etc).
|
|
||||||
// * The division by 1000 can be done efficiently (Cortex-M3 and up), or
|
|
||||||
// can be fully const-propagated.
|
|
||||||
// * The CPU frequency is lower than 256MHz. If it is higher, long sleep
|
|
||||||
// times (1-16ms) may not work correctly.
|
|
||||||
cycles := uint32(duration) * (machine.CPUFrequency() / 1000_000) / 1000
|
|
||||||
slept := C.tinygo_drivers_sleep(C.uint32_t(cycles))
|
|
||||||
if !slept {
|
|
||||||
// Fallback for platforms without inline assembly support.
|
|
||||||
time.Sleep(duration)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
//go:build tinygo
|
//go:build tinygo
|
||||||
|
// +build tinygo
|
||||||
|
|
||||||
// Package dht provides a driver for DHTXX family temperature and humidity sensors.
|
// Package dht provides a driver for DHTXX family temperature and humidity sensors.
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
//go:build mimxrt1062 || stm32f405 || atsamd51 || stm32f103xx || k210 || stm32f407
|
//go:build mimxrt1062 || stm32f405 || atsamd51 || stm32f103xx || k210 || stm32f407
|
||||||
|
// +build mimxrt1062 stm32f405 atsamd51 stm32f103xx k210 stm32f407
|
||||||
|
|
||||||
package dht // import "tinygo.org/x/drivers/dht"
|
package dht // import "tinygo.org/x/drivers/dht"
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
//go:build !mimxrt1062 && !stm32f405 && !atsamd51 && !stm32f103xx && !k210 && !stm32f407
|
//go:build !mimxrt1062 && !stm32f405 && !atsamd51 && !stm32f103xx && !k210 && !stm32f407
|
||||||
|
// +build !mimxrt1062,!stm32f405,!atsamd51,!stm32f103xx,!k210,!stm32f407
|
||||||
|
|
||||||
package dht // import "tinygo.org/x/drivers/dht"
|
package dht // import "tinygo.org/x/drivers/dht"
|
||||||
|
|
||||||
|
|||||||
+3
-3
@@ -1,4 +1,5 @@
|
|||||||
//go:build tinygo
|
//go:build tinygo
|
||||||
|
// +build tinygo
|
||||||
|
|
||||||
// Package dht provides a driver for DHTXX family temperature and humidity sensors.
|
// Package dht provides a driver for DHTXX family temperature and humidity sensors.
|
||||||
//
|
//
|
||||||
@@ -10,7 +11,6 @@ package dht // import "tinygo.org/x/drivers/dht"
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"machine"
|
"machine"
|
||||||
"runtime/interrupt"
|
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -160,8 +160,8 @@ func (t *device) read() error {
|
|||||||
// interrupts
|
// interrupts
|
||||||
func receiveSignals(pin machine.Pin, result []counter) {
|
func receiveSignals(pin machine.Pin, result []counter) {
|
||||||
i := uint8(0)
|
i := uint8(0)
|
||||||
mask := interrupt.Disable()
|
machine.UART1.Interrupt.Disable()
|
||||||
defer interrupt.Restore(mask)
|
defer machine.UART1.Interrupt.Enable()
|
||||||
for ; i < 40; i++ {
|
for ; i < 40; i++ {
|
||||||
result[i*2] = expectChange(pin, false)
|
result[i*2] = expectChange(pin, false)
|
||||||
result[i*2+1] = expectChange(pin, true)
|
result[i*2+1] = expectChange(pin, true)
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
//go:build tinygo
|
//go:build tinygo
|
||||||
|
// +build tinygo
|
||||||
|
|
||||||
// Package dht provides a driver for DHTXX family temperature and humidity sensors.
|
// Package dht provides a driver for DHTXX family temperature and humidity sensors.
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
//go:build tinygo
|
//go:build tinygo
|
||||||
|
// +build tinygo
|
||||||
|
|
||||||
package dht // import "tinygo.org/x/drivers/dht"
|
package dht // import "tinygo.org/x/drivers/dht"
|
||||||
|
|
||||||
|
|||||||
@@ -12,19 +12,3 @@ type Displayer interface {
|
|||||||
// Display sends the buffer (if any) to the screen.
|
// Display sends the buffer (if any) to the screen.
|
||||||
Display() error
|
Display() error
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rotation is how much a display has been rotated. Displays can be rotated, and
|
|
||||||
// sometimes also mirrored.
|
|
||||||
type Rotation uint8
|
|
||||||
|
|
||||||
// Clockwise rotation of the screen.
|
|
||||||
const (
|
|
||||||
Rotation0 = iota
|
|
||||||
Rotation90
|
|
||||||
Rotation180
|
|
||||||
Rotation270
|
|
||||||
Rotation0Mirror
|
|
||||||
Rotation90Mirror
|
|
||||||
Rotation180Mirror
|
|
||||||
Rotation270Mirror
|
|
||||||
)
|
|
||||||
|
|||||||
+4
-5
@@ -9,7 +9,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"tinygo.org/x/drivers"
|
"tinygo.org/x/drivers"
|
||||||
"tinygo.org/x/drivers/internal/legacy"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Device wraps an I2C connection to a DS1307 device.
|
// Device wraps an I2C connection to a DS1307 device.
|
||||||
@@ -45,7 +44,7 @@ func (d *Device) SetTime(t time.Time) error {
|
|||||||
// ReadTime returns the date and time
|
// ReadTime returns the date and time
|
||||||
func (d *Device) ReadTime() (time.Time, error) {
|
func (d *Device) ReadTime() (time.Time, error) {
|
||||||
data := make([]byte, 8)
|
data := make([]byte, 8)
|
||||||
err := legacy.ReadRegister(d.bus, d.Address, uint8(TimeDate), data)
|
err := d.bus.ReadRegister(d.Address, uint8(TimeDate), data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return time.Time{}, err
|
return time.Time{}, err
|
||||||
}
|
}
|
||||||
@@ -106,7 +105,7 @@ func (d *Device) Read(data []uint8) (n int, err error) {
|
|||||||
if int(d.AddressSRAM)+len(data)-1 > SRAMEndAddress {
|
if int(d.AddressSRAM)+len(data)-1 > SRAMEndAddress {
|
||||||
return 0, errors.New("EOF")
|
return 0, errors.New("EOF")
|
||||||
}
|
}
|
||||||
err = legacy.ReadRegister(d.bus, d.Address, d.AddressSRAM, data)
|
err = d.bus.ReadRegister(d.Address, d.AddressSRAM, data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
@@ -125,7 +124,7 @@ func (d *Device) SetOscillatorFrequency(sqw uint8) error {
|
|||||||
// IsOscillatorRunning returns if the oscillator is running
|
// IsOscillatorRunning returns if the oscillator is running
|
||||||
func (d *Device) IsOscillatorRunning() bool {
|
func (d *Device) IsOscillatorRunning() bool {
|
||||||
data := []byte{0}
|
data := []byte{0}
|
||||||
err := legacy.ReadRegister(d.bus, d.Address, uint8(TimeDate), data)
|
err := d.bus.ReadRegister(d.Address, uint8(TimeDate), data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -135,7 +134,7 @@ func (d *Device) IsOscillatorRunning() bool {
|
|||||||
// SetOscillatorRunning starts/stops internal oscillator by toggling halt bit
|
// SetOscillatorRunning starts/stops internal oscillator by toggling halt bit
|
||||||
func (d *Device) SetOscillatorRunning(running bool) error {
|
func (d *Device) SetOscillatorRunning(running bool) error {
|
||||||
data := make([]byte, 3)
|
data := make([]byte, 3)
|
||||||
err := legacy.ReadRegister(d.bus, d.Address, uint8(TimeDate), data)
|
err := d.bus.ReadRegister(d.Address, uint8(TimeDate), data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,89 +0,0 @@
|
|||||||
// Package ds18b20 provides a driver for the DS18B20 digital thermometer
|
|
||||||
//
|
|
||||||
// Datasheet:
|
|
||||||
// https://www.analog.com/media/en/technical-documentation/data-sheets/DS18B20.pdf
|
|
||||||
package ds18b20 // import "tinygo.org/x/drivers/ds18b20"
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Device ROM commands
|
|
||||||
const (
|
|
||||||
CONVERT_TEMPERATURE uint8 = 0x44
|
|
||||||
READ_SCRATCHPAD uint8 = 0xBE
|
|
||||||
WRITE_SCRATCHPAD uint8 = 0x4E
|
|
||||||
)
|
|
||||||
|
|
||||||
type OneWireDevice interface {
|
|
||||||
Write(uint8)
|
|
||||||
Read() uint8
|
|
||||||
Select([]uint8) error
|
|
||||||
Сrc8([]uint8, int) uint8
|
|
||||||
}
|
|
||||||
|
|
||||||
// Device wraps a connection to an 1-Wire devices.
|
|
||||||
type Device struct {
|
|
||||||
owd OneWireDevice
|
|
||||||
}
|
|
||||||
|
|
||||||
// Errors list
|
|
||||||
var (
|
|
||||||
errReadTemperature = errors.New("Error: DS18B20. Read temperature error: CRC mismatch.")
|
|
||||||
)
|
|
||||||
|
|
||||||
func New(owd OneWireDevice) Device {
|
|
||||||
return Device{
|
|
||||||
owd: owd,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Configure. Initializes the device, left for compatibility reasons.
|
|
||||||
func (d Device) Configure() {}
|
|
||||||
|
|
||||||
// ThermometerResolution sets thermometer resolution from 9 to 12 bits
|
|
||||||
func (d Device) ThermometerResolution(romid []uint8, resolution uint8) {
|
|
||||||
if 9 <= resolution && resolution <= 12 {
|
|
||||||
d.owd.Select(romid)
|
|
||||||
d.owd.Write(WRITE_SCRATCHPAD) // send three data bytes to scratchpad (TH, TL, and config)
|
|
||||||
d.owd.Write(0xFF) // to TH
|
|
||||||
d.owd.Write(0x00) // to TL
|
|
||||||
d.owd.Write(((resolution - 9) << 5) | 0x1F) // to resolution config
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// RequestTemperature sends request to device
|
|
||||||
func (d Device) RequestTemperature(romid []uint8) {
|
|
||||||
d.owd.Select(romid)
|
|
||||||
d.owd.Write(CONVERT_TEMPERATURE)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadTemperatureRaw returns the raw temperature.
|
|
||||||
// ScratchPad memory map:
|
|
||||||
// byte 0: Temperature LSB
|
|
||||||
// byte 1: Temperature MSB
|
|
||||||
func (d Device) ReadTemperatureRaw(romid []uint8) ([]uint8, error) {
|
|
||||||
spb := make([]uint8, 9) // ScratchPad buffer
|
|
||||||
d.owd.Select(romid)
|
|
||||||
d.owd.Write(READ_SCRATCHPAD)
|
|
||||||
for i := 0; i < 9; i++ {
|
|
||||||
spb[i] = d.owd.Read()
|
|
||||||
}
|
|
||||||
if d.owd.Сrc8(spb, 8) != spb[8] {
|
|
||||||
return nil, errReadTemperature
|
|
||||||
}
|
|
||||||
return spb[:2:2], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadTemperature returns the temperature in celsius milli degrees (°C/1000)
|
|
||||||
func (d Device) ReadTemperature(romid []uint8) (int32, error) {
|
|
||||||
raw, err := d.ReadTemperatureRaw(romid)
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
t := int32(uint16(raw[0]) | uint16(raw[1])<<8)
|
|
||||||
if t&0x8000 == 0x8000 {
|
|
||||||
t -= 0x10000
|
|
||||||
}
|
|
||||||
return (t * 625 / 10), nil
|
|
||||||
}
|
|
||||||
+11
-46
@@ -8,7 +8,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"tinygo.org/x/drivers"
|
"tinygo.org/x/drivers"
|
||||||
"tinygo.org/x/drivers/internal/legacy"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Mode uint8
|
type Mode uint8
|
||||||
@@ -38,7 +37,7 @@ func (d *Device) Configure() bool {
|
|||||||
// IsTimeValid return true/false is the time in the device is valid
|
// IsTimeValid return true/false is the time in the device is valid
|
||||||
func (d *Device) IsTimeValid() bool {
|
func (d *Device) IsTimeValid() bool {
|
||||||
data := []byte{0}
|
data := []byte{0}
|
||||||
err := legacy.ReadRegister(d.bus, uint8(d.Address), REG_STATUS, data)
|
err := d.bus.ReadRegister(uint8(d.Address), REG_STATUS, data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -48,7 +47,7 @@ func (d *Device) IsTimeValid() bool {
|
|||||||
// IsRunning returns if the oscillator is running
|
// IsRunning returns if the oscillator is running
|
||||||
func (d *Device) IsRunning() bool {
|
func (d *Device) IsRunning() bool {
|
||||||
data := []uint8{0}
|
data := []uint8{0}
|
||||||
err := legacy.ReadRegister(d.bus, uint8(d.Address), REG_CONTROL, data)
|
err := d.bus.ReadRegister(uint8(d.Address), REG_CONTROL, data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -58,7 +57,7 @@ func (d *Device) IsRunning() bool {
|
|||||||
// SetRunning starts the internal oscillator
|
// SetRunning starts the internal oscillator
|
||||||
func (d *Device) SetRunning(isRunning bool) error {
|
func (d *Device) SetRunning(isRunning bool) error {
|
||||||
data := []uint8{0}
|
data := []uint8{0}
|
||||||
err := legacy.ReadRegister(d.bus, uint8(d.Address), REG_CONTROL, data)
|
err := d.bus.ReadRegister(uint8(d.Address), REG_CONTROL, data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -67,32 +66,22 @@ func (d *Device) SetRunning(isRunning bool) error {
|
|||||||
} else {
|
} else {
|
||||||
data[0] |= 1 << EOSC
|
data[0] |= 1 << EOSC
|
||||||
}
|
}
|
||||||
err = legacy.WriteRegister(d.bus, uint8(d.Address), REG_CONTROL, data)
|
err = d.bus.WriteRegister(uint8(d.Address), REG_CONTROL, data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetTime sets the date and time in the DS3231. The DS3231 hardware supports
|
// SetTime sets the date and time in the DS3231
|
||||||
// only a 2-digit year field, so the current year will be stored as an offset
|
|
||||||
// from the year 2000, which supports the year 2000 until 2100.
|
|
||||||
//
|
|
||||||
// The DS3231 also supports a one-bit 'century' flag which is set by the chip
|
|
||||||
// when the year field rolls over from 99 to 00. The current code interprets
|
|
||||||
// this flag to be the year 2100, which appears to extend the range of years
|
|
||||||
// until the year 2200. However the DS3231 does not incorporate the 'century'
|
|
||||||
// flag in its leap year calculation, so it will incorrectly identify the year
|
|
||||||
// 2100 as a leap year, causing it to increment from 2100-02-28 to 2100-02-29
|
|
||||||
// instead of 2100-03-01.
|
|
||||||
func (d *Device) SetTime(dt time.Time) error {
|
func (d *Device) SetTime(dt time.Time) error {
|
||||||
data := []byte{0}
|
data := []byte{0}
|
||||||
err := legacy.ReadRegister(d.bus, uint8(d.Address), REG_STATUS, data)
|
err := d.bus.ReadRegister(uint8(d.Address), REG_STATUS, data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
data[0] &^= 1 << OSF
|
data[0] &^= 1 << OSF
|
||||||
err = legacy.WriteRegister(d.bus, uint8(d.Address), REG_STATUS, data)
|
err = d.bus.WriteRegister(uint8(d.Address), REG_STATUS, data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -103,10 +92,6 @@ func (d *Device) SetTime(dt time.Time) error {
|
|||||||
data[2] = uint8ToBCD(uint8(dt.Hour()))
|
data[2] = uint8ToBCD(uint8(dt.Hour()))
|
||||||
|
|
||||||
year := uint8(dt.Year() - 2000)
|
year := uint8(dt.Year() - 2000)
|
||||||
// This code interprets the centuryFlag to be the year 2100. Warning: The
|
|
||||||
// DS3231 does not incorporate the centuryFlag in its leap year calculation.
|
|
||||||
// It will increment from 2100-02-28 to 2100-02-29, which is incorrect because
|
|
||||||
// the year 2100 is not a leap year in the Gregorian calendar.
|
|
||||||
centuryFlag := uint8(0)
|
centuryFlag := uint8(0)
|
||||||
if year >= 100 {
|
if year >= 100 {
|
||||||
year -= 100
|
year -= 100
|
||||||
@@ -118,7 +103,7 @@ func (d *Device) SetTime(dt time.Time) error {
|
|||||||
data[5] = uint8ToBCD(uint8(dt.Month()) | centuryFlag)
|
data[5] = uint8ToBCD(uint8(dt.Month()) | centuryFlag)
|
||||||
data[6] = uint8ToBCD(year)
|
data[6] = uint8ToBCD(year)
|
||||||
|
|
||||||
err = legacy.WriteRegister(d.bus, uint8(d.Address), REG_TIMEDATE, data)
|
err = d.bus.WriteRegister(uint8(d.Address), REG_TIMEDATE, data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -129,7 +114,7 @@ func (d *Device) SetTime(dt time.Time) error {
|
|||||||
// ReadTime returns the date and time
|
// ReadTime returns the date and time
|
||||||
func (d *Device) ReadTime() (dt time.Time, err error) {
|
func (d *Device) ReadTime() (dt time.Time, err error) {
|
||||||
data := make([]uint8, 7)
|
data := make([]uint8, 7)
|
||||||
err = legacy.ReadRegister(d.bus, uint8(d.Address), REG_TIMEDATE, data)
|
err = d.bus.ReadRegister(uint8(d.Address), REG_TIMEDATE, data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -151,31 +136,11 @@ func (d *Device) ReadTime() (dt time.Time, err error) {
|
|||||||
// ReadTemperature returns the temperature in millicelsius (mC)
|
// ReadTemperature returns the temperature in millicelsius (mC)
|
||||||
func (d *Device) ReadTemperature() (int32, error) {
|
func (d *Device) ReadTemperature() (int32, error) {
|
||||||
data := make([]uint8, 2)
|
data := make([]uint8, 2)
|
||||||
err := legacy.ReadRegister(d.bus, uint8(d.Address), REG_TEMP, data)
|
err := d.bus.ReadRegister(uint8(d.Address), REG_TEMP, data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
return milliCelsius(data[0], data[1]), nil
|
return int32(data[0])*1000 + int32((data[1]>>6)*25)*10, nil
|
||||||
}
|
|
||||||
|
|
||||||
// milliCelsius converts the raw temperature bytes (msb and lsb) from the DS3231
|
|
||||||
// into a 32-bit signed integer in units of milli Celsius (1/1000 deg C).
|
|
||||||
//
|
|
||||||
// According to the DS3231 datasheet: "Temperature is represented as a 10-bit
|
|
||||||
// code with a resolution of 0.25 deg C and is accessible at location 11h and
|
|
||||||
// 12h. The temperature is encoded in two's complement format. The upper 8 bits,
|
|
||||||
// the integer portion, are at location 11h and the lower 2 bits, the fractional
|
|
||||||
// portion, are in the upper nibble at location 12h."
|
|
||||||
//
|
|
||||||
// In other words, the msb and lsb bytes should be treated as a signed 16-bit
|
|
||||||
// integer in units of (1/256 deg C). It is possible to convert this into a
|
|
||||||
// 16-bit signed integer in units of centi Celsius (1/100 deg C) with no loss of
|
|
||||||
// precision or dynamic range. But for backwards compatibility, let's instead
|
|
||||||
// convert this into a 32-bit signed integer in units of milli Celsius.
|
|
||||||
func milliCelsius(msb uint8, lsb uint8) int32 {
|
|
||||||
t256 := int16(uint16(msb)<<8 | uint16(lsb))
|
|
||||||
t1000 := int32(t256) / 64 * 250
|
|
||||||
return t1000
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// uint8ToBCD converts a byte to BCD for the DS3231
|
// uint8ToBCD converts a byte to BCD for the DS3231
|
||||||
|
|||||||
@@ -1,76 +0,0 @@
|
|||||||
package ds3231
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestPositiveMilliCelsius(t *testing.T) {
|
|
||||||
t1000 := milliCelsius(0, 0)
|
|
||||||
if t1000 != 0 {
|
|
||||||
t.Fatal(t1000)
|
|
||||||
}
|
|
||||||
|
|
||||||
t1000 = milliCelsius(0, 0b01000000)
|
|
||||||
if t1000 != 250 {
|
|
||||||
t.Fatal(t1000)
|
|
||||||
}
|
|
||||||
|
|
||||||
t1000 = milliCelsius(0, 0b10000000)
|
|
||||||
if t1000 != 500 {
|
|
||||||
t.Fatal(t1000)
|
|
||||||
}
|
|
||||||
|
|
||||||
t1000 = milliCelsius(0, 0b11000000)
|
|
||||||
if t1000 != 750 {
|
|
||||||
t.Fatal(t1000)
|
|
||||||
}
|
|
||||||
|
|
||||||
t1000 = milliCelsius(1, 0b00000000)
|
|
||||||
if t1000 != 1000 {
|
|
||||||
t.Fatal(t1000)
|
|
||||||
}
|
|
||||||
|
|
||||||
t1000 = milliCelsius(2, 0b00000000)
|
|
||||||
if t1000 != 2000 {
|
|
||||||
t.Fatal(t1000)
|
|
||||||
}
|
|
||||||
|
|
||||||
// highest temperature is 127.750C
|
|
||||||
t1000 = milliCelsius(0x7f, 0b11000000)
|
|
||||||
if t1000 != 127750 {
|
|
||||||
t.Fatal(t1000)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNegativeMilliCelsius(t *testing.T) {
|
|
||||||
t1000 := milliCelsius(0xff, 0b11000000)
|
|
||||||
if t1000 != -250 {
|
|
||||||
t.Fatal(t1000)
|
|
||||||
}
|
|
||||||
|
|
||||||
t1000 = milliCelsius(0xff, 0b10000000)
|
|
||||||
if t1000 != -500 {
|
|
||||||
t.Fatal(t1000)
|
|
||||||
}
|
|
||||||
|
|
||||||
t1000 = milliCelsius(0xff, 0b01000000)
|
|
||||||
if t1000 != -750 {
|
|
||||||
t.Fatal(t1000)
|
|
||||||
}
|
|
||||||
|
|
||||||
t1000 = milliCelsius(0xff, 0b00000000)
|
|
||||||
if t1000 != -1000 {
|
|
||||||
t.Fatal(t1000)
|
|
||||||
}
|
|
||||||
|
|
||||||
t1000 = milliCelsius(0xfe, 0b00000000)
|
|
||||||
if t1000 != -2000 {
|
|
||||||
t.Fatal(t1000)
|
|
||||||
}
|
|
||||||
|
|
||||||
// lowest temperature is -128.000C
|
|
||||||
t1000 = milliCelsius(0x80, 0b00000000)
|
|
||||||
if t1000 != -128000 {
|
|
||||||
t.Fatal(t1000)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package espat
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"tinygo.org/x/drivers/net"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (d *Device) ConnectToAccessPoint(ssid, pass string, timeout time.Duration) error {
|
||||||
|
if len(ssid) == 0 {
|
||||||
|
return net.ErrWiFiMissingSSID
|
||||||
|
}
|
||||||
|
|
||||||
|
d.SetWifiMode(WifiModeClient)
|
||||||
|
return d.ConnectToAP(ssid, pass, int(timeout.Seconds()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Device) Disconnect() error {
|
||||||
|
return d.DisconnectFromAP()
|
||||||
|
}
|
||||||
+34
-297
@@ -15,302 +15,41 @@
|
|||||||
//
|
//
|
||||||
// AT command set:
|
// AT command set:
|
||||||
// https://www.espressif.com/sites/default/files/documentation/4a-esp8266_at_instruction_set_en.pdf
|
// https://www.espressif.com/sites/default/files/documentation/4a-esp8266_at_instruction_set_en.pdf
|
||||||
//
|
|
||||||
// 02/2023 sfeldma@gmail.com Heavily modified to use netdev interface
|
|
||||||
|
|
||||||
package espat // import "tinygo.org/x/drivers/espat"
|
package espat // import "tinygo.org/x/drivers/espat"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"machine"
|
|
||||||
"net"
|
|
||||||
"net/netip"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"tinygo.org/x/drivers/netdev"
|
"tinygo.org/x/drivers"
|
||||||
"tinygo.org/x/drivers/netlink"
|
"tinygo.org/x/drivers/net"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Config struct {
|
// Device wraps UART connection to the ESP8266/ESP32.
|
||||||
// UART config
|
|
||||||
Uart *machine.UART
|
|
||||||
Tx machine.Pin
|
|
||||||
Rx machine.Pin
|
|
||||||
}
|
|
||||||
|
|
||||||
type socket struct {
|
|
||||||
inUse bool
|
|
||||||
protocol int
|
|
||||||
laddr netip.AddrPort
|
|
||||||
}
|
|
||||||
|
|
||||||
type Device struct {
|
type Device struct {
|
||||||
cfg *Config
|
bus drivers.UART
|
||||||
uart *machine.UART
|
|
||||||
// command responses that come back from the ESP8266/ESP32
|
// command responses that come back from the ESP8266/ESP32
|
||||||
response []byte
|
response []byte
|
||||||
|
|
||||||
// data received from a TCP/UDP connection forwarded by the ESP8266/ESP32
|
// data received from a TCP/UDP connection forwarded by the ESP8266/ESP32
|
||||||
data []byte
|
socketdata []byte
|
||||||
socket socket
|
|
||||||
mu sync.Mutex
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDevice(cfg *Config) *Device {
|
// ActiveDevice is the currently configured Device in use. There can only be one.
|
||||||
return &Device{
|
var ActiveDevice *Device
|
||||||
cfg: cfg,
|
|
||||||
response: make([]byte, 1500),
|
// New returns a new espat driver. Pass in a fully configured UART bus.
|
||||||
data: make([]byte, 0, 1500),
|
func New(b drivers.UART) *Device {
|
||||||
}
|
return &Device{bus: b, response: make([]byte, 512), socketdata: make([]byte, 0, 1024)}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Device) NetConnect(params *netlink.ConnectParams) error {
|
// Configure sets up the device for communication.
|
||||||
|
func (d Device) Configure() {
|
||||||
if len(params.Ssid) == 0 {
|
ActiveDevice = &d
|
||||||
return netlink.ErrMissingSSID
|
net.ActiveDevice = ActiveDevice
|
||||||
}
|
|
||||||
|
|
||||||
d.uart = d.cfg.Uart
|
|
||||||
d.uart.Configure(machine.UARTConfig{TX: d.cfg.Tx, RX: d.cfg.Rx})
|
|
||||||
|
|
||||||
// Connect to ESP8266/ESP32
|
|
||||||
fmt.Printf("Connecting to device...")
|
|
||||||
|
|
||||||
for i := 0; i < 5; i++ {
|
|
||||||
if d.Connected() {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
time.Sleep(1 * time.Second)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !d.Connected() {
|
|
||||||
fmt.Printf("FAILED\r\n")
|
|
||||||
return netlink.ErrConnectFailed
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Printf("CONNECTED\r\n")
|
|
||||||
|
|
||||||
// Connect to Wifi AP
|
|
||||||
fmt.Printf("Connecting to Wifi SSID '%s'...", params.Ssid)
|
|
||||||
|
|
||||||
d.SetWifiMode(WifiModeClient)
|
|
||||||
|
|
||||||
err := d.ConnectToAP(params.Ssid, params.Passphrase, 10 /* secs */)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("FAILED\r\n")
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Printf("CONNECTED\r\n")
|
|
||||||
|
|
||||||
ip, err := d.Addr()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
fmt.Printf("DHCP-assigned IP: %s\r\n", ip)
|
|
||||||
fmt.Printf("\r\n")
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *Device) NetDisconnect() {
|
|
||||||
d.DisconnectFromAP()
|
|
||||||
fmt.Printf("\r\nDisconnected from Wifi\r\n\r\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *Device) NetNotify(cb func(netlink.Event)) {
|
|
||||||
// Not supported
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *Device) GetHostByName(name string) (netip.Addr, error) {
|
|
||||||
ip, err := d.GetDNS(name)
|
|
||||||
if err != nil {
|
|
||||||
return netip.Addr{}, err
|
|
||||||
}
|
|
||||||
return netip.ParseAddr(ip)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *Device) GetHardwareAddr() (net.HardwareAddr, error) {
|
|
||||||
return net.HardwareAddr{}, netlink.ErrNotSupported
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *Device) Addr() (netip.Addr, error) {
|
|
||||||
resp, err := d.GetClientIP()
|
|
||||||
if err != nil {
|
|
||||||
return netip.Addr{}, err
|
|
||||||
}
|
|
||||||
prefix := "+CIPSTA:ip:"
|
|
||||||
for _, line := range strings.Split(resp, "\n") {
|
|
||||||
if ok := strings.HasPrefix(line, prefix); ok {
|
|
||||||
ip := line[len(prefix)+1 : len(line)-2]
|
|
||||||
return netip.ParseAddr(ip)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return netip.Addr{}, fmt.Errorf("Error getting IP address")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *Device) Socket(domain int, stype int, protocol int) (int, error) {
|
|
||||||
|
|
||||||
switch domain {
|
|
||||||
case netdev.AF_INET:
|
|
||||||
default:
|
|
||||||
return -1, netdev.ErrFamilyNotSupported
|
|
||||||
}
|
|
||||||
|
|
||||||
switch {
|
|
||||||
case protocol == netdev.IPPROTO_TCP && stype == netdev.SOCK_STREAM:
|
|
||||||
case protocol == netdev.IPPROTO_TLS && stype == netdev.SOCK_STREAM:
|
|
||||||
case protocol == netdev.IPPROTO_UDP && stype == netdev.SOCK_DGRAM:
|
|
||||||
default:
|
|
||||||
return -1, netdev.ErrProtocolNotSupported
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only supporting single connection mode, so only one socket at a time
|
|
||||||
if d.socket.inUse {
|
|
||||||
return -1, netdev.ErrNoMoreSockets
|
|
||||||
}
|
|
||||||
d.socket.inUse = true
|
|
||||||
d.socket.protocol = protocol
|
|
||||||
|
|
||||||
return 0, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *Device) Bind(sockfd int, ip netip.AddrPort) error {
|
|
||||||
d.socket.laddr = ip
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *Device) Connect(sockfd int, host string, ip netip.AddrPort) error {
|
|
||||||
var err error
|
|
||||||
var addr = ip.Addr().String()
|
|
||||||
var rport = strconv.Itoa(int(ip.Port()))
|
|
||||||
var lport = strconv.Itoa(int(d.socket.laddr.Port()))
|
|
||||||
|
|
||||||
switch d.socket.protocol {
|
|
||||||
case netdev.IPPROTO_TCP:
|
|
||||||
err = d.ConnectTCPSocket(addr, rport)
|
|
||||||
case netdev.IPPROTO_UDP:
|
|
||||||
err = d.ConnectUDPSocket(addr, rport, lport)
|
|
||||||
case netdev.IPPROTO_TLS:
|
|
||||||
err = d.ConnectSSLSocket(host, rport)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
if host == "" {
|
|
||||||
return fmt.Errorf("Connect to %s timed out", ip)
|
|
||||||
} else {
|
|
||||||
return fmt.Errorf("Connect to %s:%d timed out", host, ip.Port())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *Device) Listen(sockfd int, backlog int) error {
|
|
||||||
switch d.socket.protocol {
|
|
||||||
case netdev.IPPROTO_UDP:
|
|
||||||
default:
|
|
||||||
return netdev.ErrProtocolNotSupported
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *Device) Accept(sockfd int, ip netip.AddrPort) (int, error) {
|
|
||||||
return -1, netdev.ErrNotSupported
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *Device) sendChunk(sockfd int, buf []byte, deadline time.Time) (int, error) {
|
|
||||||
// Check if we've timed out
|
|
||||||
if !deadline.IsZero() {
|
|
||||||
if time.Now().After(deadline) {
|
|
||||||
return -1, netdev.ErrTimeout
|
|
||||||
}
|
|
||||||
}
|
|
||||||
err := d.StartSocketSend(len(buf))
|
|
||||||
if err != nil {
|
|
||||||
return -1, err
|
|
||||||
}
|
|
||||||
n, err := d.Write(buf)
|
|
||||||
if err != nil {
|
|
||||||
return -1, err
|
|
||||||
}
|
|
||||||
_, err = d.Response(1000)
|
|
||||||
if err != nil {
|
|
||||||
return -1, err
|
|
||||||
}
|
|
||||||
return n, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *Device) Send(sockfd int, buf []byte, flags int, deadline time.Time) (int, error) {
|
|
||||||
|
|
||||||
d.mu.Lock()
|
|
||||||
defer d.mu.Unlock()
|
|
||||||
|
|
||||||
// Break large bufs into chunks so we don't overrun the hw queue
|
|
||||||
|
|
||||||
chunkSize := 1436
|
|
||||||
for i := 0; i < len(buf); i += chunkSize {
|
|
||||||
end := i + chunkSize
|
|
||||||
if end > len(buf) {
|
|
||||||
end = len(buf)
|
|
||||||
}
|
|
||||||
_, err := d.sendChunk(sockfd, buf[i:end], deadline)
|
|
||||||
if err != nil {
|
|
||||||
return -1, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return len(buf), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *Device) Recv(sockfd int, buf []byte, flags int, deadline time.Time) (int, error) {
|
|
||||||
|
|
||||||
d.mu.Lock()
|
|
||||||
defer d.mu.Unlock()
|
|
||||||
|
|
||||||
var length = len(buf)
|
|
||||||
|
|
||||||
// Limit length read size to chunk large read requests
|
|
||||||
if length > 1436 {
|
|
||||||
length = 1436
|
|
||||||
}
|
|
||||||
|
|
||||||
for {
|
|
||||||
// Check if we've timed out
|
|
||||||
if !deadline.IsZero() {
|
|
||||||
if time.Now().After(deadline) {
|
|
||||||
return -1, netdev.ErrTimeout
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
n, err := d.ReadSocket(buf[:length])
|
|
||||||
if err != nil {
|
|
||||||
return -1, err
|
|
||||||
}
|
|
||||||
if n == 0 {
|
|
||||||
d.mu.Unlock()
|
|
||||||
time.Sleep(100 * time.Millisecond)
|
|
||||||
d.mu.Lock()
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
return n, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *Device) Close(sockfd int) error {
|
|
||||||
d.mu.Lock()
|
|
||||||
defer d.mu.Unlock()
|
|
||||||
|
|
||||||
d.socket.inUse = false
|
|
||||||
return d.DisconnectSocket()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *Device) SetSockOpt(sockfd int, level int, opt int, value interface{}) error {
|
|
||||||
return netdev.ErrNotSupported
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Connected checks if there is communication with the ESP8266/ESP32.
|
// Connected checks if there is communication with the ESP8266/ESP32.
|
||||||
@@ -318,7 +57,7 @@ func (d *Device) Connected() bool {
|
|||||||
d.Execute(Test)
|
d.Execute(Test)
|
||||||
|
|
||||||
// handle response here, should include "OK"
|
// handle response here, should include "OK"
|
||||||
_, err := d.Response(1000)
|
_, err := d.Response(100)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -327,12 +66,12 @@ func (d *Device) Connected() bool {
|
|||||||
|
|
||||||
// Write raw bytes to the UART.
|
// Write raw bytes to the UART.
|
||||||
func (d *Device) Write(b []byte) (n int, err error) {
|
func (d *Device) Write(b []byte) (n int, err error) {
|
||||||
return d.uart.Write(b)
|
return d.bus.Write(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read raw bytes from the UART.
|
// Read raw bytes from the UART.
|
||||||
func (d *Device) Read(b []byte) (n int, err error) {
|
func (d *Device) Read(b []byte) (n int, err error) {
|
||||||
return d.uart.Read(b)
|
return d.bus.Read(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
// how long in milliseconds to pause after sending AT commands
|
// how long in milliseconds to pause after sending AT commands
|
||||||
@@ -361,10 +100,9 @@ func (d Device) Set(cmd, params string) error {
|
|||||||
// Version returns the ESP8266/ESP32 firmware version info.
|
// Version returns the ESP8266/ESP32 firmware version info.
|
||||||
func (d Device) Version() []byte {
|
func (d Device) Version() []byte {
|
||||||
d.Execute(Version)
|
d.Execute(Version)
|
||||||
r, err := d.Response(2000)
|
r, err := d.Response(100)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
//return []byte("unknown")
|
return []byte("unknown")
|
||||||
return []byte(err.Error())
|
|
||||||
}
|
}
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
@@ -394,16 +132,16 @@ func (d *Device) ReadSocket(b []byte) (n int, err error) {
|
|||||||
d.Response(300)
|
d.Response(300)
|
||||||
|
|
||||||
count := len(b)
|
count := len(b)
|
||||||
if len(b) >= len(d.data) {
|
if len(b) >= len(d.socketdata) {
|
||||||
// copy it all, then clear socket data
|
// copy it all, then clear socket data
|
||||||
count = len(d.data)
|
count = len(d.socketdata)
|
||||||
copy(b, d.data[:count])
|
copy(b, d.socketdata[:count])
|
||||||
d.data = d.data[:0]
|
d.socketdata = d.socketdata[:0]
|
||||||
} else {
|
} else {
|
||||||
// copy all we can, then keep the remaining socket data around
|
// copy all we can, then keep the remaining socket data around
|
||||||
copy(b, d.data[:count])
|
copy(b, d.socketdata[:count])
|
||||||
copy(d.data, d.data[count:])
|
copy(d.socketdata, d.socketdata[count:])
|
||||||
d.data = d.data[:len(d.data)-count]
|
d.socketdata = d.socketdata[:len(d.socketdata)-count]
|
||||||
}
|
}
|
||||||
|
|
||||||
return count, nil
|
return count, nil
|
||||||
@@ -419,11 +157,11 @@ func (d *Device) Response(timeout int) ([]byte, error) {
|
|||||||
retries := timeout / pause
|
retries := timeout / pause
|
||||||
|
|
||||||
for {
|
for {
|
||||||
size = d.uart.Buffered()
|
size = d.bus.Buffered()
|
||||||
|
|
||||||
if size > 0 {
|
if size > 0 {
|
||||||
end += size
|
end += size
|
||||||
d.uart.Read(d.response[start:end])
|
d.bus.Read(d.response[start:end])
|
||||||
|
|
||||||
// if "+IPD" then read socket data
|
// if "+IPD" then read socket data
|
||||||
if strings.Contains(string(d.response[:end]), "+IPD") {
|
if strings.Contains(string(d.response[:end]), "+IPD") {
|
||||||
@@ -466,19 +204,18 @@ func (d *Device) parseIPD(end int) error {
|
|||||||
val := string(d.response[s+5 : e])
|
val := string(d.response[s+5 : e])
|
||||||
|
|
||||||
// TODO: verify count
|
// TODO: verify count
|
||||||
v, err := strconv.Atoi(val)
|
_, err := strconv.Atoi(val)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// not expected data here. what to do?
|
// not expected data here. what to do?
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// load up the socket data
|
// load up the socket data
|
||||||
//d.data = append(d.data, d.response[e+1:end]...)
|
d.socketdata = append(d.socketdata, d.response[e+1:end]...)
|
||||||
d.data = append(d.data, d.response[e+1:e+1+v]...)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsSocketDataAvailable returns of there is socket data available
|
// IsSocketDataAvailable returns of there is socket data available
|
||||||
func (d *Device) IsSocketDataAvailable() bool {
|
func (d *Device) IsSocketDataAvailable() bool {
|
||||||
return len(d.data) > 0 || d.uart.Buffered() > 0
|
return len(d.socketdata) > 0 || d.bus.Buffered() > 0
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -51,7 +51,7 @@ func (d *Device) ConnectTCPSocket(addr, port string) error {
|
|||||||
// ConnectUDPSocket creates a new UDP connection for the ESP8266/ESP32.
|
// ConnectUDPSocket creates a new UDP connection for the ESP8266/ESP32.
|
||||||
func (d *Device) ConnectUDPSocket(addr, sendport, listenport string) error {
|
func (d *Device) ConnectUDPSocket(addr, sendport, listenport string) error {
|
||||||
protocol := "UDP"
|
protocol := "UDP"
|
||||||
val := "\"" + protocol + "\",\"" + addr + "\"," + sendport + "," + listenport + ",0"
|
val := "\"" + protocol + "\",\"" + addr + "\"," + sendport + "," + listenport + ",2"
|
||||||
err := d.Set(TCPConnect, val)
|
err := d.Set(TCPConnect, val)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
+4
-1
@@ -44,7 +44,10 @@ func (d *Device) ConnectToAP(ssid, pwd string, ws int) error {
|
|||||||
d.Set(ConnectAP, val)
|
d.Set(ConnectAP, val)
|
||||||
|
|
||||||
_, err := d.Response(ws * 1000)
|
_, err := d.Response(ws * 1000)
|
||||||
return err
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// DisconnectFromAP disconnects the ESP8266/ESP32 from the current access point.
|
// DisconnectFromAP disconnects the ESP8266/ESP32 from the current access point.
|
||||||
|
|||||||
@@ -1,42 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"image/color"
|
|
||||||
"machine"
|
|
||||||
"tinygo.org/x/drivers"
|
|
||||||
"tinygo.org/x/drivers/adafruit4650"
|
|
||||||
"tinygo.org/x/tinyfont"
|
|
||||||
"tinygo.org/x/tinyfont/freemono"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
machine.I2C0.Configure(machine.I2CConfig{})
|
|
||||||
|
|
||||||
dev := adafruit4650.New(machine.I2C0)
|
|
||||||
|
|
||||||
err := dev.Configure()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
drawPlus(&dev)
|
|
||||||
drawHelloWorld(&dev)
|
|
||||||
|
|
||||||
err = dev.Display()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func drawPlus(d drivers.Displayer) {
|
|
||||||
for i := int16(0); i < 128; i++ {
|
|
||||||
d.SetPixel(i, 32, color.RGBA{R: 1})
|
|
||||||
}
|
|
||||||
for i := int16(0); i < 64; i++ {
|
|
||||||
d.SetPixel(64, i, color.RGBA{R: 1})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func drawHelloWorld(d drivers.Displayer) {
|
|
||||||
tinyfont.WriteLine(d, &freemono.Regular9pt7b, 0, 32, "Hello World!", color.RGBA{R: 0xff, G: 0xff, B: 0xff, A: 0xff})
|
|
||||||
}
|
|
||||||
@@ -1,66 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"machine"
|
|
||||||
"machine/usb/hid/mouse"
|
|
||||||
"math"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"tinygo.org/x/drivers/as560x"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
// Let's use the AS5600 to make the world's most useless mouse with just a single X-axis & no buttons (!)
|
|
||||||
machine.I2C0.Configure(machine.I2CConfig{
|
|
||||||
Frequency: machine.TWI_FREQ_400KHZ,
|
|
||||||
SDA: machine.GPIO4,
|
|
||||||
SCL: machine.GPIO5,
|
|
||||||
})
|
|
||||||
as5600 := as560x.NewAS5600(machine.I2C0)
|
|
||||||
as5600.Configure(as560x.Config{})
|
|
||||||
mouse := mouse.New()
|
|
||||||
|
|
||||||
lastAngle := -1
|
|
||||||
for {
|
|
||||||
time.Sleep(time.Millisecond * 10)
|
|
||||||
// Get the magnet status of the AS5600
|
|
||||||
magnetDetected, magnetStrength, err := as5600.MagnetStatus()
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// Get the raw angle from the AS5600
|
|
||||||
angle, _, err := as5600.RawAngle(as560x.ANGLE_NATIVE)
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
str := ""
|
|
||||||
if !magnetDetected {
|
|
||||||
str += "NOT "
|
|
||||||
}
|
|
||||||
str += "detected. Strength is "
|
|
||||||
switch magnetStrength {
|
|
||||||
case as560x.MagnetTooWeak:
|
|
||||||
str += "too weak"
|
|
||||||
case as560x.MagnetTooStrong:
|
|
||||||
str += "too strong"
|
|
||||||
default:
|
|
||||||
str += "ok"
|
|
||||||
}
|
|
||||||
println("Raw angle:", angle, "Magnet was", str)
|
|
||||||
if lastAngle != -1 {
|
|
||||||
diff := int(angle) - lastAngle
|
|
||||||
// correct the zero crossover glitch
|
|
||||||
if diff < -0xc00 {
|
|
||||||
diff += 0xfff
|
|
||||||
} else if diff > 0xc00 {
|
|
||||||
diff -= 0xfff
|
|
||||||
}
|
|
||||||
// debounce the noise (could use the sensor's filters/hysteresis instead?)
|
|
||||||
if math.Abs(float64(diff)) > 2 {
|
|
||||||
// move the mouse x-axis in response to the AS5600
|
|
||||||
mouse.Move(diff, 0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
lastAngle = int(angle)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
// Smoke test for the BMA421/BMA425 sensors.
|
|
||||||
// Warning: this code has _not been tested_. It's only here as a smoke test.
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"machine"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"tinygo.org/x/drivers"
|
|
||||||
"tinygo.org/x/drivers/bma42x"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
time.Sleep(5 * time.Second)
|
|
||||||
|
|
||||||
i2cBus := machine.I2C1
|
|
||||||
i2cBus.Configure(machine.I2CConfig{
|
|
||||||
Frequency: 400 * machine.KHz,
|
|
||||||
SDA: machine.SDA_PIN,
|
|
||||||
SCL: machine.SCL_PIN,
|
|
||||||
})
|
|
||||||
|
|
||||||
sensor := bma42x.NewI2C(i2cBus, bma42x.Address)
|
|
||||||
err := sensor.Configure(bma42x.Config{
|
|
||||||
Device: bma42x.DeviceBMA421 | bma42x.DeviceBMA425,
|
|
||||||
Features: bma42x.FeatureStepCounting,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
println("could not configure BMA421/BMA425:", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if !sensor.Connected() {
|
|
||||||
println("BMA42x not connected")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for {
|
|
||||||
time.Sleep(time.Second)
|
|
||||||
|
|
||||||
err := sensor.Update(drivers.Acceleration | drivers.Temperature)
|
|
||||||
if err != nil {
|
|
||||||
println("Error reading sensor", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Printf("Temperature: %.2f °C\n", float32(sensor.Temperature())/1000)
|
|
||||||
|
|
||||||
accelX, accelY, accelZ := sensor.Acceleration()
|
|
||||||
fmt.Printf("Acceleration: %.2fg %.2fg %.2fg\n", float32(accelX)/1e6, float32(accelY)/1e6, float32(accelZ)/1e6)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -27,9 +27,6 @@ func main() {
|
|||||||
pressure, _ := sensor.ReadPressure()
|
pressure, _ := sensor.ReadPressure()
|
||||||
println("Pressure", float32(pressure)/100000, "hPa")
|
println("Pressure", float32(pressure)/100000, "hPa")
|
||||||
|
|
||||||
altitude, _ := sensor.ReadAltitude()
|
|
||||||
println("Altitude", altitude, "meters")
|
|
||||||
|
|
||||||
time.Sleep(2 * time.Second)
|
time.Sleep(2 * time.Second)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"tinygo.org/x/drivers/delay"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
time.Sleep(time.Second) // wait for a serial console
|
|
||||||
start := time.Now()
|
|
||||||
for i := 0; i < 2000; i++ {
|
|
||||||
delay.Sleep(50 * time.Microsecond)
|
|
||||||
}
|
|
||||||
duration := time.Since(start)
|
|
||||||
println("sleep of 2000*50µs (100ms) took:", duration.String())
|
|
||||||
}
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/hex"
|
|
||||||
"machine"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"tinygo.org/x/drivers/onewire"
|
|
||||||
|
|
||||||
"tinygo.org/x/drivers/ds18b20"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
// Define pin for DS18B20
|
|
||||||
pin := machine.D2
|
|
||||||
|
|
||||||
ow := onewire.New(pin)
|
|
||||||
romIDs, err := ow.Search(onewire.SEARCH_ROM)
|
|
||||||
if err != nil {
|
|
||||||
println(err)
|
|
||||||
}
|
|
||||||
sensor := ds18b20.New(ow)
|
|
||||||
|
|
||||||
for {
|
|
||||||
time.Sleep(3 * time.Second)
|
|
||||||
|
|
||||||
println()
|
|
||||||
println("Device:", machine.Device)
|
|
||||||
|
|
||||||
println()
|
|
||||||
println("Request Temperature.")
|
|
||||||
for _, romid := range romIDs {
|
|
||||||
println("Sensor RomID: ", hex.EncodeToString(romid))
|
|
||||||
sensor.RequestTemperature(romid)
|
|
||||||
}
|
|
||||||
|
|
||||||
// wait 750ms or more for DS18B20 convert T
|
|
||||||
time.Sleep(1 * time.Second)
|
|
||||||
|
|
||||||
println()
|
|
||||||
println("Read Temperature")
|
|
||||||
for _, romid := range romIDs {
|
|
||||||
raw, err := sensor.ReadTemperatureRaw(romid)
|
|
||||||
if err != nil {
|
|
||||||
println(err)
|
|
||||||
}
|
|
||||||
println()
|
|
||||||
println("Sensor RomID: ", hex.EncodeToString(romid))
|
|
||||||
println("Temperature Raw value: ", hex.EncodeToString(raw))
|
|
||||||
|
|
||||||
t, err := sensor.ReadTemperature(romid)
|
|
||||||
if err != nil {
|
|
||||||
println(err)
|
|
||||||
}
|
|
||||||
println("Temperature in celsius milli degrees (°C/1000): ", t)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
// Connects to an DS3231 I2C Real Time Clock (RTC).
|
// Connects to an MAG3110 I2C magnetometer.
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
@@ -0,0 +1,145 @@
|
|||||||
|
// 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
|
||||||
|
|
||||||
|
var (
|
||||||
|
// access point info
|
||||||
|
ssid string
|
||||||
|
pass string
|
||||||
|
)
|
||||||
|
|
||||||
|
// these are the default pins for the Arduino Nano33 IoT.
|
||||||
|
// change these to connect to a different UART or pins for the ESP8266/ESP32
|
||||||
|
var (
|
||||||
|
uart = machine.UART1
|
||||||
|
tx = machine.PA22
|
||||||
|
rx = machine.PA23
|
||||||
|
|
||||||
|
console = machine.Serial
|
||||||
|
|
||||||
|
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 connectToESP() {
|
||||||
|
println("Connected to wifi adaptor.")
|
||||||
|
adaptor.Echo(false)
|
||||||
|
|
||||||
|
connectToAP()
|
||||||
|
} else {
|
||||||
|
println("")
|
||||||
|
failMessage("Unable to connect to wifi adaptor.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
println("Type an AT command then press enter:")
|
||||||
|
prompt()
|
||||||
|
|
||||||
|
input := make([]byte, 64)
|
||||||
|
i := 0
|
||||||
|
for {
|
||||||
|
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
|
||||||
|
r, _ := adaptor.Response(500)
|
||||||
|
console.Write(r)
|
||||||
|
|
||||||
|
// prompt
|
||||||
|
prompt()
|
||||||
|
|
||||||
|
i = 0
|
||||||
|
continue
|
||||||
|
default:
|
||||||
|
// just echo the character
|
||||||
|
console.WriteByte(data)
|
||||||
|
input[i] = data
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
time.Sleep(10 * time.Millisecond)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func prompt() {
|
||||||
|
print("ESPAT>")
|
||||||
|
}
|
||||||
|
|
||||||
|
// connect to ESP8266/ESP32
|
||||||
|
func connectToESP() bool {
|
||||||
|
for i := 0; i < 5; i++ {
|
||||||
|
println("Connecting to wifi adaptor...")
|
||||||
|
if adaptor.Connected() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// connect to access point
|
||||||
|
func connectToAP() {
|
||||||
|
println("Connecting to wifi network '" + ssid + "'")
|
||||||
|
|
||||||
|
if err := adaptor.ConnectToAccessPoint(ssid, pass, 10*time.Second); err != nil {
|
||||||
|
failMessage(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
println("Connected.")
|
||||||
|
ip, err := adaptor.GetClientIP()
|
||||||
|
if err != nil {
|
||||||
|
failMessage(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
println(ip)
|
||||||
|
}
|
||||||
|
|
||||||
|
// provide access point
|
||||||
|
func provideAP() {
|
||||||
|
println("Starting wifi network as access point '" + ssid + "'...")
|
||||||
|
adaptor.SetWifiMode(espat.WifiModeAP)
|
||||||
|
adaptor.SetAPConfig(ssid, pass, 7, espat.WifiAPSecurityWPA2_PSK)
|
||||||
|
println("Ready.")
|
||||||
|
ip, _ := adaptor.GetAPIP()
|
||||||
|
println(ip)
|
||||||
|
}
|
||||||
|
|
||||||
|
func failMessage(msg string) {
|
||||||
|
for {
|
||||||
|
println(msg)
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,131 @@
|
|||||||
|
// 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/net"
|
||||||
|
)
|
||||||
|
|
||||||
|
// change actAsAP to true to act as an access point instead of connecting to one.
|
||||||
|
const actAsAP = false
|
||||||
|
|
||||||
|
var (
|
||||||
|
// access point info
|
||||||
|
ssid string
|
||||||
|
pass string
|
||||||
|
)
|
||||||
|
|
||||||
|
// these are the default pins for the Arduino Nano33 IoT.
|
||||||
|
// change these to connect to a different UART or pins for the ESP8266/ESP32
|
||||||
|
var (
|
||||||
|
uart = machine.UART1
|
||||||
|
tx = machine.PA22
|
||||||
|
rx = machine.PA23
|
||||||
|
|
||||||
|
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 connectToESP() {
|
||||||
|
println("Connected to wifi adaptor.")
|
||||||
|
adaptor.Echo(false)
|
||||||
|
|
||||||
|
connectToAP()
|
||||||
|
} else {
|
||||||
|
println("")
|
||||||
|
failMessage("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 ESP8266/ESP32
|
||||||
|
func connectToESP() bool {
|
||||||
|
for i := 0; i < 5; i++ {
|
||||||
|
println("Connecting to wifi adaptor...")
|
||||||
|
if adaptor.Connected() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// connect to access point
|
||||||
|
func connectToAP() {
|
||||||
|
println("Connecting to wifi network '" + ssid + "'")
|
||||||
|
|
||||||
|
if err := adaptor.ConnectToAccessPoint(ssid, pass, 10*time.Second); err != nil {
|
||||||
|
failMessage(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
println("Connected.")
|
||||||
|
ip, err := adaptor.GetClientIP()
|
||||||
|
if err != nil {
|
||||||
|
failMessage(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
println(ip)
|
||||||
|
}
|
||||||
|
|
||||||
|
// provide access point
|
||||||
|
func provideAP() {
|
||||||
|
println("Starting wifi network as access point '" + ssid + "'...")
|
||||||
|
adaptor.SetWifiMode(espat.WifiModeAP)
|
||||||
|
adaptor.SetAPConfig(ssid, pass, 7, espat.WifiAPSecurityWPA2_PSK)
|
||||||
|
println("Ready.")
|
||||||
|
ip, _ := adaptor.GetAPIP()
|
||||||
|
println(ip)
|
||||||
|
}
|
||||||
|
|
||||||
|
func failMessage(msg string) {
|
||||||
|
for {
|
||||||
|
println(msg)
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,110 @@
|
|||||||
|
// 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/net"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// access point info
|
||||||
|
ssid string
|
||||||
|
pass string
|
||||||
|
)
|
||||||
|
|
||||||
|
// IP address of the listener aka "hub". Replace with your own info.
|
||||||
|
const hubIP = "0.0.0.0"
|
||||||
|
|
||||||
|
// these are the default pins for the Arduino Nano33 IoT.
|
||||||
|
// change these to connect to a different UART or pins for the ESP8266/ESP32
|
||||||
|
var (
|
||||||
|
uart = machine.UART1
|
||||||
|
tx = machine.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 connectToESP() {
|
||||||
|
println("Connected to wifi adaptor.")
|
||||||
|
adaptor.Echo(false)
|
||||||
|
|
||||||
|
connectToAP()
|
||||||
|
} else {
|
||||||
|
println("")
|
||||||
|
failMessage("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 ESP8266/ESP32
|
||||||
|
func connectToESP() bool {
|
||||||
|
for i := 0; i < 5; i++ {
|
||||||
|
println("Connecting to wifi adaptor...")
|
||||||
|
if adaptor.Connected() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// connect to access point
|
||||||
|
func connectToAP() {
|
||||||
|
println("Connecting to wifi network '" + ssid + "'")
|
||||||
|
|
||||||
|
if err := adaptor.ConnectToAccessPoint(ssid, pass, 10*time.Second); err != nil {
|
||||||
|
failMessage(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
println("Connected.")
|
||||||
|
ip, err := adaptor.GetClientIP()
|
||||||
|
if err != nil {
|
||||||
|
failMessage(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
println(ip)
|
||||||
|
}
|
||||||
|
|
||||||
|
func failMessage(msg string) {
|
||||||
|
for {
|
||||||
|
println(msg)
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,144 @@
|
|||||||
|
// 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/net/mqtt"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// access point info
|
||||||
|
ssid string
|
||||||
|
pass string
|
||||||
|
)
|
||||||
|
|
||||||
|
// IP address of the MQTT broker to use. Replace with your own info.
|
||||||
|
const server = "tcp://test.mosquitto.org:1883"
|
||||||
|
|
||||||
|
//const server = "ssl://test.mosquitto.org:8883"
|
||||||
|
|
||||||
|
// these are the default pins for the Arduino Nano33 IoT.
|
||||||
|
// change these to connect to a different UART or pins for the ESP8266/ESP32
|
||||||
|
var (
|
||||||
|
uart = machine.UART2
|
||||||
|
tx = machine.PA22
|
||||||
|
rx = machine.PA23
|
||||||
|
|
||||||
|
console = machine.Serial
|
||||||
|
|
||||||
|
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()
|
||||||
|
opts.AddBroker(server).SetClientID("tinygo-client-" + randomString(10))
|
||||||
|
|
||||||
|
println("Connecting to MQTT broker at", server)
|
||||||
|
cl := mqtt.NewClient(opts)
|
||||||
|
if token := cl.Connect(); token.Wait() && token.Error() != nil {
|
||||||
|
failMessage(token.Error().Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
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 '" + ssid + "'")
|
||||||
|
|
||||||
|
if err := adaptor.ConnectToAccessPoint(ssid, pass, 10*time.Second); err != nil {
|
||||||
|
failMessage(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
println("Connected.")
|
||||||
|
ip, err := adaptor.GetClientIP()
|
||||||
|
if err != nil {
|
||||||
|
failMessage(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
println(ip)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns an int >= min, < max
|
||||||
|
func randomInt(min, max int) int {
|
||||||
|
return min + rand.Intn(max-min)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate a random string of A-Z chars with len = l
|
||||||
|
func randomString(len int) string {
|
||||||
|
bytes := make([]byte, len)
|
||||||
|
for i := 0; i < len; i++ {
|
||||||
|
bytes[i] = byte(randomInt(65, 90))
|
||||||
|
}
|
||||||
|
return string(bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
func failMessage(msg string) {
|
||||||
|
for {
|
||||||
|
println(msg)
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,165 @@
|
|||||||
|
// This is a sensor station that uses a ESP8266 or ESP32 running on the device UART1.
|
||||||
|
// It creates an MQTT connection that publishes a message every second
|
||||||
|
// to an MQTT broker.
|
||||||
|
//
|
||||||
|
// In other words:
|
||||||
|
// Your computer <--> UART0 <--> MCU <--> UART1 <--> ESP8266 <--> Internet <--> MQTT broker.
|
||||||
|
//
|
||||||
|
// You must also install the Paho MQTT package to build this program:
|
||||||
|
//
|
||||||
|
// go get -u github.com/eclipse/paho.mqtt.golang
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"machine"
|
||||||
|
"math/rand"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"tinygo.org/x/drivers/espat"
|
||||||
|
"tinygo.org/x/drivers/net/mqtt"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// access point info
|
||||||
|
ssid string
|
||||||
|
pass string
|
||||||
|
)
|
||||||
|
|
||||||
|
// IP address of the MQTT broker to use. Replace with your own info.
|
||||||
|
//const server = "tcp://test.mosquitto.org:1883"
|
||||||
|
|
||||||
|
const server = "ssl://test.mosquitto.org:8883"
|
||||||
|
|
||||||
|
// change these to connect to a different UART or pins for the ESP8266/ESP32
|
||||||
|
var (
|
||||||
|
// these are defaults for the Arduino Nano33 IoT.
|
||||||
|
uart = machine.UART1
|
||||||
|
tx = machine.PA22
|
||||||
|
rx = machine.PA23
|
||||||
|
|
||||||
|
console = machine.Serial
|
||||||
|
|
||||||
|
adaptor *espat.Device
|
||||||
|
cl mqtt.Client
|
||||||
|
topicTx = "tinygo/tx"
|
||||||
|
topicRx = "tinygo/rx"
|
||||||
|
)
|
||||||
|
|
||||||
|
func subHandler(client mqtt.Client, msg mqtt.Message) {
|
||||||
|
fmt.Printf("[%s] ", msg.Topic())
|
||||||
|
fmt.Printf("%s\r\n", msg.Payload())
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
time.Sleep(3000 * time.Millisecond)
|
||||||
|
|
||||||
|
uart.Configure(machine.UARTConfig{TX: tx, RX: rx})
|
||||||
|
rand.Seed(time.Now().UnixNano())
|
||||||
|
|
||||||
|
// Init esp8266/esp32
|
||||||
|
adaptor = espat.New(uart)
|
||||||
|
adaptor.Configure()
|
||||||
|
|
||||||
|
// first check if connected
|
||||||
|
if connectToESP() {
|
||||||
|
println("Connected to wifi adaptor.")
|
||||||
|
adaptor.Echo(false)
|
||||||
|
|
||||||
|
connectToAP()
|
||||||
|
} else {
|
||||||
|
println("")
|
||||||
|
failMessage("Unable to connect to wifi adaptor.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
opts := mqtt.NewClientOptions()
|
||||||
|
opts.AddBroker(server).SetClientID("tinygo-client-" + randomString(10))
|
||||||
|
|
||||||
|
println("Connecting to MQTT broker at", server)
|
||||||
|
cl = mqtt.NewClient(opts)
|
||||||
|
if token := cl.Connect(); token.Wait() && token.Error() != nil {
|
||||||
|
failMessage(token.Error().Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// subscribe
|
||||||
|
token := cl.Subscribe(topicRx, 0, subHandler)
|
||||||
|
token.Wait()
|
||||||
|
if token.Error() != nil {
|
||||||
|
failMessage(token.Error().Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
go publishing()
|
||||||
|
|
||||||
|
select {}
|
||||||
|
|
||||||
|
// Right now this code is never reached. Need a way to trigger it...
|
||||||
|
println("Disconnecting MQTT...")
|
||||||
|
cl.Disconnect(100)
|
||||||
|
|
||||||
|
println("Done.")
|
||||||
|
}
|
||||||
|
|
||||||
|
func publishing() {
|
||||||
|
for {
|
||||||
|
println("Publishing MQTT message...")
|
||||||
|
data := []byte("{\"e\":[{ \"n\":\"hello\", \"v\":101 }]}")
|
||||||
|
token := cl.Publish(topicTx, 0, false, data)
|
||||||
|
token.Wait()
|
||||||
|
if token.Error() != nil {
|
||||||
|
println(token.Error().Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(1000 * time.Millisecond)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// connect to ESP8266/ESP32
|
||||||
|
func connectToESP() bool {
|
||||||
|
for i := 0; i < 5; i++ {
|
||||||
|
println("Connecting to wifi adaptor...")
|
||||||
|
if adaptor.Connected() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// connect to access point
|
||||||
|
func connectToAP() {
|
||||||
|
println("Connecting to wifi network '" + ssid + "'")
|
||||||
|
|
||||||
|
if err := adaptor.ConnectToAccessPoint(ssid, pass, 10*time.Second); err != nil {
|
||||||
|
failMessage(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
println("Connected.")
|
||||||
|
ip, err := adaptor.GetClientIP()
|
||||||
|
if err != nil {
|
||||||
|
failMessage(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
println(ip)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns an int >= min, < max
|
||||||
|
func randomInt(min, max int) int {
|
||||||
|
return min + rand.Intn(max-min)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate a random string of A-Z chars with len = l
|
||||||
|
func randomString(len int) string {
|
||||||
|
bytes := make([]byte, len)
|
||||||
|
for i := 0; i < len; i++ {
|
||||||
|
bytes[i] = byte(randomInt(65, 90))
|
||||||
|
}
|
||||||
|
return string(bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
func failMessage(msg string) {
|
||||||
|
for {
|
||||||
|
println(msg)
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,113 @@
|
|||||||
|
// 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/net"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// access point info
|
||||||
|
ssid string
|
||||||
|
pass string
|
||||||
|
)
|
||||||
|
|
||||||
|
// IP address of the server aka "hub". Replace with your own info.
|
||||||
|
const serverIP = "0.0.0.0"
|
||||||
|
|
||||||
|
// these are the default pins for the Arduino Nano33 IoT.
|
||||||
|
// change these to connect to a different UART or pins for the ESP8266/ESP32
|
||||||
|
var (
|
||||||
|
uart = machine.UART1
|
||||||
|
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 connectToESP() {
|
||||||
|
println("Connected to wifi adaptor.")
|
||||||
|
adaptor.Echo(false)
|
||||||
|
|
||||||
|
connectToAP()
|
||||||
|
} else {
|
||||||
|
println("")
|
||||||
|
failMessage("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, err := net.DialTCP("tcp", laddr, raddr)
|
||||||
|
if err != nil {
|
||||||
|
failMessage(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
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 ESP8266/ESP32
|
||||||
|
func connectToESP() bool {
|
||||||
|
for i := 0; i < 5; i++ {
|
||||||
|
println("Connecting to wifi adaptor...")
|
||||||
|
if adaptor.Connected() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// connect to access point
|
||||||
|
func connectToAP() {
|
||||||
|
println("Connecting to wifi network '" + ssid + "'")
|
||||||
|
|
||||||
|
if err := adaptor.ConnectToAccessPoint(ssid, pass, 10*time.Second); err != nil {
|
||||||
|
failMessage(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
println("Connected.")
|
||||||
|
ip, err := adaptor.GetClientIP()
|
||||||
|
if err != nil {
|
||||||
|
failMessage(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
println(ip)
|
||||||
|
}
|
||||||
|
|
||||||
|
func failMessage(msg string) {
|
||||||
|
for {
|
||||||
|
println(msg)
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
//go:build m5stack_core2
|
//go:build m5stack_core2
|
||||||
|
// +build m5stack_core2
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
//go:build m5stack_core2
|
//go:build m5stack_core2
|
||||||
|
// +build m5stack_core2
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
//go:build atsamd21
|
//go:build atsamd21
|
||||||
|
// +build atsamd21
|
||||||
|
|
||||||
package initdisplay
|
package initdisplay
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
//go:build feather_m0 || feather_m4 || feather_m4_can || feather_nrf52840 || feather_nrf52840_sense || feather_stm32f405 || feather_rp2040
|
//go:build feather_m0 || feather_m4 || feather_m4_can || feather_nrf52840 || feather_nrf52840_sense || feather_stm32f405 || feather_rp2040
|
||||||
|
// +build feather_m0 feather_m4 feather_m4_can feather_nrf52840 feather_nrf52840_sense feather_stm32f405 feather_rp2040
|
||||||
|
|
||||||
package initdisplay
|
package initdisplay
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
//go:build m5stack
|
//go:build m5stack
|
||||||
|
// +build m5stack
|
||||||
|
|
||||||
package initdisplay
|
package initdisplay
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
//go:build m5stack_core2
|
//go:build m5stack_core2
|
||||||
|
// +build m5stack_core2
|
||||||
|
|
||||||
package initdisplay
|
package initdisplay
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
//go:build pyportal
|
//go:build pyportal
|
||||||
|
// +build pyportal
|
||||||
|
|
||||||
package initdisplay
|
package initdisplay
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
//go:build wioterminal
|
//go:build wioterminal
|
||||||
|
// +build wioterminal
|
||||||
|
|
||||||
package initdisplay
|
package initdisplay
|
||||||
|
|
||||||
|
|||||||
@@ -8,16 +8,15 @@ import (
|
|||||||
"tinygo.org/x/drivers/examples/ili9341/initdisplay"
|
"tinygo.org/x/drivers/examples/ili9341/initdisplay"
|
||||||
"tinygo.org/x/drivers/examples/ili9341/pyportal_boing/graphics"
|
"tinygo.org/x/drivers/examples/ili9341/pyportal_boing/graphics"
|
||||||
"tinygo.org/x/drivers/ili9341"
|
"tinygo.org/x/drivers/ili9341"
|
||||||
"tinygo.org/x/drivers/pixel"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
BGCOLOR = pixel.RGB565BE(0x75AD)
|
BGCOLOR = 0xAD75
|
||||||
GRIDCOLOR = pixel.RGB565BE(0x15A8)
|
GRIDCOLOR = 0xA815
|
||||||
BGSHADOW = pixel.RGB565BE(0x8552)
|
BGSHADOW = 0x5285
|
||||||
GRIDSHADOW = pixel.RGB565BE(0x0C60)
|
GRIDSHADOW = 0x600C
|
||||||
RED = pixel.RGB565BE(0x00F8)
|
RED = 0xF800
|
||||||
WHITE = pixel.RGB565BE(0xFFFF)
|
WHITE = 0xFFFF
|
||||||
|
|
||||||
YBOTTOM = 123 // Ball Y coord at bottom
|
YBOTTOM = 123 // Ball Y coord at bottom
|
||||||
YBOUNCE = -3.5 // Upward velocity on ball bounce
|
YBOUNCE = -3.5 // Upward velocity on ball bounce
|
||||||
@@ -26,7 +25,7 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
frameBuffer = pixel.NewImage[pixel.RGB565BE](graphics.BALLWIDTH+8, graphics.BALLHEIGHT+8)
|
frameBuffer = [(graphics.BALLHEIGHT + 8) * (graphics.BALLWIDTH + 8) * 2]uint8{}
|
||||||
|
|
||||||
startTime int64
|
startTime int64
|
||||||
frame int64
|
frame int64
|
||||||
@@ -42,7 +41,7 @@ var (
|
|||||||
balloldy float32
|
balloldy float32
|
||||||
|
|
||||||
// Color table for ball rotation effect
|
// Color table for ball rotation effect
|
||||||
palette [16]pixel.RGB565BE
|
palette [16]uint16
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -109,7 +108,6 @@ func main() {
|
|||||||
|
|
||||||
width = maxx - minx + 1
|
width = maxx - minx + 1
|
||||||
height = maxy - miny + 1
|
height = maxy - miny + 1
|
||||||
buffer := frameBuffer.Rescale(int(width), int(height))
|
|
||||||
|
|
||||||
// Ball animation frame # is incremented opposite the ball's X velocity
|
// Ball animation frame # is incremented opposite the ball's X velocity
|
||||||
ballframe -= ballvx * 0.5
|
ballframe -= ballvx * 0.5
|
||||||
@@ -130,7 +128,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Only the changed rectangle is drawn into the 'renderbuf' array...
|
// Only the changed rectangle is drawn into the 'renderbuf' array...
|
||||||
var c pixel.RGB565BE //, *destPtr;
|
var c uint16 //, *destPtr;
|
||||||
bx := minx - int16(ballx) // X relative to ball bitmap (can be negative)
|
bx := minx - int16(ballx) // X relative to ball bitmap (can be negative)
|
||||||
by := miny - int16(bally) // Y relative to ball bitmap (can be negative)
|
by := miny - int16(bally) // Y relative to ball bitmap (can be negative)
|
||||||
bgx := minx // X relative to background bitmap (>= 0)
|
bgx := minx // X relative to background bitmap (>= 0)
|
||||||
@@ -151,20 +149,19 @@ func main() {
|
|||||||
(by >= 0) && (by < graphics.BALLHEIGHT) { // inside the ball bitmap area?
|
(by >= 0) && (by < graphics.BALLHEIGHT) { // inside the ball bitmap area?
|
||||||
// Yes, do ball compositing math...
|
// Yes, do ball compositing math...
|
||||||
p = graphics.Ball[int(by*(graphics.BALLWIDTH/2))+int(bx1/2)] // Get packed value (2 pixels)
|
p = graphics.Ball[int(by*(graphics.BALLWIDTH/2))+int(bx1/2)] // Get packed value (2 pixels)
|
||||||
var nibble uint8
|
|
||||||
if (bx1 & 1) != 0 {
|
if (bx1 & 1) != 0 {
|
||||||
nibble = p & 0xF
|
c = uint16(p & 0xF)
|
||||||
} else {
|
} else {
|
||||||
nibble = p >> 4
|
c = uint16(p >> 4)
|
||||||
} // Unpack high or low nybble
|
} // Unpack high or low nybble
|
||||||
if nibble == 0 { // Outside ball - just draw grid
|
if c == 0 { // Outside ball - just draw grid
|
||||||
if graphics.Background[bgidx]&(0x80>>(bgx1&7)) != 0 {
|
if graphics.Background[bgidx]&(0x80>>(bgx1&7)) != 0 {
|
||||||
c = GRIDCOLOR
|
c = GRIDCOLOR
|
||||||
} else {
|
} else {
|
||||||
c = BGCOLOR
|
c = BGCOLOR
|
||||||
}
|
}
|
||||||
} else if nibble > 1 { // In ball area...
|
} else if c > 1 { // In ball area...
|
||||||
c = palette[nibble]
|
c = palette[c]
|
||||||
} else { // In shadow area...
|
} else { // In shadow area...
|
||||||
if graphics.Background[bgidx]&(0x80>>(bgx1&7)) != 0 {
|
if graphics.Background[bgidx]&(0x80>>(bgx1&7)) != 0 {
|
||||||
c = GRIDSHADOW
|
c = GRIDSHADOW
|
||||||
@@ -179,7 +176,8 @@ func main() {
|
|||||||
c = BGCOLOR
|
c = BGCOLOR
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
buffer.Set(x, y, c)
|
frameBuffer[(y*int(width)+x)*2] = byte(c >> 8)
|
||||||
|
frameBuffer[(y*int(width)+x)*2+1] = byte(c)
|
||||||
bx1++ // Increment bitmap position counters (X axis)
|
bx1++ // Increment bitmap position counters (X axis)
|
||||||
bgx1++
|
bgx1++
|
||||||
}
|
}
|
||||||
@@ -190,7 +188,7 @@ func main() {
|
|||||||
bgy++
|
bgy++
|
||||||
}
|
}
|
||||||
|
|
||||||
display.DrawBitmap(minx, miny, buffer)
|
display.DrawRGBBitmap8(minx, miny, frameBuffer[:width*height*2], width, height)
|
||||||
|
|
||||||
// Show approximate frame rate
|
// Show approximate frame rate
|
||||||
frame++
|
frame++
|
||||||
@@ -207,7 +205,6 @@ func DrawBackground() {
|
|||||||
w, h := display.Size()
|
w, h := display.Size()
|
||||||
byteWidth := (w + 7) / 8 // Bitmap scanline pad = whole byte
|
byteWidth := (w + 7) / 8 // Bitmap scanline pad = whole byte
|
||||||
var b uint8
|
var b uint8
|
||||||
buffer := frameBuffer.Rescale(int(w), 1)
|
|
||||||
for j := int16(0); j < h; j++ {
|
for j := int16(0); j < h; j++ {
|
||||||
for k := int16(0); k < w; k++ {
|
for k := int16(0); k < w; k++ {
|
||||||
if k&7 > 0 {
|
if k&7 > 0 {
|
||||||
@@ -216,11 +213,13 @@ func DrawBackground() {
|
|||||||
b = graphics.Background[j*byteWidth+k/8]
|
b = graphics.Background[j*byteWidth+k/8]
|
||||||
}
|
}
|
||||||
if b&0x80 == 0 {
|
if b&0x80 == 0 {
|
||||||
buffer.Set(int(k), 0, BGCOLOR)
|
frameBuffer[2*k] = byte(BGCOLOR >> 8)
|
||||||
|
frameBuffer[2*k+1] = byte(BGCOLOR & 0xFF)
|
||||||
} else {
|
} else {
|
||||||
buffer.Set(int(k), 0, GRIDCOLOR)
|
frameBuffer[2*k] = byte(GRIDCOLOR >> 8)
|
||||||
|
frameBuffer[2*k+1] = byte(GRIDCOLOR & 0xFF)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
display.DrawBitmap(0, j, buffer)
|
display.DrawRGBBitmap8(0, j, frameBuffer[0:w*2], w, 1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,52 +0,0 @@
|
|||||||
# AT-CMD implementation of at-lora command set
|
|
||||||
|
|
||||||
This example implements the AT command set as used by Seeed in the LoRa-E5 series of boards, but in the form of a TinyGo program that provides a serial interface.
|
|
||||||
|
|
||||||
See https://files.seeedstudio.com/products/317990687/res/LoRa-E5%20AT%20Command%20Specification_V1.0%20.pdf for more information.
|
|
||||||
|
|
||||||
```
|
|
||||||
$ tinygo monitor
|
|
||||||
Connected to /dev/ttyACM0. Press Ctrl-C to exit.
|
|
||||||
+AT: OK
|
|
||||||
+VER: 0.0.1 (sx127x v18)
|
|
||||||
```
|
|
||||||
|
|
||||||
# Building
|
|
||||||
|
|
||||||
Run the following commands from the main `drivers` directory.
|
|
||||||
|
|
||||||
## Simulator
|
|
||||||
|
|
||||||
Builds/flashes atcmd console application with simulator instead of actual LoRa radio.
|
|
||||||
|
|
||||||
```
|
|
||||||
tinygo flash -target pico ./examples/lora/lorawan/atcmd/
|
|
||||||
```
|
|
||||||
|
|
||||||
## PyBadge with LoRa Featherwing for EU868 region
|
|
||||||
|
|
||||||
Builds/flashes atcmd console application on PyBadge using LoRa Featherwing (RFM95/SX1276).
|
|
||||||
|
|
||||||
```
|
|
||||||
tinygo flash -target pybadge -tags featherwing -ldflags="-X main.reg=EU868" ./examples/lora/lorawan/atcmd/
|
|
||||||
```
|
|
||||||
|
|
||||||
## LoRa-E5 for US915 region
|
|
||||||
|
|
||||||
Builds/flashes atcmd console application on Lora-E5 using onboard SX126x.
|
|
||||||
|
|
||||||
```
|
|
||||||
tinygo flash -target lorae5 -ldflags="-X main.reg=US915" ./examples/lora/lorawan/atcmd/
|
|
||||||
```
|
|
||||||
|
|
||||||
## Joining a Public Lorawan Network
|
|
||||||
|
|
||||||
```
|
|
||||||
AT+ID=DevEui,0101010101010101
|
|
||||||
AT+ID=AppEui,0123012301230213
|
|
||||||
AT+KEY=APPKEY,AEAEAEAEAEAEAEAAEAEAEAEAEAEAAEAE
|
|
||||||
AT+LW=NET,ON
|
|
||||||
AT+JOIN
|
|
||||||
```
|
|
||||||
|
|
||||||
AT+LW=NET,(ON|OFF) command changes Lora Sync Word to connect on public network(ON) or private networks(OFF)
|
|
||||||
@@ -1,496 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/hex"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"tinygo.org/x/drivers/examples/lora/lorawan/common"
|
|
||||||
"tinygo.org/x/drivers/lora/lorawan"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Use to test if connection to module is OK.
|
|
||||||
func quicktest() {
|
|
||||||
writeCommandOutput("AT", "OK")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check firmware version.
|
|
||||||
func version() {
|
|
||||||
writeCommandOutput("VER", common.CurrentVersion()+" ("+common.FirmwareVersion()+")")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use to check the ID of the LoRaWAN module, or change the ID.
|
|
||||||
func id(args string) error {
|
|
||||||
cmd := "ID"
|
|
||||||
|
|
||||||
// look for comma in args
|
|
||||||
param, val, hasComma := strings.Cut(args, ",")
|
|
||||||
if hasComma {
|
|
||||||
// set
|
|
||||||
data := strings.Trim(val, "\"'")
|
|
||||||
|
|
||||||
// convert data from hex formatted string
|
|
||||||
data = strings.ReplaceAll(data, " ", "")
|
|
||||||
hexdata, err := hex.DecodeString(data)
|
|
||||||
if err != nil {
|
|
||||||
writeCommandOutput(cmd, err.Error())
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
switch param {
|
|
||||||
case "DevAddr":
|
|
||||||
if err := session.SetDevAddr(hexdata); err != nil {
|
|
||||||
writeCommandOutput(cmd, err.Error())
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
writeCommandOutput(cmd, "DevAddr, "+session.GetDevAddr())
|
|
||||||
case "DevEui":
|
|
||||||
if err := otaa.SetDevEUI(hexdata); err != nil {
|
|
||||||
writeCommandOutput(cmd, err.Error())
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
writeCommandOutput(cmd, "DevEui, "+otaa.GetDevEUI())
|
|
||||||
case "AppEui":
|
|
||||||
if err := otaa.SetAppEUI(hexdata); err != nil {
|
|
||||||
writeCommandOutput(cmd, err.Error())
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
writeCommandOutput(cmd, "AppEui, "+otaa.GetAppEUI())
|
|
||||||
default:
|
|
||||||
return errInvalidCommand
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// get
|
|
||||||
switch param {
|
|
||||||
case "DevAddr":
|
|
||||||
writeCommandOutput(cmd, "DevAddr, "+session.GetDevAddr())
|
|
||||||
case "DevEui":
|
|
||||||
writeCommandOutput(cmd, "DevEui, "+otaa.GetDevEUI())
|
|
||||||
case "AppEui":
|
|
||||||
writeCommandOutput(cmd, "AppEui, "+otaa.GetAppEUI())
|
|
||||||
default:
|
|
||||||
writeCommandOutput(cmd, "DevAddr, "+session.GetDevAddr())
|
|
||||||
writeCommandOutput(cmd, "DevEui, "+otaa.GetDevEUI())
|
|
||||||
writeCommandOutput(cmd, "AppEui, "+otaa.GetAppEUI())
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use to reset the module. If module returns error, then reset function is invalid.
|
|
||||||
func reset() error {
|
|
||||||
radio.Reset()
|
|
||||||
|
|
||||||
writeCommandOutput("RESET", "OK")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use to send string format frame which is no need to be confirmed by the server.
|
|
||||||
func msg(data string) error {
|
|
||||||
cmd := "MSG"
|
|
||||||
writeCommandOutput(cmd, "Start")
|
|
||||||
|
|
||||||
if err := radio.Tx([]byte(data), defaultTimeout); err != nil {
|
|
||||||
writeCommandOutput(cmd, err.Error())
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
writeCommandOutput(cmd, "Done")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use to send string format frame which must be confirmed by the server
|
|
||||||
func cmsg(data string) error {
|
|
||||||
cmd := "CMSG"
|
|
||||||
writeCommandOutput(cmd, "Start")
|
|
||||||
|
|
||||||
if err := radio.Tx([]byte(data), defaultTimeout); err != nil {
|
|
||||||
writeCommandOutput(cmd, err.Error())
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: confirmation
|
|
||||||
|
|
||||||
writeCommandOutput(cmd, "Done")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use to send hex format frame which is no need to be confirmed by the server
|
|
||||||
func msghex(data string) error {
|
|
||||||
cmd := "MSGHEX"
|
|
||||||
writeCommandOutput(cmd, "Start")
|
|
||||||
|
|
||||||
writeCommandOutput(cmd, "Done")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use to send hex format frame which must be confirmed by the server.
|
|
||||||
func cmsghex(data string) error {
|
|
||||||
cmd := "CMSGHEX"
|
|
||||||
writeCommandOutput(cmd, "Start")
|
|
||||||
|
|
||||||
writeCommandOutput(cmd, "Done")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use to send string format LoRaWAN proprietary frames
|
|
||||||
func pmsg(data string) error {
|
|
||||||
cmd := "PMSG"
|
|
||||||
writeCommandOutput(cmd, "Start")
|
|
||||||
|
|
||||||
writeCommandOutput(cmd, "Done")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use to send hex format LoRaWAN proprietary frames.
|
|
||||||
func pmsghex(data string) error {
|
|
||||||
cmd := "PMSGHEX"
|
|
||||||
writeCommandOutput(cmd, "Start")
|
|
||||||
|
|
||||||
writeCommandOutput(cmd, "Done")
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set PORT number which will be used by MSG/CMSG/MSGHEX/CMSGHEX command to send
|
|
||||||
// message, port number should range from 1 to 255. User should refer to LoRaWAN
|
|
||||||
// specification to choose port.
|
|
||||||
func port(p string) error {
|
|
||||||
cmd := "PMSG"
|
|
||||||
writeCommandOutput(cmd, p)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set ADR function of LoRaWAN module
|
|
||||||
func adr(state string) error {
|
|
||||||
cmd := "ADR"
|
|
||||||
writeCommandOutput(cmd, state)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use LoRaWAN defined DRx to set datarate of LoRaWAN AT modem.
|
|
||||||
func dr(rate string) error {
|
|
||||||
cmd := "DR"
|
|
||||||
writeCommandOutput(cmd, rate)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Channel Configuration
|
|
||||||
func ch(channel string) error {
|
|
||||||
cmd := "CH"
|
|
||||||
writeCommandOutput(cmd, channel)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set and Check Power
|
|
||||||
func power(setting string) error {
|
|
||||||
cmd := "POWER"
|
|
||||||
writeCommandOutput(cmd, setting)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unconfirmed message repeats times.
|
|
||||||
func rept(setting string) error {
|
|
||||||
cmd := "REPT"
|
|
||||||
writeCommandOutput(cmd, setting)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Confirmed message retry times. Valid range 0~254,
|
|
||||||
// if retry times is less than 2, only one message will
|
|
||||||
// be sent. Random delay 3 - 10s between each retry
|
|
||||||
// (band duty cycle limitation has the priority)
|
|
||||||
func retry(setting string) error {
|
|
||||||
cmd := "RETRY"
|
|
||||||
writeCommandOutput(cmd, setting)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func rxwin2(setting string) error {
|
|
||||||
cmd := "RXWIN2"
|
|
||||||
writeCommandOutput(cmd, setting)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func rxwin1(setting string) error {
|
|
||||||
cmd := "RXWIN1"
|
|
||||||
writeCommandOutput(cmd, setting)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func key(setting string) error {
|
|
||||||
cmd := "KEY"
|
|
||||||
|
|
||||||
// look for comma in args
|
|
||||||
param, val, hasComma := strings.Cut(setting, ",")
|
|
||||||
if hasComma {
|
|
||||||
// set
|
|
||||||
data := strings.Trim(val, "\"'")
|
|
||||||
|
|
||||||
// convert data from hex formatted string
|
|
||||||
data = strings.ReplaceAll(data, " ", "")
|
|
||||||
hexdata, err := hex.DecodeString(data)
|
|
||||||
if err != nil {
|
|
||||||
writeCommandOutput(cmd, err.Error())
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
switch param {
|
|
||||||
case "APPKEY":
|
|
||||||
if err := otaa.SetAppKey(hexdata); err != nil {
|
|
||||||
writeCommandOutput(cmd, err.Error())
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
writeCommandOutput(cmd, "APPKEY, "+otaa.GetAppKey())
|
|
||||||
default:
|
|
||||||
return errInvalidCommand
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// cannot get keys
|
|
||||||
return errInvalidCommand
|
|
||||||
}
|
|
||||||
|
|
||||||
func fdefault(setting string) error {
|
|
||||||
cmd := "FDEFAULT"
|
|
||||||
writeCommandOutput(cmd, "OK")
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func mode(setting string) error {
|
|
||||||
cmd := "MODE"
|
|
||||||
writeCommandOutput(cmd, setting)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func join(setting string) error {
|
|
||||||
// TODO: check that DevEUI, AppEUI, and AppKey have values
|
|
||||||
|
|
||||||
cmd := "JOIN"
|
|
||||||
writeCommandOutput(cmd, "Starting")
|
|
||||||
if err := lorawan.Join(otaa, session); err != nil {
|
|
||||||
writeCommandOutput(cmd, err.Error())
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
writeCommandOutput(cmd, "Network joined")
|
|
||||||
writeCommandOutput(cmd, "DevEui, "+otaa.GetDevEUI())
|
|
||||||
writeCommandOutput(cmd, "AppEui, "+otaa.GetAppEUI())
|
|
||||||
writeCommandOutput(cmd, "DevAddr, "+session.GetDevAddr())
|
|
||||||
writeCommandOutput(cmd, "NetID, "+otaa.GetNetID())
|
|
||||||
writeCommandOutput(cmd, "NwkSKey, "+session.GetNwkSKey())
|
|
||||||
writeCommandOutput(cmd, "AppSKey, "+session.GetAppSKey())
|
|
||||||
writeCommandOutput(cmd, "Done")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func beacon(setting string) error {
|
|
||||||
cmd := "BEACON"
|
|
||||||
writeCommandOutput(cmd, "Starting")
|
|
||||||
|
|
||||||
writeCommandOutput(cmd, "Done")
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func class(setting string) error {
|
|
||||||
cmd := "CLASS"
|
|
||||||
writeCommandOutput(cmd, "Starting")
|
|
||||||
|
|
||||||
writeCommandOutput(cmd, "Done")
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func delay(setting string) error {
|
|
||||||
cmd := "DELAY"
|
|
||||||
writeCommandOutput(cmd, setting)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func lw(setting string) error {
|
|
||||||
cmd := "LW"
|
|
||||||
|
|
||||||
param, val, hasComma := strings.Cut(setting, ",")
|
|
||||||
if hasComma {
|
|
||||||
if param == "NET" {
|
|
||||||
if val == "ON" {
|
|
||||||
lorawan.SetPublicNetwork(true)
|
|
||||||
} else {
|
|
||||||
lorawan.SetPublicNetwork(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
writeCommandOutput(cmd, setting)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func wdt(setting string) error {
|
|
||||||
cmd := "WDT"
|
|
||||||
writeCommandOutput(cmd, "Not implemented")
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func lowpower(setting string) error {
|
|
||||||
cmd := "LOWPOWER"
|
|
||||||
writeCommandOutput(cmd, "Not implemented")
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func vdd(setting string) error {
|
|
||||||
cmd := "VDD"
|
|
||||||
writeCommandOutput(cmd, "Not implemented")
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func temp(setting string) error {
|
|
||||||
cmd := "TEMP"
|
|
||||||
writeCommandOutput(cmd, "Not implemented")
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func rtc(setting string) error {
|
|
||||||
cmd := "RTC"
|
|
||||||
writeCommandOutput(cmd, "Not implemented")
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func eeprom(setting string) error {
|
|
||||||
cmd := "EEPROM"
|
|
||||||
writeCommandOutput(cmd, "Not implemented")
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func uartcmd(setting string) error {
|
|
||||||
cmd := "UART"
|
|
||||||
writeCommandOutput(cmd, "Not implemented")
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func test(setting string) error {
|
|
||||||
cmd := "TEST"
|
|
||||||
writeCommandOutput(cmd, "Not implemented")
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func log(setting string) error {
|
|
||||||
cmd := "LOG"
|
|
||||||
writeCommandOutput(cmd, "Not implemented")
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func send(data string) error {
|
|
||||||
cmd := "SEND"
|
|
||||||
writeCommandOutput(cmd, "Start")
|
|
||||||
|
|
||||||
// remove leading/trailing quotes
|
|
||||||
data = strings.Trim(data, "\"'")
|
|
||||||
|
|
||||||
if err := radio.Tx([]byte(data), defaultTimeout); err != nil {
|
|
||||||
writeCommandOutput(cmd, err.Error())
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
writeCommandOutput(cmd, "Done")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func sendhex(data string) error {
|
|
||||||
cmd := "SENDHEX"
|
|
||||||
writeCommandOutput(cmd, "Start")
|
|
||||||
|
|
||||||
// remove leading/trailing quotes
|
|
||||||
data = strings.Trim(data, "\"'")
|
|
||||||
|
|
||||||
// convert data from hex formatted string
|
|
||||||
data = strings.ReplaceAll(data, " ", "")
|
|
||||||
payload, err := hex.DecodeString(data)
|
|
||||||
if err != nil {
|
|
||||||
writeCommandOutput(cmd, err.Error())
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := radio.Tx(payload, defaultTimeout); err != nil {
|
|
||||||
writeCommandOutput(cmd, err.Error())
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
writeCommandOutput(cmd, "Done")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func recv(setting string) error {
|
|
||||||
cmd := "RECV"
|
|
||||||
|
|
||||||
data, err := common.Lorarx()
|
|
||||||
if err != nil {
|
|
||||||
writeCommandOutput(cmd, "ERROR "+err.Error())
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
writeCommandOutput(cmd, string(data))
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func recvhex(setting string) error {
|
|
||||||
cmd := "RECVHEX"
|
|
||||||
|
|
||||||
data, err := common.Lorarx()
|
|
||||||
if err != nil {
|
|
||||||
writeCommandOutput(cmd, "ERROR "+err.Error())
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
writeCommandOutput(cmd, string(data))
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func crlf() {
|
|
||||||
uart.Write([]byte("\r\n"))
|
|
||||||
}
|
|
||||||
|
|
||||||
func writeCommandOutput(cmd, data string) {
|
|
||||||
uart.Write([]byte("+" + cmd + ": "))
|
|
||||||
uart.Write([]byte(data))
|
|
||||||
crlf()
|
|
||||||
}
|
|
||||||
@@ -1,90 +0,0 @@
|
|||||||
// AT command set console running on the device UART to communicate with
|
|
||||||
// an attached LoRa device.
|
|
||||||
//
|
|
||||||
// Computer <-> UART <-> MCU <-> SPI <-> SX126x/SX127x
|
|
||||||
//
|
|
||||||
// Connect using default baudrate for this hardware, 8-N-1 with your terminal program.
|
|
||||||
// For details on the AT command set, see:
|
|
||||||
// https://files.seeedstudio.com/products/317990687/res/LoRa-E5%20AT%20Command%20Specification_V1.0%20.pdf
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"machine"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"tinygo.org/x/drivers/examples/lora/lorawan/common"
|
|
||||||
"tinygo.org/x/drivers/lora"
|
|
||||||
"tinygo.org/x/drivers/lora/lorawan"
|
|
||||||
"tinygo.org/x/drivers/lora/lorawan/region"
|
|
||||||
)
|
|
||||||
|
|
||||||
// change these to test a different UART or pins if available
|
|
||||||
var (
|
|
||||||
uart = machine.Serial
|
|
||||||
tx = machine.UART_TX_PIN
|
|
||||||
rx = machine.UART_RX_PIN
|
|
||||||
input = make([]byte, 0, 64)
|
|
||||||
|
|
||||||
radio lora.Radio
|
|
||||||
session *lorawan.Session
|
|
||||||
otaa *lorawan.Otaa
|
|
||||||
|
|
||||||
defaultTimeout uint32 = 1000
|
|
||||||
)
|
|
||||||
|
|
||||||
var reg string
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
uart.Configure(machine.UARTConfig{TX: tx, RX: rx})
|
|
||||||
|
|
||||||
var err error
|
|
||||||
radio, err = common.SetupLora()
|
|
||||||
if err != nil {
|
|
||||||
fail(err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
session = &lorawan.Session{}
|
|
||||||
otaa = &lorawan.Otaa{}
|
|
||||||
lorawan.UseRadio(radio)
|
|
||||||
|
|
||||||
switch reg {
|
|
||||||
case "AU915":
|
|
||||||
lorawan.UseRegionSettings(region.AU915())
|
|
||||||
case "EU868":
|
|
||||||
lorawan.UseRegionSettings(region.EU868())
|
|
||||||
case "US915":
|
|
||||||
lorawan.UseRegionSettings(region.US915())
|
|
||||||
default:
|
|
||||||
lorawan.UseRegionSettings(region.EU868())
|
|
||||||
}
|
|
||||||
|
|
||||||
for {
|
|
||||||
if uart.Buffered() > 0 {
|
|
||||||
data, _ := uart.ReadByte()
|
|
||||||
|
|
||||||
switch data {
|
|
||||||
case 13:
|
|
||||||
// return key
|
|
||||||
if err := parse(input); err != nil {
|
|
||||||
uart.Write([]byte("ERROR: "))
|
|
||||||
uart.Write([]byte(err.Error()))
|
|
||||||
crlf()
|
|
||||||
}
|
|
||||||
input = input[:0]
|
|
||||||
default:
|
|
||||||
// just capture the character
|
|
||||||
input = append(input, data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
time.Sleep(10 * time.Millisecond)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func fail(msg string) {
|
|
||||||
for {
|
|
||||||
uart.Write([]byte(msg))
|
|
||||||
crlf()
|
|
||||||
|
|
||||||
time.Sleep(time.Minute)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,115 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
errInvalidCommand = errors.New("Invalid command")
|
|
||||||
)
|
|
||||||
|
|
||||||
func parse(data []byte) error {
|
|
||||||
switch {
|
|
||||||
case len(data) < 2, string(data[0:2]) != "AT":
|
|
||||||
return errInvalidCommand
|
|
||||||
case len(data) == 2:
|
|
||||||
// just the AT command by itself
|
|
||||||
quicktest()
|
|
||||||
case len(data) < 6 || data[2] != '+':
|
|
||||||
return errInvalidCommand
|
|
||||||
default:
|
|
||||||
// parse the rest of the command
|
|
||||||
cmd, args, _ := strings.Cut(string(data[3:]), "=")
|
|
||||||
return parseCommand(cmd, args)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseCommand(cmd, args string) error {
|
|
||||||
switch cmd {
|
|
||||||
case "VER":
|
|
||||||
version()
|
|
||||||
case "ID":
|
|
||||||
id(args)
|
|
||||||
case "RESET":
|
|
||||||
reset()
|
|
||||||
case "MSG":
|
|
||||||
msg(args)
|
|
||||||
case "CMSG":
|
|
||||||
cmsg(args)
|
|
||||||
case "MSGHEX":
|
|
||||||
msghex(args)
|
|
||||||
case "CMSGHEX":
|
|
||||||
cmsghex(args)
|
|
||||||
case "PMSG":
|
|
||||||
pmsg(args)
|
|
||||||
case "PMSGHEX":
|
|
||||||
pmsg(args)
|
|
||||||
case "PORT":
|
|
||||||
port(args)
|
|
||||||
case "ADR":
|
|
||||||
adr(args)
|
|
||||||
case "DR":
|
|
||||||
dr(args)
|
|
||||||
case "CH":
|
|
||||||
ch(args)
|
|
||||||
case "POWER":
|
|
||||||
power(args)
|
|
||||||
case "REPT":
|
|
||||||
rept(args)
|
|
||||||
case "RETRY":
|
|
||||||
retry(args)
|
|
||||||
case "RXWIN2":
|
|
||||||
rxwin2(args)
|
|
||||||
case "RXWIN1":
|
|
||||||
rxwin1(args)
|
|
||||||
case "KEY":
|
|
||||||
key(args)
|
|
||||||
case "FDEFAULT":
|
|
||||||
fdefault(args)
|
|
||||||
case "MODE":
|
|
||||||
mode(args)
|
|
||||||
case "JOIN":
|
|
||||||
join(args)
|
|
||||||
case "BEACON":
|
|
||||||
join(args)
|
|
||||||
case "CLASS":
|
|
||||||
class(args)
|
|
||||||
case "DELAY":
|
|
||||||
delay(args)
|
|
||||||
case "LW":
|
|
||||||
lw(args)
|
|
||||||
case "WDT":
|
|
||||||
wdt(args)
|
|
||||||
case "LOWPOWER":
|
|
||||||
lowpower(args)
|
|
||||||
case "VDD":
|
|
||||||
vdd(args)
|
|
||||||
case "TEMP":
|
|
||||||
temp(args)
|
|
||||||
case "RTC":
|
|
||||||
rtc(args)
|
|
||||||
case "EEPROM":
|
|
||||||
eeprom(args)
|
|
||||||
case "UART":
|
|
||||||
uartcmd(args)
|
|
||||||
case "TEST":
|
|
||||||
test(args)
|
|
||||||
case "LOG":
|
|
||||||
log(args)
|
|
||||||
case "RECV":
|
|
||||||
recv(args)
|
|
||||||
case "RECVHEX":
|
|
||||||
recvhex(args)
|
|
||||||
case "SEND":
|
|
||||||
send(args)
|
|
||||||
case "SENDHEX":
|
|
||||||
sendhex(args)
|
|
||||||
default:
|
|
||||||
return errInvalidCommand
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
# Simple Lorawan example
|
|
||||||
|
|
||||||
This demo code will connect Lorawan network and send sample uplink message
|
|
||||||
|
|
||||||
You may change your Lorawan keys (AppEUI, DevEUI, AppKEY) in key-default.go
|
|
||||||
|
|
||||||
|
|
||||||
```
|
|
||||||
$ tinygo monitor
|
|
||||||
Connected to /dev/ttyACM0. Press Ctrl-C to exit.
|
|
||||||
Lorawan Simple Demo
|
|
||||||
Start Lorawan Join sequence
|
|
||||||
loraConnect: Connected !
|
|
||||||
```
|
|
||||||
|
|
||||||
# Building
|
|
||||||
|
|
||||||
## Simulator
|
|
||||||
|
|
||||||
```
|
|
||||||
tinygo flash -target pico ./examples/lora/lorawan/basic-demo
|
|
||||||
```
|
|
||||||
|
|
||||||
## PyBadge with LoRa Featherwing for EU868 region
|
|
||||||
|
|
||||||
```
|
|
||||||
tinygo flash -target pybadge -tags featherwing -ldflags="-X main.reg=EU868" ./examples/lora/lorawan/basic-demo
|
|
||||||
```
|
|
||||||
|
|
||||||
## LoRa-E5 for US915 region
|
|
||||||
|
|
||||||
```
|
|
||||||
tinygo flash -target lorae5 -ldflags="-X main.reg=US915" ./examples/lora/lorawan/basic-demo
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## Enable debugging
|
|
||||||
|
|
||||||
You can also enable some debug logs with ldflags :
|
|
||||||
|
|
||||||
```
|
|
||||||
$ tinygo build -ldflags="-X 'main.debug=true'" -target=lorae5
|
|
||||||
```
|
|
||||||
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
//go:build !customkeys
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"tinygo.org/x/drivers/lora/lorawan"
|
|
||||||
)
|
|
||||||
|
|
||||||
// These are sample keys, so the example builds
|
|
||||||
// Either change here, or create a new go file and use customkeys build tag
|
|
||||||
func setLorawanKeys() {
|
|
||||||
otaa.SetAppEUI([]uint8{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})
|
|
||||||
otaa.SetDevEUI([]uint8{0xB3, 0xD5, 0x41, 0x00, 0x0A, 0xF1, 0xA4, 0x45})
|
|
||||||
otaa.SetAppKey([]uint8{0x12, 0x22, 0xA3, 0xFF, 0x0C, 0x7B, 0x76, 0x7B, 0x8F, 0xD3, 0x12, 0x4F, 0xCE, 0x7A, 0x32, 0x16})
|
|
||||||
lorawan.SetPublicNetwork(true)
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,120 +0,0 @@
|
|||||||
// Simple code for connecting to Lorawan network and uploading sample payload
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"strconv"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"tinygo.org/x/drivers/examples/lora/lorawan/common"
|
|
||||||
"tinygo.org/x/drivers/lora"
|
|
||||||
"tinygo.org/x/drivers/lora/lorawan"
|
|
||||||
"tinygo.org/x/drivers/lora/lorawan/region"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
reg string
|
|
||||||
debug string
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
LORAWAN_JOIN_TIMEOUT_SEC = 180
|
|
||||||
LORAWAN_RECONNECT_DELAY_SEC = 15
|
|
||||||
LORAWAN_UPLINK_DELAY_SEC = 60
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
radio lora.Radio
|
|
||||||
session *lorawan.Session
|
|
||||||
otaa *lorawan.Otaa
|
|
||||||
)
|
|
||||||
|
|
||||||
func loraConnect() error {
|
|
||||||
start := time.Now()
|
|
||||||
var err error
|
|
||||||
for time.Since(start) < LORAWAN_JOIN_TIMEOUT_SEC*time.Second {
|
|
||||||
println("Trying to join network")
|
|
||||||
err = lorawan.Join(otaa, session)
|
|
||||||
if err == nil {
|
|
||||||
println("Connected to network !")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
println("Join error:", err, "retrying in", LORAWAN_RECONNECT_DELAY_SEC, "sec")
|
|
||||||
time.Sleep(time.Second * LORAWAN_RECONNECT_DELAY_SEC)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = errors.New("Unable to join Lorawan network")
|
|
||||||
println(err.Error())
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func failMessage(err error) {
|
|
||||||
println("FATAL:", err)
|
|
||||||
for {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
println("*** Lorawan basic join and uplink demo ***")
|
|
||||||
|
|
||||||
// Board specific Lorawan initialization
|
|
||||||
var err error
|
|
||||||
radio, err = common.SetupLora()
|
|
||||||
if err != nil {
|
|
||||||
failMessage(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Required for LoraWan operations
|
|
||||||
session = &lorawan.Session{}
|
|
||||||
otaa = &lorawan.Otaa{}
|
|
||||||
|
|
||||||
// Connect the lorawan with the Lora Radio device.
|
|
||||||
lorawan.UseRadio(radio)
|
|
||||||
switch reg {
|
|
||||||
case "AU915":
|
|
||||||
lorawan.UseRegionSettings(region.AU915())
|
|
||||||
case "EU868":
|
|
||||||
lorawan.UseRegionSettings(region.EU868())
|
|
||||||
case "US915":
|
|
||||||
lorawan.UseRegionSettings(region.US915())
|
|
||||||
default:
|
|
||||||
lorawan.UseRegionSettings(region.EU868())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Configure AppEUI, DevEUI, APPKey, and public/private Lorawan Network
|
|
||||||
setLorawanKeys()
|
|
||||||
|
|
||||||
if debug != "" {
|
|
||||||
println("main: Network joined")
|
|
||||||
println("main: DevEui, " + otaa.GetDevEUI())
|
|
||||||
println("main: AppEui, " + otaa.GetAppEUI())
|
|
||||||
println("main: DevAddr, " + otaa.GetAppKey())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try to connect Lorawan network
|
|
||||||
if err := loraConnect(); err != nil {
|
|
||||||
failMessage(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if debug != "" {
|
|
||||||
println("main: NetID, " + otaa.GetNetID())
|
|
||||||
println("main: NwkSKey, " + session.GetNwkSKey())
|
|
||||||
println("main: AppSKey, " + session.GetAppSKey())
|
|
||||||
println("main: Done")
|
|
||||||
}
|
|
||||||
// Try to periodicaly send an uplink sample message
|
|
||||||
upCount := 1
|
|
||||||
for {
|
|
||||||
payload := "Hello TinyGo #" + strconv.Itoa(upCount)
|
|
||||||
|
|
||||||
if err := lorawan.SendUplink([]byte(payload), session); err != nil {
|
|
||||||
println("Uplink error:", err)
|
|
||||||
} else {
|
|
||||||
println("Uplink success, msg=", payload)
|
|
||||||
}
|
|
||||||
|
|
||||||
println("Sleeping for", LORAWAN_UPLINK_DELAY_SEC, "sec")
|
|
||||||
time.Sleep(time.Second * LORAWAN_UPLINK_DELAY_SEC)
|
|
||||||
upCount++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
//go:build featherwing
|
|
||||||
|
|
||||||
package common
|
|
||||||
|
|
||||||
import "machine"
|
|
||||||
|
|
||||||
var (
|
|
||||||
// We assume LoRa Featherwing module with sx127x is connected to PyBadge
|
|
||||||
rstPin = machine.D11
|
|
||||||
csPin = machine.D10
|
|
||||||
dio0Pin = machine.D6
|
|
||||||
dio1Pin = machine.D9
|
|
||||||
spi = machine.SPI0
|
|
||||||
)
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
//go:build lgt92
|
|
||||||
|
|
||||||
package common
|
|
||||||
|
|
||||||
import "machine"
|
|
||||||
|
|
||||||
var (
|
|
||||||
rstPin = machine.PB0
|
|
||||||
csPin = machine.PA15
|
|
||||||
dio0Pin = machine.PC13
|
|
||||||
dio1Pin = machine.PB10
|
|
||||||
spi = machine.SPI0
|
|
||||||
)
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
package common
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
errRadioNotFound = errors.New("radio not found")
|
|
||||||
errRxTimeout = errors.New("radio RX timeout")
|
|
||||||
)
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
//go:build !featherwing && !lgt92 && !stm32wlx && !sx126x
|
|
||||||
|
|
||||||
package common
|
|
||||||
|
|
||||||
import "tinygo.org/x/drivers/lora"
|
|
||||||
|
|
||||||
// do simulator setup here
|
|
||||||
func SetupLora() (lora.Radio, error) {
|
|
||||||
return &SimLoraRadio{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type SimLoraRadio struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sr *SimLoraRadio) Reset() {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sr *SimLoraRadio) Tx(pkt []uint8, timeoutMs uint32) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sr *SimLoraRadio) Rx(timeoutMs uint32) ([]uint8, error) {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sr *SimLoraRadio) SetFrequency(freq uint32) {}
|
|
||||||
func (sr *SimLoraRadio) SetIqMode(mode uint8) {}
|
|
||||||
func (sr *SimLoraRadio) SetCodingRate(cr uint8) {}
|
|
||||||
func (sr *SimLoraRadio) SetBandwidth(bw uint8) {}
|
|
||||||
func (sr *SimLoraRadio) SetCrc(enable bool) {}
|
|
||||||
func (sr *SimLoraRadio) SetSpreadingFactor(sf uint8) {}
|
|
||||||
func (sr *SimLoraRadio) SetHeaderType(headerType uint8) {}
|
|
||||||
func (sr *SimLoraRadio) SetPreambleLength(pLen uint16) {}
|
|
||||||
func (sr *SimLoraRadio) SetPublicNetwork(enabled bool) {}
|
|
||||||
func (sr *SimLoraRadio) SetSyncWord(syncWord uint16) {}
|
|
||||||
func (sr *SimLoraRadio) SetTxPower(txPower int8) {}
|
|
||||||
func (sr *SimLoraRadio) LoraConfig(cnf lora.Config) {}
|
|
||||||
|
|
||||||
func FirmwareVersion() string {
|
|
||||||
return "simulator " + CurrentVersion()
|
|
||||||
}
|
|
||||||
|
|
||||||
func Lorarx() ([]byte, error) {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
//go:build stm32wlx
|
|
||||||
|
|
||||||
package common
|
|
||||||
|
|
||||||
import (
|
|
||||||
"machine"
|
|
||||||
|
|
||||||
"tinygo.org/x/drivers/lora"
|
|
||||||
"tinygo.org/x/drivers/sx126x"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
FREQ = 868100000
|
|
||||||
LORA_DEFAULT_RXTIMEOUT_MS = 1000
|
|
||||||
LORA_DEFAULT_TXTIMEOUT_MS = 5000
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
loraRadio *sx126x.Device
|
|
||||||
)
|
|
||||||
|
|
||||||
var spi = machine.SPI3
|
|
||||||
|
|
||||||
func newRadioControl() sx126x.RadioController {
|
|
||||||
return sx126x.NewRadioControl()
|
|
||||||
}
|
|
||||||
|
|
||||||
// do sx126x setup here
|
|
||||||
func SetupLora() (lora.Radio, error) {
|
|
||||||
loraRadio = sx126x.New(spi)
|
|
||||||
loraRadio.SetDeviceType(sx126x.DEVICE_TYPE_SX1262)
|
|
||||||
|
|
||||||
// Create radio controller for target
|
|
||||||
loraRadio.SetRadioController(newRadioControl())
|
|
||||||
|
|
||||||
if state := loraRadio.DetectDevice(); !state {
|
|
||||||
return nil, errRadioNotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
return loraRadio, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func FirmwareVersion() string {
|
|
||||||
return "sx126x"
|
|
||||||
}
|
|
||||||
|
|
||||||
func Lorarx() ([]byte, error) {
|
|
||||||
return loraRadio.Rx(LORA_DEFAULT_RXTIMEOUT_MS)
|
|
||||||
}
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
//go:build !stm32wlx && sx126x
|
|
||||||
|
|
||||||
package common
|
|
||||||
|
|
||||||
import (
|
|
||||||
"machine"
|
|
||||||
|
|
||||||
"tinygo.org/x/drivers/lora"
|
|
||||||
"tinygo.org/x/drivers/sx126x"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
FREQ = 868100000
|
|
||||||
LORA_DEFAULT_RXTIMEOUT_MS = 1000
|
|
||||||
LORA_DEFAULT_TXTIMEOUT_MS = 5000
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
loraRadio *sx126x.Device
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
spi = machine.SPI0
|
|
||||||
nssPin, busyPin, dio1Pin = machine.GP17, machine.GP10, machine.GP11
|
|
||||||
rxPin, txLowPin, txHighPin = machine.GP13, machine.GP12, machine.GP12
|
|
||||||
)
|
|
||||||
|
|
||||||
func newRadioControl() sx126x.RadioController {
|
|
||||||
return sx126x.NewRadioControl(nssPin, busyPin, dio1Pin, rxPin, txLowPin, txHighPin)
|
|
||||||
}
|
|
||||||
|
|
||||||
// do sx126x setup here
|
|
||||||
func SetupLora() (lora.Radio, error) {
|
|
||||||
loraRadio = sx126x.New(spi)
|
|
||||||
loraRadio.SetDeviceType(sx126x.DEVICE_TYPE_SX1262)
|
|
||||||
|
|
||||||
// Create radio controller for target
|
|
||||||
loraRadio.SetRadioController(newRadioControl())
|
|
||||||
|
|
||||||
if state := loraRadio.DetectDevice(); !state {
|
|
||||||
return nil, errRadioNotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
return loraRadio, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func FirmwareVersion() string {
|
|
||||||
return "sx126x"
|
|
||||||
}
|
|
||||||
|
|
||||||
func Lorarx() ([]byte, error) {
|
|
||||||
return loraRadio.Rx(LORA_DEFAULT_RXTIMEOUT_MS)
|
|
||||||
}
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
//go:build featherwing || lgt92
|
|
||||||
|
|
||||||
package common
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"machine"
|
|
||||||
|
|
||||||
"tinygo.org/x/drivers/lora"
|
|
||||||
"tinygo.org/x/drivers/sx127x"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
FREQ = 868100000
|
|
||||||
LORA_DEFAULT_RXTIMEOUT_MS = 1000
|
|
||||||
LORA_DEFAULT_TXTIMEOUT_MS = 5000
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
loraRadio *sx127x.Device
|
|
||||||
)
|
|
||||||
|
|
||||||
// do sx127x setup here
|
|
||||||
func SetupLora() (lora.Radio, error) {
|
|
||||||
rstPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
|
||||||
spi.Configure(machine.SPIConfig{Frequency: 500000, Mode: 0})
|
|
||||||
|
|
||||||
loraRadio = sx127x.New(spi, rstPin)
|
|
||||||
loraRadio.SetRadioController(sx127x.NewRadioControl(csPin, dio0Pin, dio1Pin))
|
|
||||||
loraRadio.Reset()
|
|
||||||
|
|
||||||
if state := loraRadio.DetectDevice(); !state {
|
|
||||||
return nil, errRadioNotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
return loraRadio, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func FirmwareVersion() string {
|
|
||||||
v := loraRadio.GetVersion()
|
|
||||||
return "sx127x v" + strconv.Itoa(int(v))
|
|
||||||
}
|
|
||||||
|
|
||||||
func Lorarx() ([]byte, error) {
|
|
||||||
return loraRadio.Rx(LORA_DEFAULT_RXTIMEOUT_MS)
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
package common
|
|
||||||
|
|
||||||
const VERSION = "0.0.1"
|
|
||||||
|
|
||||||
func CurrentVersion() string {
|
|
||||||
return VERSION
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
// Connects to an MPU6886 I2C accelerometer/gyroscope.
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"machine"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"tinygo.org/x/drivers/mpu6886"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
machine.I2C0.Configure(machine.I2CConfig{})
|
|
||||||
|
|
||||||
accel := mpu6886.New(machine.I2C0)
|
|
||||||
accel.Configure(mpu6886.Config{})
|
|
||||||
|
|
||||||
for {
|
|
||||||
x, y, z, _ := accel.ReadAcceleration()
|
|
||||||
println(x, y, z)
|
|
||||||
time.Sleep(time.Millisecond * 100)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
// Connects to an MPU9150 I2C accelerometer/gyroscope.
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"machine"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"tinygo.org/x/drivers/mpu9150"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
machine.I2C0.Configure(machine.I2CConfig{})
|
|
||||||
|
|
||||||
accel := mpu9150.New(machine.I2C0)
|
|
||||||
accel.Configure()
|
|
||||||
|
|
||||||
for {
|
|
||||||
x, y, z := accel.ReadAcceleration(mpu9150.ACCEL_XOUT_H)
|
|
||||||
println(x, y, z)
|
|
||||||
time.Sleep(time.Millisecond * 100)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"machine"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"tinygo.org/x/drivers"
|
|
||||||
"tinygo.org/x/drivers/ndir"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
ndirBus = machine.I2C0
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
err := ndirBus.Configure(machine.I2CConfig{
|
|
||||||
Frequency: 100_000,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
panic("i2c config fail:" + err.Error())
|
|
||||||
}
|
|
||||||
// Set the address based on how the resistors are soldered.
|
|
||||||
// True means the left and middle pads are joined.
|
|
||||||
ndirAddr := ndir.Addr(true, false)
|
|
||||||
dev := ndir.NewDevI2C(ndirBus, ndirAddr)
|
|
||||||
err = dev.Init()
|
|
||||||
if err != nil {
|
|
||||||
panic("ndir init fail:" + err.Error())
|
|
||||||
}
|
|
||||||
// Datasheet tells us to wait 12 seconds before reading from the sensor.
|
|
||||||
time.Sleep(12 * time.Second)
|
|
||||||
for {
|
|
||||||
time.Sleep(time.Second)
|
|
||||||
err := dev.Update(drivers.AllMeasurements)
|
|
||||||
if err != nil {
|
|
||||||
println(err.Error())
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
println("PPM:", dev.PPMCO2())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,88 +0,0 @@
|
|||||||
// This example gets an URL using http.Get(). URL scheme can be http or https.
|
|
||||||
//
|
|
||||||
// Note: It may be necessary to increase the stack size when using "net/http".
|
|
||||||
// Use the -stack-size=4KB command line option.
|
|
||||||
//
|
|
||||||
// Some targets (Arduino Nano33 IoT) don't have enough SRAM to run http.Get().
|
|
||||||
// Use the following for those targets:
|
|
||||||
//
|
|
||||||
// examples/net/webclient (for HTTP)
|
|
||||||
// examples/net/tlsclient (for HTTPS)
|
|
||||||
|
|
||||||
//go:build pyportal || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 || wioterminal
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"log"
|
|
||||||
"machine"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"tinygo.org/x/drivers/netlink"
|
|
||||||
"tinygo.org/x/drivers/netlink/probe"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
ssid string
|
|
||||||
pass string
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
|
|
||||||
waitSerial()
|
|
||||||
|
|
||||||
link, _ := probe.Probe()
|
|
||||||
|
|
||||||
err := link.NetConnect(&netlink.ConnectParams{
|
|
||||||
Ssid: ssid,
|
|
||||||
Passphrase: pass,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
name := "John Doe"
|
|
||||||
occupation := "gardener"
|
|
||||||
|
|
||||||
params := "name=" + url.QueryEscape(name) + "&" +
|
|
||||||
"occupation=" + url.QueryEscape(occupation)
|
|
||||||
|
|
||||||
path := fmt.Sprintf("https://httpbin.org/get?%s", params)
|
|
||||||
|
|
||||||
cnt := 0
|
|
||||||
for {
|
|
||||||
fmt.Printf("Getting %s\r\n\r\n", path)
|
|
||||||
resp, err := http.Get(path)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("%s\r\n", err.Error())
|
|
||||||
time.Sleep(10 * time.Second)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Printf("%s %s\r\n", resp.Proto, resp.Status)
|
|
||||||
for k, v := range resp.Header {
|
|
||||||
fmt.Printf("%s: %s\r\n", k, strings.Join(v, " "))
|
|
||||||
}
|
|
||||||
fmt.Printf("\r\n")
|
|
||||||
|
|
||||||
body, err := io.ReadAll(resp.Body)
|
|
||||||
println(string(body))
|
|
||||||
resp.Body.Close()
|
|
||||||
|
|
||||||
cnt++
|
|
||||||
fmt.Printf("-------- %d --------\r\n", cnt)
|
|
||||||
time.Sleep(10 * time.Second)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait for user to open serial console
|
|
||||||
func waitSerial() {
|
|
||||||
for !machine.Serial.DTR() {
|
|
||||||
time.Sleep(100 * time.Millisecond)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
// This example gets an URL using http.Head(). URL scheme can be http or https.
|
|
||||||
//
|
|
||||||
// Note: It may be necessary to increase the stack size when using "net/http".
|
|
||||||
// Use the -stack-size=4KB command line option.
|
|
||||||
//
|
|
||||||
// Some targets (Arduino Nano33 IoT) don't have enough SRAM to run http.Head().
|
|
||||||
// Use the following for those targets:
|
|
||||||
//
|
|
||||||
// examples/net/webclient (for HTTP)
|
|
||||||
// examples/net/tlsclient (for HTTPS)
|
|
||||||
|
|
||||||
//go:build pyportal || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 || wioterminal
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"machine"
|
|
||||||
"net/http"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"tinygo.org/x/drivers/netlink"
|
|
||||||
"tinygo.org/x/drivers/netlink/probe"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
ssid string
|
|
||||||
pass string
|
|
||||||
url string = "https://httpbin.org"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
|
|
||||||
waitSerial()
|
|
||||||
|
|
||||||
link, _ := probe.Probe()
|
|
||||||
|
|
||||||
err := link.NetConnect(&netlink.ConnectParams{
|
|
||||||
Ssid: ssid,
|
|
||||||
Passphrase: pass,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := http.Head(url)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
var buf bytes.Buffer
|
|
||||||
if err := resp.Write(&buf); err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
fmt.Println(string(buf.Bytes()))
|
|
||||||
|
|
||||||
link.NetDisconnect()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait for user to open serial console
|
|
||||||
func waitSerial() {
|
|
||||||
for !machine.Serial.DTR() {
|
|
||||||
time.Sleep(100 * time.Millisecond)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,72 +0,0 @@
|
|||||||
// This example posts an URL using http.Post(). URL scheme can be http or https.
|
|
||||||
//
|
|
||||||
// Note: It may be necessary to increase the stack size when using "net/http".
|
|
||||||
// Use the -stack-size=4KB command line option.
|
|
||||||
//
|
|
||||||
// Some targets (Arduino Nano33 IoT) don't have enough SRAM to run http.Post().
|
|
||||||
// Use the following for those targets:
|
|
||||||
//
|
|
||||||
// examples/net/webclient (for HTTP)
|
|
||||||
// examples/net/tlsclient (for HTTPS)
|
|
||||||
|
|
||||||
//go:build pyportal || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 || wioterminal
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"log"
|
|
||||||
"machine"
|
|
||||||
"net/http"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"tinygo.org/x/drivers/netlink"
|
|
||||||
"tinygo.org/x/drivers/netlink/probe"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
ssid string
|
|
||||||
pass string
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
|
|
||||||
waitSerial()
|
|
||||||
|
|
||||||
link, _ := probe.Probe()
|
|
||||||
|
|
||||||
err := link.NetConnect(&netlink.ConnectParams{
|
|
||||||
Ssid: ssid,
|
|
||||||
Passphrase: pass,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
path := "https://httpbin.org/post"
|
|
||||||
data := []byte("{\"name\":\"John Doe\",\"occupation\":\"gardener\"}")
|
|
||||||
|
|
||||||
resp, err := http.Post(path, "application/json", bytes.NewBuffer(data))
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
body, err := io.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println(string(body))
|
|
||||||
|
|
||||||
link.NetDisconnect()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait for user to open serial console
|
|
||||||
func waitSerial() {
|
|
||||||
for !machine.Serial.DTR() {
|
|
||||||
time.Sleep(100 * time.Millisecond)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,73 +0,0 @@
|
|||||||
// This example posts an URL using http.PostForm(). URL scheme can be http or https.
|
|
||||||
//
|
|
||||||
// Note: It may be necessary to increase the stack size when using "net/http".
|
|
||||||
// Use the -stack-size=4KB command line option.
|
|
||||||
//
|
|
||||||
// Some targets (Arduino Nano33 IoT) don't have enough SRAM to run
|
|
||||||
// http.PostForm(). Use the following for those targets:
|
|
||||||
//
|
|
||||||
// examples/net/webclient (for HTTP)
|
|
||||||
// examples/net/tlsclient (for HTTPS)
|
|
||||||
|
|
||||||
//go:build pyportal || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 || wioterminal
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"log"
|
|
||||||
"machine"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"tinygo.org/x/drivers/netlink"
|
|
||||||
"tinygo.org/x/drivers/netlink/probe"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
ssid string
|
|
||||||
pass string
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
|
|
||||||
waitSerial()
|
|
||||||
|
|
||||||
link, _ := probe.Probe()
|
|
||||||
|
|
||||||
err := link.NetConnect(&netlink.ConnectParams{
|
|
||||||
Ssid: ssid,
|
|
||||||
Passphrase: pass,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
path := "https://httpbin.org/post"
|
|
||||||
data := url.Values{
|
|
||||||
"name": {"John Doe"},
|
|
||||||
"occupation": {"gardener"},
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := http.PostForm(path, data)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
body, err := io.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println(string(body))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait for user to open serial console
|
|
||||||
func waitSerial() {
|
|
||||||
for !machine.Serial.DTR() {
|
|
||||||
time.Sleep(100 * time.Millisecond)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,146 +0,0 @@
|
|||||||
// This example is an MQTT client built with the natiu-mqtt package. It sends
|
|
||||||
// machine.CPUFrequency() readings to the broker every second for 10 seconds.
|
|
||||||
//
|
|
||||||
// Note: It may be necessary to increase the stack size when using
|
|
||||||
// paho.mqtt.golang. Use the -stack-size=4KB command line option.
|
|
||||||
|
|
||||||
//go:build pyportal || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 || wioterminal || challenger_rp2040
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"log"
|
|
||||||
"machine"
|
|
||||||
"math/rand"
|
|
||||||
"net"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
mqtt "github.com/soypat/natiu-mqtt"
|
|
||||||
"tinygo.org/x/drivers/netlink"
|
|
||||||
"tinygo.org/x/drivers/netlink/probe"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
ssid string
|
|
||||||
pass string
|
|
||||||
broker string = "test.mosquitto.org:1883"
|
|
||||||
topic string = "cpu/freq"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
waitSerial()
|
|
||||||
|
|
||||||
link, _ := probe.Probe()
|
|
||||||
|
|
||||||
err := link.NetConnect(&netlink.ConnectParams{
|
|
||||||
Ssid: ssid,
|
|
||||||
Passphrase: pass,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
clientId := "tinygo-client-" + randomString(10)
|
|
||||||
fmt.Printf("ClientId: %s\n", clientId)
|
|
||||||
|
|
||||||
// Get a transport for MQTT packets
|
|
||||||
fmt.Printf("Connecting to MQTT broker at %s\n", broker)
|
|
||||||
conn, err := net.Dial("tcp", broker)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
defer conn.Close()
|
|
||||||
|
|
||||||
// Create new client
|
|
||||||
client := mqtt.NewClient(mqtt.ClientConfig{
|
|
||||||
Decoder: mqtt.DecoderNoAlloc{make([]byte, 1500)},
|
|
||||||
OnPub: func(_ mqtt.Header, _ mqtt.VariablesPublish, r io.Reader) error {
|
|
||||||
message, _ := io.ReadAll(r)
|
|
||||||
fmt.Printf("Message %s received on topic %s\n", string(message), topic)
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
// Connect client
|
|
||||||
var varconn mqtt.VariablesConnect
|
|
||||||
varconn.SetDefaultMQTT([]byte(clientId))
|
|
||||||
ctx, _ := context.WithTimeout(context.Background(), 10*time.Second)
|
|
||||||
err = client.Connect(ctx, conn, &varconn)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("failed to connect: ", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Subscribe to topic
|
|
||||||
ctx, _ = context.WithTimeout(context.Background(), 10*time.Second)
|
|
||||||
err = client.Subscribe(ctx, mqtt.VariablesSubscribe{
|
|
||||||
PacketIdentifier: 23,
|
|
||||||
TopicFilters: []mqtt.SubscribeRequest{
|
|
||||||
{TopicFilter: []byte(topic), QoS: mqtt.QoS0},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("failed to subscribe to", topic, err)
|
|
||||||
}
|
|
||||||
fmt.Printf("Subscribed to topic %s\n", topic)
|
|
||||||
|
|
||||||
// Publish on topic
|
|
||||||
pubFlags, _ := mqtt.NewPublishFlags(mqtt.QoS0, false, false)
|
|
||||||
pubVar := mqtt.VariablesPublish{
|
|
||||||
TopicName: []byte(topic),
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 0; i < 10; i++ {
|
|
||||||
if !client.IsConnected() {
|
|
||||||
log.Fatal("client disconnected: ", client.Err())
|
|
||||||
}
|
|
||||||
|
|
||||||
freq := float32(machine.CPUFrequency()) / 1000000
|
|
||||||
payload := fmt.Sprintf("%.02fMhz", freq)
|
|
||||||
|
|
||||||
pubVar.PacketIdentifier++
|
|
||||||
err = client.PublishPayload(pubFlags, pubVar, []byte(payload))
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("error transmitting message: ", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
time.Sleep(time.Second)
|
|
||||||
|
|
||||||
conn.SetReadDeadline(time.Now().Add(10 * time.Second))
|
|
||||||
err = client.HandleNext()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("handle next: ", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
client.Disconnect(errors.New("disconnected gracefully"))
|
|
||||||
|
|
||||||
for {
|
|
||||||
select {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait for user to open serial console
|
|
||||||
func waitSerial() {
|
|
||||||
for !machine.Serial.DTR() {
|
|
||||||
time.Sleep(100 * time.Millisecond)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,114 +0,0 @@
|
|||||||
// This example is an MQTT client built with the paho-mqtt package. It sends
|
|
||||||
// machine.CPUFrequency() readings to the broker every second for 10 seconds.
|
|
||||||
//
|
|
||||||
// Note: It may be necessary to increase the stack size when using
|
|
||||||
// paho.mqtt.golang. Use the -stack-size=4KB command line option.
|
|
||||||
|
|
||||||
//go:build pyportal || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 || wioterminal || challenger_rp2040
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"machine"
|
|
||||||
"math/rand"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
mqtt "github.com/eclipse/paho.mqtt.golang"
|
|
||||||
"tinygo.org/x/drivers/netlink"
|
|
||||||
"tinygo.org/x/drivers/netlink/probe"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
ssid string
|
|
||||||
pass string
|
|
||||||
broker string = "tcp://test.mosquitto.org:1883"
|
|
||||||
)
|
|
||||||
|
|
||||||
var messagePubHandler mqtt.MessageHandler = func(client mqtt.Client, msg mqtt.Message) {
|
|
||||||
fmt.Printf("Message %s received on topic %s\n", msg.Payload(), msg.Topic())
|
|
||||||
}
|
|
||||||
|
|
||||||
var connectHandler mqtt.OnConnectHandler = func(client mqtt.Client) {
|
|
||||||
fmt.Println("Connected")
|
|
||||||
}
|
|
||||||
|
|
||||||
var connectionLostHandler mqtt.ConnectionLostHandler = func(client mqtt.Client, err error) {
|
|
||||||
fmt.Printf("Connection Lost: %s\n", err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
waitSerial()
|
|
||||||
|
|
||||||
link, _ := probe.Probe()
|
|
||||||
|
|
||||||
err := link.NetConnect(&netlink.ConnectParams{
|
|
||||||
Ssid: ssid,
|
|
||||||
Passphrase: pass,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
clientId := "tinygo-client-" + randomString(10)
|
|
||||||
fmt.Printf("ClientId: %s\n", clientId)
|
|
||||||
|
|
||||||
options := mqtt.NewClientOptions()
|
|
||||||
options.AddBroker(broker)
|
|
||||||
options.SetClientID(clientId)
|
|
||||||
options.SetDefaultPublishHandler(messagePubHandler)
|
|
||||||
options.OnConnect = connectHandler
|
|
||||||
options.OnConnectionLost = connectionLostHandler
|
|
||||||
|
|
||||||
fmt.Printf("Connecting to MQTT broker at %s\n", broker)
|
|
||||||
client := mqtt.NewClient(options)
|
|
||||||
token := client.Connect()
|
|
||||||
if token.Wait() && token.Error() != nil {
|
|
||||||
panic(token.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
topic := "cpu/freq"
|
|
||||||
token = client.Subscribe(topic, 1, nil)
|
|
||||||
if token.Wait() && token.Error() != nil {
|
|
||||||
panic(token.Error())
|
|
||||||
}
|
|
||||||
fmt.Printf("Subscribed to topic %s\n", topic)
|
|
||||||
|
|
||||||
for i := 0; i < 10; i++ {
|
|
||||||
freq := float32(machine.CPUFrequency()) / 1000000
|
|
||||||
payload := fmt.Sprintf("%.02fMhz", freq)
|
|
||||||
token = client.Publish(topic, 0, false, payload)
|
|
||||||
if token.Wait() && token.Error() != nil {
|
|
||||||
panic(token.Error())
|
|
||||||
}
|
|
||||||
time.Sleep(time.Second)
|
|
||||||
}
|
|
||||||
|
|
||||||
client.Disconnect(100)
|
|
||||||
|
|
||||||
for {
|
|
||||||
select {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait for user to open serial console
|
|
||||||
func waitSerial() {
|
|
||||||
for !machine.Serial.DTR() {
|
|
||||||
time.Sleep(100 * time.Millisecond)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,115 +0,0 @@
|
|||||||
// This is an example of an NTP client.
|
|
||||||
//
|
|
||||||
// It creates a UDP connection to request the current time and parse the
|
|
||||||
// response from a NTP server. The system time is set to NTP time.
|
|
||||||
|
|
||||||
//go:build pyportal || arduino_nano33 || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 || wioterminal || challenger_rp2040
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"log"
|
|
||||||
"machine"
|
|
||||||
"net"
|
|
||||||
"runtime"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"tinygo.org/x/drivers/netlink"
|
|
||||||
"tinygo.org/x/drivers/netlink/probe"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
ssid string
|
|
||||||
pass string
|
|
||||||
// IP address of the server aka "hub". Replace with your own info.
|
|
||||||
ntpHost string = "0.pool.ntp.org:123"
|
|
||||||
)
|
|
||||||
|
|
||||||
const NTP_PACKET_SIZE = 48
|
|
||||||
|
|
||||||
var response = make([]byte, NTP_PACKET_SIZE)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
|
|
||||||
waitSerial()
|
|
||||||
|
|
||||||
link, _ := probe.Probe()
|
|
||||||
|
|
||||||
err := link.NetConnect(&netlink.ConnectParams{
|
|
||||||
Ssid: ssid,
|
|
||||||
Passphrase: pass,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
conn, err := net.Dial("udp", ntpHost)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
println("Requesting NTP time...")
|
|
||||||
|
|
||||||
t, err := getCurrentTime(conn)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(fmt.Sprintf("Error getting current time: %v", err))
|
|
||||||
} else {
|
|
||||||
message("NTP time: %v", t)
|
|
||||||
}
|
|
||||||
|
|
||||||
conn.Close()
|
|
||||||
link.NetDisconnect()
|
|
||||||
|
|
||||||
runtime.AdjustTimeOffset(-1 * int64(time.Since(t)))
|
|
||||||
|
|
||||||
for {
|
|
||||||
message("Current time: %v", time.Now())
|
|
||||||
time.Sleep(time.Minute)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait for user to open serial console
|
|
||||||
func waitSerial() {
|
|
||||||
for !machine.Serial.DTR() {
|
|
||||||
time.Sleep(100 * time.Millisecond)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func getCurrentTime(conn net.Conn) (time.Time, error) {
|
|
||||||
if err := sendNTPpacket(conn); err != nil {
|
|
||||||
return time.Time{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
n, err := conn.Read(response)
|
|
||||||
if err != nil && err != io.EOF {
|
|
||||||
return time.Time{}, err
|
|
||||||
}
|
|
||||||
if n != NTP_PACKET_SIZE {
|
|
||||||
return time.Time{}, fmt.Errorf("expected NTP packet size of %d: %d", NTP_PACKET_SIZE, n)
|
|
||||||
}
|
|
||||||
|
|
||||||
return parseNTPpacket(response), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func sendNTPpacket(conn net.Conn) error {
|
|
||||||
var request = [48]byte{
|
|
||||||
0xe3,
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err := conn.Write(request[:])
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseNTPpacket(r []byte) time.Time {
|
|
||||||
// the timestamp starts at byte 40 of the received packet and is four bytes,
|
|
||||||
// this is NTP time (seconds since Jan 1 1900):
|
|
||||||
t := uint32(r[40])<<24 | uint32(r[41])<<16 | uint32(r[42])<<8 | uint32(r[43])
|
|
||||||
const seventyYears = 2208988800
|
|
||||||
return time.Unix(int64(t-seventyYears), 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func message(format string, args ...interface{}) {
|
|
||||||
println(fmt.Sprintf(format, args...), "\r")
|
|
||||||
}
|
|
||||||
@@ -1,108 +0,0 @@
|
|||||||
// This example opens a TCP connection and sends some data using netdev sockets.
|
|
||||||
//
|
|
||||||
// You can open a server to accept connections from this program using:
|
|
||||||
//
|
|
||||||
// nc -lk 8080
|
|
||||||
|
|
||||||
//go:build pyportal || arduino_nano33 || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 || wioterminal || challenger_rp2040
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"machine"
|
|
||||||
"net/netip"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"tinygo.org/x/drivers/netdev"
|
|
||||||
"tinygo.org/x/drivers/netlink"
|
|
||||||
"tinygo.org/x/drivers/netlink/probe"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
ssid string
|
|
||||||
pass string
|
|
||||||
addr string = "10.0.0.100:8080"
|
|
||||||
)
|
|
||||||
|
|
||||||
var buf = &bytes.Buffer{}
|
|
||||||
var link netlink.Netlinker
|
|
||||||
var dev netdev.Netdever
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
|
|
||||||
waitSerial()
|
|
||||||
|
|
||||||
link, dev = probe.Probe()
|
|
||||||
|
|
||||||
err := link.NetConnect(&netlink.ConnectParams{
|
|
||||||
Ssid: ssid,
|
|
||||||
Passphrase: pass,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for {
|
|
||||||
sendBatch()
|
|
||||||
time.Sleep(500 * time.Millisecond)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func sendBatch() {
|
|
||||||
|
|
||||||
addrPort, _ := netip.ParseAddrPort(addr)
|
|
||||||
|
|
||||||
// make TCP connection
|
|
||||||
message("---------------\r\nDialing TCP connection")
|
|
||||||
fd, _ := dev.Socket(netdev.AF_INET, netdev.SOCK_STREAM, netdev.IPPROTO_TCP)
|
|
||||||
err := dev.Connect(fd, "", addrPort)
|
|
||||||
for ; err != nil; err = dev.Connect(fd, "", addrPort) {
|
|
||||||
message(err.Error())
|
|
||||||
time.Sleep(5 * time.Second)
|
|
||||||
}
|
|
||||||
|
|
||||||
n := 0
|
|
||||||
w := 0
|
|
||||||
start := time.Now()
|
|
||||||
|
|
||||||
// send data
|
|
||||||
message("Sending data")
|
|
||||||
|
|
||||||
for i := 0; i < 1000; i++ {
|
|
||||||
buf.Reset()
|
|
||||||
fmt.Fprint(buf,
|
|
||||||
"\r---------------------------- i == ", i, " ----------------------------"+
|
|
||||||
"\r---------------------------- i == ", i, " ----------------------------")
|
|
||||||
if w, err = dev.Send(fd, buf.Bytes(), 0, time.Time{}); err != nil {
|
|
||||||
println("error:", err.Error(), "\r")
|
|
||||||
break
|
|
||||||
}
|
|
||||||
n += w
|
|
||||||
}
|
|
||||||
|
|
||||||
buf.Reset()
|
|
||||||
ms := time.Now().Sub(start).Milliseconds()
|
|
||||||
fmt.Fprint(buf, "\nWrote ", n, " bytes in ", ms, " ms\r\n")
|
|
||||||
message(buf.String())
|
|
||||||
|
|
||||||
if _, err := dev.Send(fd, buf.Bytes(), 0, time.Time{}); err != nil {
|
|
||||||
println("error:", err.Error(), "\r")
|
|
||||||
}
|
|
||||||
|
|
||||||
println("Disconnecting TCP...")
|
|
||||||
dev.Close(fd)
|
|
||||||
}
|
|
||||||
|
|
||||||
func message(msg string) {
|
|
||||||
println(msg, "\r")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait for user to open serial console
|
|
||||||
func waitSerial() {
|
|
||||||
for !machine.Serial.DTR() {
|
|
||||||
time.Sleep(100 * time.Millisecond)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user