mirror of
https://github.com/tinygo-org/drivers.git
synced 2026-07-26 10:38:41 +00:00
Compare commits
359 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1bf1a11067 | |||
| 848f5def03 | |||
| 4a0bcba87c | |||
| 7d983647ad | |||
| 831982ad33 | |||
| 9063f46313 | |||
| 2a621dc3b1 | |||
| 7dbca2a543 | |||
| 50b6f18cc2 | |||
| bb0e6b8ba8 | |||
| 5eeba138a4 | |||
| 1095629f62 | |||
| bf0b383176 | |||
| f206f6f56f | |||
| a0293643b3 | |||
| 60801ba852 | |||
| a74770b2e6 | |||
| 3ed09d3fe1 | |||
| 22581dfd58 | |||
| 31f09d93b8 | |||
| 0ea969f25a | |||
| 8665d44e0f | |||
| c344e5d879 | |||
| 308763f500 | |||
| c41f2e472d | |||
| aa7bd47c68 | |||
| e7931c6a22 | |||
| 3c5e17423a | |||
| 0262122ccd | |||
| 7d24c3c285 | |||
| 5c0f0480bc | |||
| 8642886f73 | |||
| 1a96fc4547 | |||
| 3fabdc5c96 | |||
| a8928d137e | |||
| 709fc77960 | |||
| d6ae0b0d8b | |||
| 7a7235f83b | |||
| 3089bf8b1b | |||
| 196d1dd58d | |||
| 1fd8a768ae | |||
| 8238f96319 | |||
| e7d51d3c73 | |||
| 4bf631a7e3 | |||
| 3c4df6e811 | |||
| 255c639ad4 | |||
| 276feecc20 | |||
| 00b1b4fbfb | |||
| 3b183ad6ed | |||
| 68da68c8c8 | |||
| f7b80ef95a | |||
| 933c9e127d | |||
| 92050d90da | |||
| 93cbba5d8b | |||
| 6cf07aff4c | |||
| af33129f41 | |||
| 100aadf585 | |||
| 4a9667ffef | |||
| f96a70915e | |||
| 3e64e754a2 | |||
| 2c2da5c7bb | |||
| 14994a3f31 | |||
| 47dfeb9e94 | |||
| f20d1759d3 | |||
| 6b79a1b386 | |||
| 7dd1067cc1 | |||
| 4edb771c5b | |||
| e95cfe66c1 | |||
| 4edb58c0c5 | |||
| f384e2db48 | |||
| 7c96387845 | |||
| 182d4c6ebb | |||
| dc2de4f9ed | |||
| 36a12dd7a2 | |||
| 00992756eb | |||
| 715c33d4b0 | |||
| fd21e6ac9b | |||
| 485ed702c8 | |||
| cfa50fd3c2 | |||
| 5888bb2ded | |||
| a9b36f8bd4 | |||
| 1e4545828f | |||
| cfa5103969 | |||
| a45227590e | |||
| 4f789fb556 | |||
| 996f1b047f | |||
| eef03917ab | |||
| 31538f1b2f | |||
| 64029612e0 | |||
| e6f82fad2e | |||
| e20c6d05f8 | |||
| 9c29529cbb | |||
| f6d399ec08 | |||
| 5e0191655b | |||
| 95f0ca8c3e | |||
| 42e4d29157 | |||
| f627fd162d | |||
| f3f2d65878 | |||
| deca190ba2 | |||
| 184dff93b1 | |||
| 331a60ce2d | |||
| de2742f10c | |||
| 422051d822 | |||
| 66abe9c28b | |||
| 5f25a4f6be | |||
| 9955e09466 | |||
| bc9177726d | |||
| aac959eb7b | |||
| 9fdf0d657d | |||
| b0c9f259dc | |||
| 53404b3f4b | |||
| 8ddfa6ac8f | |||
| c0ab3596fa | |||
| 9de76bc145 | |||
| ae04d5b0bb | |||
| 3c01b7e222 | |||
| c689c11838 | |||
| 59d66d1e73 | |||
| 1eb7072162 | |||
| 5cc21329a6 | |||
| d3022e7d6e | |||
| 5c06b82b27 | |||
| bf53cb2fd4 | |||
| 1fd38a8a68 | |||
| 55e100c4fe | |||
| cba17ef125 | |||
| c59b5a909b | |||
| b0a971573b | |||
| 94017b5437 | |||
| 56208a28d5 | |||
| 0adc7e78aa | |||
| 820298bfe3 | |||
| 46cbacbe03 | |||
| 9d4847fc6d | |||
| 4de0afc224 | |||
| 21fa184b76 | |||
| fae4307190 | |||
| 7f61bda24c | |||
| e613ab1eb3 | |||
| d57d9b6c74 | |||
| 07ba9ec0fd | |||
| fa0cf6ec7f | |||
| 4e687b29c3 | |||
| d283abdb3a | |||
| 02d808a6fb | |||
| d473b9e1dc | |||
| 7cc32dffed | |||
| 58cb44f3f6 | |||
| 78cc1afe46 | |||
| e6cde8f7ae | |||
| 89f113c0ca | |||
| 3b8a808a52 | |||
| 7506a895a2 | |||
| 4a950a4474 | |||
| f2637a87d2 | |||
| ecb343b3b3 | |||
| 0b62d4fb4a | |||
| 5b461ec2d1 | |||
| b1c9612158 | |||
| 0b6e1d0e78 | |||
| 49e7a6d6c8 | |||
| be644b36fe | |||
| 409723e496 | |||
| 8aa3530c8b | |||
| 390854ee45 | |||
| ce5dd65dad | |||
| 73e9d5bfec | |||
| 462d2b3315 | |||
| 9e70ecbe64 | |||
| d6d06396b4 | |||
| 80b2a4a569 | |||
| 556aa9e6d3 | |||
| 4bc8ecdbdd | |||
| 999f42dc03 | |||
| a1b47cd778 | |||
| 889536f7f0 | |||
| 301e6bc35b | |||
| 49b390f7bc | |||
| a62fc01d81 | |||
| 36dfdf568e | |||
| 7b5a3970ca | |||
| 3433364586 | |||
| 338be928c9 | |||
| 98943393a5 | |||
| 6b817faed3 | |||
| 9b14ee3ec5 | |||
| 6d51370512 | |||
| 2005d7d483 | |||
| f15eec822f | |||
| dc4f0c974a | |||
| 28ddb2681d | |||
| 877342d0ff | |||
| 8b3075fdba | |||
| 7d58709b7d | |||
| 789bbbd89f | |||
| ea944c8933 | |||
| d27859453c | |||
| 0ccd979f23 | |||
| a888570c8c | |||
| 5b2c2f800d | |||
| 0ec887c9d8 | |||
| c5dbe18be1 | |||
| 2bca84b8d1 | |||
| 690daebcd1 | |||
| 89770a7d05 | |||
| 8a39bb7aae | |||
| a106fd48ce | |||
| b2fdd3c333 | |||
| 42dc6eb068 | |||
| c00cb3abdd | |||
| 81bc1bcad1 | |||
| 1bb1b621c6 | |||
| 98ba5a231a | |||
| 5af2512901 | |||
| 4061adf6db | |||
| 7133c2e9db | |||
| 0ea72e7f1e | |||
| 7396a5be43 | |||
| c409e8f3cf | |||
| a6a8d478b9 | |||
| 54ddb895cc | |||
| 6f05615f3e | |||
| 2a2e897261 | |||
| ce0ce03cfe | |||
| 5bd814c9de | |||
| 41b7f06d05 | |||
| 749b2f6726 | |||
| 71c77d4b53 | |||
| 1f54c7dd23 | |||
| d0163c67f5 | |||
| 80cd4d9dac | |||
| 594ba1dab2 | |||
| 625c565c0b | |||
| b64d4ec3a0 | |||
| 171366698b | |||
| ca34b93e5d | |||
| 2cfc08b560 | |||
| 1850c5effb | |||
| 810888ce42 | |||
| cafe8df620 | |||
| 13c43350a3 | |||
| 3edd8e413b | |||
| 159f13974c | |||
| 8ddcd69ac0 | |||
| 1c038f5183 | |||
| 36a8b791dc | |||
| df0d0d29a4 | |||
| 3bdab45452 | |||
| 9f6783670f | |||
| ec00fdef9b | |||
| e4951091f4 | |||
| 19caee9b76 | |||
| c235509580 | |||
| dbff576c9e | |||
| f0a260be66 | |||
| 06e298c514 | |||
| f2f470973d | |||
| 448598cbf8 | |||
| ec98f2dc2c | |||
| 2476cd7bd8 | |||
| 01fed475e1 | |||
| edfdd4e09e | |||
| 5e54f08605 | |||
| 880b958d64 | |||
| 24db8b4e2c | |||
| 6df9247f92 | |||
| 56fb346438 | |||
| 3d279d4d25 | |||
| 4a336f674c | |||
| 2d32995f6a | |||
| 9a98d1be55 | |||
| 02e4548966 | |||
| 45dce188f5 | |||
| b3c0315a09 | |||
| 0ced12683c | |||
| b4eb406a43 | |||
| 10bd48c39d | |||
| ad3ef92cfe | |||
| f7dce6ae22 | |||
| a15f2167cc | |||
| 5d5378a47c | |||
| 3ceb688663 | |||
| 941c1c9057 | |||
| d8c813d515 | |||
| 905fc6fce3 | |||
| 80913d5fe7 | |||
| b6c750ccd1 | |||
| 43899e1330 | |||
| 45c68ef0fc | |||
| 025a66655f | |||
| 121e8147f7 | |||
| b48bc5ac5d | |||
| 0d40c99d1f | |||
| bee7422c3a | |||
| 43099c5d5f | |||
| 0f9b9d873b | |||
| 114e24870e | |||
| b33c84ff78 | |||
| 966210f1b0 | |||
| 90a13a697b | |||
| d632eb888f | |||
| f6761a9079 | |||
| 51c3aa271b | |||
| 520234e9f8 | |||
| 334ed57373 | |||
| 3637033fec | |||
| 233866443c | |||
| 65fd7aa1a1 | |||
| 82e9e4a7f3 | |||
| 76ff632134 | |||
| cd2694e85f | |||
| 734adb6df8 | |||
| 2492fcb322 | |||
| 4f82c231cb | |||
| 2c9a05a413 | |||
| a89a26112e | |||
| bd42656644 | |||
| 94729a4f7d | |||
| 050cf4dbbc | |||
| 49c8810432 | |||
| 4b38841f71 | |||
| 3fe3fc64e4 | |||
| 0565a15b17 | |||
| fad9d12e0b | |||
| f2e5278965 | |||
| 259593e608 | |||
| 7a2b1b8c90 | |||
| cad03a7d4e | |||
| 98f5396625 | |||
| 3b16972ed8 | |||
| b35496dd1b | |||
| ad6be40966 | |||
| b3af3f594e | |||
| 9a3bfd4826 | |||
| 1ba9ba3143 | |||
| feb1c63656 | |||
| 0691ed5205 | |||
| e70beede1c | |||
| 92673d996d | |||
| 374e2dfe1d | |||
| b35aab6d61 | |||
| 358501fc47 | |||
| a0119ffc6f | |||
| 58106b5942 | |||
| 2537c69b0f | |||
| 633f19c4ac | |||
| 6a4cd9d8e7 | |||
| 8a4a0ff017 | |||
| 1cbf1a6f3d | |||
| 001bdb43ba | |||
| f7ae6fac6d | |||
| a575b2a4ed | |||
| 0bc3702dd0 | |||
| b53a8a17e7 | |||
| 5e117c162c | |||
| 24dfd8795a | |||
| a748451cab | |||
| a811483fd4 | |||
| 7b565e8cb0 |
@@ -1,20 +0,0 @@
|
||||
# Golang CircleCI 2.0 configuration file
|
||||
#
|
||||
# Check https://circleci.com/docs/2.0/language-go/ for more details
|
||||
version: 2
|
||||
jobs:
|
||||
build:
|
||||
docker:
|
||||
- image: tinygo/tinygo-dev
|
||||
steps:
|
||||
- checkout
|
||||
- run: tinygo version
|
||||
- run:
|
||||
name: "Enforce Go Formatted Code"
|
||||
command: make fmt-check
|
||||
- run:
|
||||
name: "Run unit tests"
|
||||
command: make unit-test
|
||||
- run:
|
||||
name: "Run build and smoke tests"
|
||||
command: make smoke-test
|
||||
@@ -0,0 +1,28 @@
|
||||
name: Build
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- dev
|
||||
- release
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
container: ghcr.io/tinygo-org/tinygo-dev:latest
|
||||
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
|
||||
uses: actions/checkout@v3
|
||||
- name: TinyGo version check
|
||||
run: tinygo version
|
||||
- name: Enforce Go Formatted Code
|
||||
run: make fmt-check
|
||||
- name: Run unit tests
|
||||
run: make unit-test
|
||||
- name: Run build and smoke tests
|
||||
run: make smoke-test
|
||||
+529
@@ -1,3 +1,532 @@
|
||||
0.28.0
|
||||
---
|
||||
- **new devices**
|
||||
- **epd2in66b**
|
||||
- Waveshare 2.66inch E-Paper Display Module (B) for Raspberry Pi Pico (#673)
|
||||
- **mcp9808**
|
||||
- Add driver for MCP9808 i2c temperature sensor (#676)
|
||||
|
||||
- **enhancements**
|
||||
- **encoders**
|
||||
- add atsamd21, atsamd51, atsame5x
|
||||
- **pixel**
|
||||
- add support for Monochrome types such as the SSD1306 display
|
||||
- **rtl8720dn**
|
||||
- implement ConnectModeAP
|
||||
- **servo**
|
||||
- add function SetAngle() to simplify API for most common use case
|
||||
- **ssd1306**
|
||||
- add DrawBitmap() function to complete Displayer interface
|
||||
- add rotation functions for Displayer interface
|
||||
- add Sleep() function for Displayer interface
|
||||
- **uc8151**
|
||||
- improvements to speed and also add flicker-free mode based on @antirez code example
|
||||
- update to support all functions needed by tinygl and board package Displayer interface
|
||||
- **wifinina**
|
||||
- implement ConnectModeAP
|
||||
|
||||
- **bugfixes**
|
||||
- **ft6336**
|
||||
- ignore bogus touch events
|
||||
- **pixel**
|
||||
- fix Image[Monochrome].Set for larger images
|
||||
- **uc8151**
|
||||
- correct DrawBitmap() also refactor SendCommand() and SendData() for clarity
|
||||
- **ws2812**
|
||||
- Fix typo and move initialization of neo to init()
|
||||
|
||||
- **examples**
|
||||
- **ws2812**
|
||||
- Simplify examples/ws2812
|
||||
|
||||
|
||||
0.27.0
|
||||
---
|
||||
- **core**
|
||||
- prepare for CGo changes in TinyGo
|
||||
|
||||
- **new devices**
|
||||
- **adafruit4650**
|
||||
- support for Adafruit 4650 feather OLED
|
||||
- **net**
|
||||
- new networking support based on tinygo net package
|
||||
- **pixel**
|
||||
- add package for efficiently working with raw pixel buffers
|
||||
- **rotary**
|
||||
- Adding driver for rotary encoder support
|
||||
- **seesaw**
|
||||
- Adding support for Adafruit Seesaw platform
|
||||
- **sgp30**
|
||||
- add SGP30 air quality sensor
|
||||
- **sk6812**
|
||||
- added support for SK6812 to WS2812 device (#610)
|
||||
|
||||
- **enhancements**
|
||||
- **epd2in13**
|
||||
- add Sleep method like other displays
|
||||
- unify rotation configuration with other displays
|
||||
- use better black/white approximation
|
||||
- **ili9341**
|
||||
- add DrawBitmap method
|
||||
- **lora/lorawan**
|
||||
- LoRa WAN US915 Support
|
||||
- LoRa WAN add setter functions
|
||||
- refactor shared functionality for channels/regions
|
||||
- **mcp2515**
|
||||
- Add more line speeds to mcp2515.go (#626)
|
||||
- **rtl8720dn**
|
||||
- use drivers package version as the driver version
|
||||
- **ssd1306**
|
||||
- improvements needed for Thumby SPI display
|
||||
- **st7735**
|
||||
- make the display generic over RGB565 and RGB444
|
||||
- **st7789**
|
||||
- add DrawBitmap method
|
||||
- make the display generic over RGB565 and RGB444
|
||||
- **wifinina**
|
||||
- add ResetIsHigh cfg switch for MKR 1010 (copied from #561)
|
||||
- maintenence. Also see PR #4085 in the main TinyGo repo
|
||||
- use drivers package version as the driver version
|
||||
|
||||
- **bugfixes**
|
||||
- **adxl345**
|
||||
- Use int16 for ADXL345 readings (#656)
|
||||
- **at24cx**
|
||||
- fixed the description of the device struct
|
||||
- **rtl8720dn**
|
||||
- allow connecting to open wifi access points
|
||||
- fix check for bad Wifi connect
|
||||
- **sh1106**
|
||||
- fix I2C interface and add smoketest
|
||||
- fixed the description of the device struct
|
||||
- **wifinina**
|
||||
- add 'unknown failure' reason code for AP connect
|
||||
- fix concurrency issues with multiple sockets
|
||||
- fix wifinina UDP send
|
||||
|
||||
- **examples**
|
||||
- **ds3231**
|
||||
- fix the description in the example
|
||||
- **lorawan**
|
||||
- add missing functions for simulated interface
|
||||
- modify atcmd and basic demo to support choosing any one of the supported regions at compile time by using ldflags
|
||||
- **net**
|
||||
- all networking examples now using netdev and netlink.
|
||||
|
||||
- **build**
|
||||
- **all**
|
||||
- fix broken testrunner
|
||||
- migrated legacy I2C
|
||||
- add natiu package for tests
|
||||
- **smoketest**
|
||||
- add stack-size param for net tests.
|
||||
- allow stack-size flag as it is needed for net examples
|
||||
|
||||
|
||||
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
|
||||
---
|
||||
- **new devices**
|
||||
- Add GC9A01 Driver (#452)
|
||||
- Initial support for VL6180x sensor
|
||||
|
||||
- **enhancements**
|
||||
- **rtl8720dn**
|
||||
- refactor by bringing this driver more in line with wifinina and espat
|
||||
- **ssd1306**
|
||||
- add getter method to the current buffer
|
||||
- **makeybutton**
|
||||
- revise to better match the algorithm defined by the original
|
||||
- **espat,wifinina,rtl8720dn**
|
||||
- change ssid/pass from const to var
|
||||
|
||||
- **bugfixes**
|
||||
- **microbitmatrix**
|
||||
- fix inverted axis
|
||||
- **espat**
|
||||
- Trim quotes from IP returned by espat's GetDNS()
|
||||
- **all**
|
||||
- correct go fmt
|
||||
|
||||
- **examples**
|
||||
- **rtl8720dn**
|
||||
- remove wifi setting
|
||||
- add call to optional debug setting
|
||||
- update all remaining examples for refactored API
|
||||
|
||||
|
||||
0.22.0
|
||||
---
|
||||
- **new devices**
|
||||
- epd: add waveshare 2.9in (v1)
|
||||
- makeybutton: add driver for MakeyMakey-like button
|
||||
|
||||
- **enhancements**
|
||||
- **rtl8720dn**
|
||||
- add UDP close function
|
||||
- improve error handling
|
||||
- **net/http**
|
||||
- improve header parsing
|
||||
- add last-will-and-testament to MQTT
|
||||
- **net/mqtt**
|
||||
- adds keepalive pinging, disconnect, and graceful goroutine cleanup
|
||||
- support for cookies when https
|
||||
- add support for retained messsages
|
||||
|
||||
- **bugfixes**
|
||||
- irremote: Fix irremote reporting incorrect NEC addresses and command codes (#422)
|
||||
- net/http: Fix http.Get() with port specification
|
||||
|
||||
- **build**
|
||||
- Makefile recursively finds unit-tests
|
||||
- switching to GHA
|
||||
|
||||
- **updates**
|
||||
- update tinyfont to v0.3.0
|
||||
- update tinyfs to v0.2.0
|
||||
|
||||
- **examples**
|
||||
- rtl8720dn: add ./examples/rtl8720dn/version
|
||||
|
||||
|
||||
0.21.0
|
||||
---
|
||||
- **new devices**
|
||||
- lsm6ds3tr: initial implementation
|
||||
- UC8151: used in Pimoroni's badger2040 e-paper (#416)
|
||||
- scd4x: implement driver for CO2 sensor
|
||||
|
||||
- **enhancements**
|
||||
- easystepper: Add support for '8-step mode'
|
||||
- vl53l1x: Add functions for setting the device address
|
||||
- sdcard: support thingplus-rp2040
|
||||
- wifinina: add mutex to prevent communication race problems
|
||||
- **ws2812**
|
||||
- support thingplus-rp2040 board
|
||||
- Added 125 MHz rp2040 timing
|
||||
- Added unsafe.Pointer for pointer conversion
|
||||
|
||||
- **bugfixes**
|
||||
- ssd1351: Fix mirrored text on OLED display
|
||||
|
||||
|
||||
0.20.0
|
||||
---
|
||||
- **new devices**
|
||||
- irremote: Add basic infra-red driver
|
||||
- IS31FL3731: add driver for IS31FL3731 matrix LED driver (#370)
|
||||
- l3gd20: add gyro driver
|
||||
- SSD1289: Driver for SSD1289 LCD
|
||||
|
||||
- **enhancements**
|
||||
- **ili9341**
|
||||
- add support for atsame5x
|
||||
- added Feather board support to InitDisplay()
|
||||
- avoid heap allocations
|
||||
- **lps22hb**
|
||||
- pin rename, sync with main repo
|
||||
- **lsmXXX**
|
||||
- unified, error handling, memory management
|
||||
- **max7xx**
|
||||
- Add a SetIntensity() function to max7xx driver and example
|
||||
- **vl53l1x**
|
||||
- Add functions for setting 'region of interest'
|
||||
- Fix switch-case semantics
|
||||
- **ws2812**
|
||||
- add support for m5stamp-c3
|
||||
- convert AVR assembly to C inline assembly
|
||||
- support high-MHz ARMv6M chips like the RP2040
|
||||
- write inline assembly using C instead of Go
|
||||
|
||||
- **bugfixes**
|
||||
- **dht**
|
||||
- fix error check in example
|
||||
- fix humidity and temperature extraction for DHT22 (#358)
|
||||
- **esp8266**
|
||||
- fix ConnectToAccessPoint timeout args
|
||||
- **image**
|
||||
- fix interface
|
||||
- **pca9685**
|
||||
- add buffered one shot write
|
||||
- fix on=0 bug
|
||||
- **wifinina**
|
||||
- correct sendParamStr to handle empty strings, such as when connecting to an unsecured access point
|
||||
|
||||
0.19.0
|
||||
---
|
||||
- **new devices**
|
||||
- ft6336: add support for ft6336
|
||||
- pca9685: PCA9685 driver
|
||||
- shtc3: Sensirion SHTC3 Relative Humidity / Temperature i2c sensor
|
||||
- sx126x: Driver for Semtech sx126x radio modules
|
||||
- xpt2046: XPT2046 Touch driver (#350)
|
||||
- **enhancements**
|
||||
- **hd44780i2c**
|
||||
- clean up for go fmt
|
||||
- Needed fixes and update hd44780i2c.go
|
||||
- **ili9341, ili9342**
|
||||
- add support for m5stack
|
||||
- add support for m5stack-core2
|
||||
- **wifi**
|
||||
- modify to use shared net.Adapter interface for all supported wifi devices
|
||||
- wifinina: remove busy wait
|
||||
- **bugfixes**
|
||||
- **hd44780**
|
||||
- fix 4-bit data length flag
|
||||
- Reset data pins to output mode after reading
|
||||
- Nano 33 BLE drivers (#351)
|
||||
- **docs**
|
||||
- examples/wifi: add unified example for tcpclient that compiles for all supported wifi adaptors
|
||||
|
||||
0.18.0
|
||||
---
|
||||
- **new devices**
|
||||
- apds9960: add support for APDS-9960 Digital Proximity sensor
|
||||
- axp192: add support for AXP192 single Cell Li-Battery and power system management IC
|
||||
- hts221: add support for HTS221 capacitive digital sensor for relative humidity and temperature
|
||||
- i2csoft: add support for software I2C
|
||||
- image: add support for image/jpeg and image/png
|
||||
- lps22hb: add support for LPS22HB MEMS nano pressure sensor
|
||||
- lsm6dox: add support for lsm6dox accelerometer
|
||||
- lsm9ds1: add support for lsm9ds1 accelerometer
|
||||
- **enhancements**
|
||||
- ili9341: change to use drivers.SPI interface
|
||||
- **ws2812**
|
||||
- generate assembly instead of handwriting it
|
||||
- improve timings to be compatible with the WS2811
|
||||
- add support for 168MHz (e.g. Adafruit Feather STM32F405)
|
||||
- add support for RISC-V
|
||||
- wifinina: control nina pins, for example leds
|
||||
- **docs**
|
||||
- rtl8720dn: examples for tcpclient, udpstation, mqtt, and webserver
|
||||
- **wifinina**
|
||||
- nina-fw update docs
|
||||
- examples/wifinina/http-get
|
||||
- ili9341: refactor examples
|
||||
- Fix broken link for SHT3x datasheet
|
||||
- **core**
|
||||
- all: use build directives for both Go1.17 and earlier versions
|
||||
- **bugfixes**
|
||||
- net: fix raddr of tcp conn
|
||||
- mcp3008: fix bitshift bug
|
||||
|
||||
0.17.1
|
||||
---
|
||||
- To correct an error in the release process. Same as 0.17.0.
|
||||
|
||||
0.17.0
|
||||
---
|
||||
- **new devices**
|
||||
- rtl8720dn: add support for rtl8720dn
|
||||
- sdcard: add support for spi sdcard driver, along with fatfs
|
||||
- **enhancements**
|
||||
- apa102: use 4-byte buffer to improve speed
|
||||
- bmi160: avoid heap allocations
|
||||
- ili9341: add standard SPI driver
|
||||
- wifinina
|
||||
- avoid fmt package
|
||||
- Fix RSSI command for WiFiNINA + Print current SSID + Wait for correct time before printing it out + Cleanup
|
||||
- ws2812
|
||||
- rename the pin to ws2812
|
||||
- add tag for nrf52833
|
||||
- Disable interrupts before sending ws2812 data
|
||||
- add support for qtpy and atsame5x
|
||||
- **core**
|
||||
- modules: switch to use tinygo-org version of tinyfs package
|
||||
- all: use machine.Serial as the default output
|
||||
|
||||
0.16.0
|
||||
---
|
||||
- **new devices**
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2018-2021 The TinyGo Authors. All rights reserved.
|
||||
Copyright (c) 2018-2024 The TinyGo Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
|
||||
@@ -2,210 +2,27 @@
|
||||
clean:
|
||||
@rm -rf build
|
||||
|
||||
FMT_PATHS = ./*.go ./examples/**/*.go
|
||||
FMT_PATHS = ./
|
||||
|
||||
fmt-check:
|
||||
@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:
|
||||
@mkdir -p build
|
||||
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=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=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=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=circuitplay-express ./examples/lis3dh/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=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=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/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=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
|
||||
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=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
|
||||
@go run ./smoketest.go -xtensa=$(XTENSA) smoketest.sh
|
||||
|
||||
DRIVERS = $(wildcard */)
|
||||
NOTESTS = build examples flash semihosting pcd8544 shiftregister st7789 microphone mcp3008 gps microbitmatrix \
|
||||
hcsr04 ssd1331 ws2812 thermistor apa102 easystepper ssd1351 ili9341 wifinina shifter hub75 \
|
||||
hd44780 buzzer ssd1306 espat l9110x st7735 bmi160 l293x dht keypad4x4 max72xx p1am tone tm1637 \
|
||||
pcf8563 mcp2515 servo sdcard
|
||||
TESTS = $(filter-out $(addsuffix /%,$(NOTESTS)),$(DRIVERS))
|
||||
|
||||
# rwildcard is a recursive version of $(wildcard)
|
||||
# https://blog.jgc.org/2011/07/gnu-make-recursive-wildcard-function.html
|
||||
rwildcard=$(foreach d,$(wildcard $1*),$(call rwildcard,$d/,$2) $(filter $(subst *,%,$2),$d))
|
||||
# Recursively find all *_test.go files from cwd & reduce to unique dir names
|
||||
HAS_TESTS = $(sort $(dir $(call rwildcard,,*_test.go)))
|
||||
# Exclude anything we explicitly don't want to test for whatever reason
|
||||
EXCLUDE_TESTS = image waveshare-epd/epd2in66b
|
||||
TESTS = $(filter-out $(addsuffix /%,$(EXCLUDE_TESTS)),$(HAS_TESTS))
|
||||
|
||||
unit-test:
|
||||
@go test -v $(addprefix ./,$(TESTS))
|
||||
@go test -v $(addprefix ./,$(TESTS))
|
||||
|
||||
test: clean fmt-check unit-test smoke-test
|
||||
|
||||
+233
@@ -0,0 +1,233 @@
|
||||
### 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()
|
||||
}
|
||||
```
|
||||
@@ -1,9 +1,12 @@
|
||||
# TinyGo Drivers
|
||||
|
||||
[](https://pkg.go.dev/tinygo.org/x/drivers) [](https://circleci.com/gh/tinygo-org/drivers/tree/dev)
|
||||
[](https://pkg.go.dev/tinygo.org/x/drivers) [](https://github.com/tinygo-org/drivers/actions/workflows/build.yml)
|
||||
|
||||
|
||||
This package provides a collection of hardware drivers for devices such as sensors and displays that can be used together with [TinyGo](https://tinygo.org).
|
||||
This package provides a collection of over 100 different hardware drivers for devices such as sensors, displays, wireless adaptors, and actuators, that can be used together with [TinyGo](https://tinygo.org).
|
||||
|
||||
For the complete list, please see:
|
||||
https://tinygo.org/docs/reference/devices/
|
||||
|
||||
## Installing
|
||||
|
||||
@@ -50,79 +53,6 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
## Currently supported devices
|
||||
|
||||
The following 66 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 |
|
||||
| [AT24CX 2-wire serial EEPROM](https://www.openimpulse.com/blog/wp-content/uploads/wpsc/downloadables/24C32-Datasheet.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 |
|
||||
| [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 |
|
||||
| [HUB75 RGB led matrix](https://cdn-learn.adafruit.com/downloads/pdf/32x16-32x32-rgb-led-matrix.pdf) | SPI |
|
||||
| [ILI9341 TFT color display](https://cdn-shop.adafruit.com/datasheets/ILI9341.pdf) | SPI |
|
||||
| [INA260 Volt/Amp/Power meter](https://www.ti.com/lit/ds/symlink/ina260.pdf) | I2C |
|
||||
| [4x4 Membrane Keypad](https://cdn.sparkfun.com/assets/f/f/a/5/0/DS-16038.pdf) | GPIO |
|
||||
| [L293x motor driver](https://www.ti.com/lit/ds/symlink/l293d.pdf) | GPIO/PWM |
|
||||
| [L9110x motor driver](https://www.elecrow.com/download/datasheet-l9110.pdf) | GPIO/PWM |
|
||||
| [LIS2MDL magnetometer](https://www.st.com/resource/en/datasheet/lis2mdl.pdf) | I2C |
|
||||
| [LIS3DH accelerometer](https://www.st.com/resource/en/datasheet/lis3dh.pdf) | I2C |
|
||||
| [LSM6DS3 accelerometer](https://www.st.com/resource/en/datasheet/lsm6ds3.pdf) | I2C |
|
||||
| [MAG3110 magnetometer](https://www.nxp.com/docs/en/data-sheet/MAG3110.pdf) | I2C |
|
||||
| [MAX7219 & MAX7221 display driver](https://datasheets.maximintegrated.com/en/ds/MAX7219-MAX7221.pdf) | SPI |
|
||||
| [MCP23017 port expander](https://ww1.microchip.com/downloads/en/DeviceDoc/20001952C.pdf) | I2C |
|
||||
| [MCP3008 analog to digital converter (ADC)](http://ww1.microchip.com/downloads/en/DeviceDoc/21295d.pdf) | SPI |
|
||||
| [MCP2515 Stand-Alone CAN Controller with SPI Interface](https://ww1.microchip.com/downloads/en/DeviceDoc/MCP2515-Family-Data-Sheet-DS20001801K.pdf) | SPI |
|
||||
| [Microphone - PDM](https://cdn-learn.adafruit.com/assets/assets/000/049/977/original/MP34DT01-M.pdf) | I2S/PDM |
|
||||
| [MMA8653 accelerometer](https://www.nxp.com/docs/en/data-sheet/MMA8653FC.pdf) | I2C |
|
||||
| [MPU6050 accelerometer/gyroscope](https://store.invensense.com/datasheets/invensense/MPU-6050_DataSheet_V3%204.pdf) | I2C |
|
||||
| [P1AM-100 Base Controller](https://facts-engineering.github.io/modules/P1AM-100/P1AM-100.html) | SPI |
|
||||
| [PCD8544 display](http://eia.udg.edu/~forest/PCD8544_1.pdf) | SPI |
|
||||
| [PCF8563 real time clock](https://www.nxp.com/docs/en/data-sheet/PCF8563.pdf) | I2C |
|
||||
| [Resistive Touchscreen (4-wire)](http://ww1.microchip.com/downloads/en/Appnotes/doc8091.pdf) | GPIO |
|
||||
| [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/0_Datasheets/Humidity/Sensirion_Humidity_Sensors_SHT3x_Datasheet_digital.pdf) | I2C |
|
||||
| [SPI NOR Flash Memory](https://en.wikipedia.org/wiki/Flash_memory#NOR_flash) | SPI/QSPI |
|
||||
| [SPI SDCARD/MMC](https://en.wikipedia.org/wiki/SD_card) | SPI |
|
||||
| [SSD1306 OLED display](https://cdn-shop.adafruit.com/datasheets/SSD1306.pdf) | I2C / SPI |
|
||||
| [SSD1331 TFT color display](https://www.crystalfontz.com/controllers/SolomonSystech/SSD1331/381/) | SPI |
|
||||
| [SSD1351 OLED display](https://download.mikroe.com/documents/datasheets/ssd1351-revision-1.3.pdf) | SPI |
|
||||
| [ST7735 TFT color display](https://www.crystalfontz.com/controllers/Sitronix/ST7735R/319/) | SPI |
|
||||
| [ST7789 TFT color display](https://cdn-shop.adafruit.com/product-files/3787/3787_tft_QT154H2201__________20190228182902.pdf) | SPI |
|
||||
| [Stepper motor "Easystepper" controller](https://en.wikipedia.org/wiki/Stepper_motor) | GPIO |
|
||||
| [Thermistor](https://www.farnell.com/datasheets/33552.pdf) | ADC |
|
||||
| [TM1637 7-segment LED display](https://www.mcielectronics.cl/website_MCI/static/documents/Datasheet_TM1637.pdf) | I2C |
|
||||
| [TMP102 I2C Temperature Sensor](https://download.mikroe.com/documents/datasheets/tmp102-data-sheet.pdf) | I2C |
|
||||
| [VEML6070 UV light sensor](https://www.vishay.com/docs/84277/veml6070.pdf) | I2C |
|
||||
| [VL53L1X time-of-flight distance sensor](https://www.st.com/resource/en/datasheet/vl53l1x.pdf) | I2C |
|
||||
| [Waveshare 2.13" (B & C) e-paper display](https://www.waveshare.com/w/upload/d/d3/2.13inch-e-paper-b-Specification.pdf) | SPI |
|
||||
| [Waveshare 2.13" e-paper display](https://www.waveshare.com/w/upload/e/e6/2.13inch_e-Paper_Datasheet.pdf) | SPI |
|
||||
| [Waveshare 4.2" e-paper B/W display](https://www.waveshare.com/w/upload/6/6a/4.2inch-e-paper-specification.pdf) | SPI |
|
||||
| [WS2812 RGB LED](https://cdn-shop.adafruit.com/datasheets/WS2812.pdf) | GPIO |
|
||||
|
||||
## Contributing
|
||||
|
||||
Your contributions are welcome!
|
||||
|
||||
@@ -0,0 +1,196 @@
|
||||
// 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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
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.
|
After Width: | Height: | Size: 449 B |
+4
-4
@@ -1,13 +1,13 @@
|
||||
// Package adt7410 provides a driver for the adt7410 I2C Temperature Sensor.
|
||||
//
|
||||
// Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/ADT7410.pdf
|
||||
//
|
||||
package adt7410 // import "tinygo.org/x/drivers/adt7410"
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
"tinygo.org/x/drivers/internal/legacy"
|
||||
)
|
||||
|
||||
type Error uint8
|
||||
@@ -55,7 +55,7 @@ func (d *Device) Configure() (err error) {
|
||||
// Connected returns whether sensor has been found.
|
||||
func (d *Device) Connected() bool {
|
||||
data := []byte{0}
|
||||
d.bus.ReadRegister(uint8(d.Address), RegID, data)
|
||||
legacy.ReadRegister(d.bus, uint8(d.Address), RegID, data)
|
||||
return data[0]&0xF8 == 0xC8
|
||||
}
|
||||
|
||||
@@ -82,11 +82,11 @@ func (d *Device) writeByte(reg uint8, data byte) {
|
||||
}
|
||||
|
||||
func (d *Device) readByte(reg uint8) byte {
|
||||
d.bus.ReadRegister(d.Address, reg, d.buf)
|
||||
legacy.ReadRegister(d.bus, d.Address, reg, d.buf)
|
||||
return d.buf[0]
|
||||
}
|
||||
|
||||
func (d *Device) readUint16(reg uint8) uint16 {
|
||||
d.bus.ReadRegister(d.Address, reg, d.buf)
|
||||
legacy.ReadRegister(d.bus, d.Address, reg, d.buf)
|
||||
return uint16(d.buf[0])<<8 | uint16(d.buf[1])
|
||||
}
|
||||
|
||||
+20
-18
@@ -3,10 +3,12 @@
|
||||
// Datasheet EN: http://www.analog.com/media/en/technical-documentation/data-sheets/ADXL345.pdf
|
||||
//
|
||||
// Datasheet JP: http://www.analog.com/media/jp/technical-documentation/data-sheets/ADXL345_jp.pdf
|
||||
//
|
||||
package adxl345 // import "tinygo.org/x/drivers/adxl345"
|
||||
|
||||
import "tinygo.org/x/drivers"
|
||||
import (
|
||||
"tinygo.org/x/drivers"
|
||||
"tinygo.org/x/drivers/internal/legacy"
|
||||
)
|
||||
|
||||
type Range uint8
|
||||
type Rate uint8
|
||||
@@ -69,21 +71,21 @@ func New(bus drivers.I2C) Device {
|
||||
|
||||
// Configure sets up the device for communication
|
||||
func (d *Device) Configure() {
|
||||
d.bus.WriteRegister(uint8(d.Address), REG_BW_RATE, []byte{d.bwRate.toByte()})
|
||||
d.bus.WriteRegister(uint8(d.Address), REG_POWER_CTL, []byte{d.powerCtl.toByte()})
|
||||
d.bus.WriteRegister(uint8(d.Address), REG_DATA_FORMAT, []byte{d.dataFormat.toByte()})
|
||||
legacy.WriteRegister(d.bus, uint8(d.Address), REG_BW_RATE, []byte{d.bwRate.toByte()})
|
||||
legacy.WriteRegister(d.bus, uint8(d.Address), REG_POWER_CTL, []byte{d.powerCtl.toByte()})
|
||||
legacy.WriteRegister(d.bus, uint8(d.Address), REG_DATA_FORMAT, []byte{d.dataFormat.toByte()})
|
||||
}
|
||||
|
||||
// Halt stops the sensor, values will not updated
|
||||
func (d *Device) Halt() {
|
||||
d.powerCtl.measure = 0
|
||||
d.bus.WriteRegister(uint8(d.Address), REG_POWER_CTL, []byte{d.powerCtl.toByte()})
|
||||
legacy.WriteRegister(d.bus, uint8(d.Address), REG_POWER_CTL, []byte{d.powerCtl.toByte()})
|
||||
}
|
||||
|
||||
// Restart makes reading the sensor working again after a halt
|
||||
func (d *Device) Restart() {
|
||||
d.powerCtl.measure = 1
|
||||
d.bus.WriteRegister(uint8(d.Address), REG_POWER_CTL, []byte{d.powerCtl.toByte()})
|
||||
legacy.WriteRegister(d.bus, uint8(d.Address), REG_POWER_CTL, []byte{d.powerCtl.toByte()})
|
||||
}
|
||||
|
||||
// ReadAcceleration reads the current acceleration from the device and returns
|
||||
@@ -93,18 +95,18 @@ func (d *Device) Restart() {
|
||||
func (d *Device) ReadAcceleration() (x int32, y int32, z int32, err error) {
|
||||
rx, ry, rz := d.ReadRawAcceleration()
|
||||
|
||||
x = d.dataFormat.convertToIS(rx)
|
||||
y = d.dataFormat.convertToIS(ry)
|
||||
z = d.dataFormat.convertToIS(rz)
|
||||
x = int32(d.dataFormat.convertToIS(rx))
|
||||
y = int32(d.dataFormat.convertToIS(ry))
|
||||
z = int32(d.dataFormat.convertToIS(rz))
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// ReadRawAcceleration reads the sensor values and returns the raw x, y and z axis
|
||||
// from the adxl345.
|
||||
func (d *Device) ReadRawAcceleration() (x int32, y int32, z int32) {
|
||||
func (d *Device) ReadRawAcceleration() (x int16, y int16, z int16) {
|
||||
data := []byte{0, 0, 0, 0, 0, 0}
|
||||
d.bus.ReadRegister(uint8(d.Address), REG_DATAX0, data)
|
||||
legacy.ReadRegister(d.bus, uint8(d.Address), REG_DATAX0, data)
|
||||
|
||||
x = readIntLE(data[0], data[1])
|
||||
y = readIntLE(data[2], data[3])
|
||||
@@ -120,25 +122,25 @@ func (d *Device) UseLowPower(power bool) {
|
||||
} else {
|
||||
d.bwRate.lowPower = 0
|
||||
}
|
||||
d.bus.WriteRegister(uint8(d.Address), REG_BW_RATE, []byte{d.bwRate.toByte()})
|
||||
legacy.WriteRegister(d.bus, uint8(d.Address), REG_BW_RATE, []byte{d.bwRate.toByte()})
|
||||
}
|
||||
|
||||
// SetRate change the current rate of the sensor
|
||||
func (d *Device) SetRate(rate Rate) bool {
|
||||
d.bwRate.rate = rate & 0x0F
|
||||
d.bus.WriteRegister(uint8(d.Address), REG_BW_RATE, []byte{d.bwRate.toByte()})
|
||||
legacy.WriteRegister(d.bus, uint8(d.Address), REG_BW_RATE, []byte{d.bwRate.toByte()})
|
||||
return true
|
||||
}
|
||||
|
||||
// SetRange change the current range of the sensor
|
||||
func (d *Device) SetRange(sensorRange Range) bool {
|
||||
d.dataFormat.sensorRange = sensorRange & 0x03
|
||||
d.bus.WriteRegister(uint8(d.Address), REG_DATA_FORMAT, []byte{d.dataFormat.toByte()})
|
||||
legacy.WriteRegister(d.bus, uint8(d.Address), REG_DATA_FORMAT, []byte{d.dataFormat.toByte()})
|
||||
return true
|
||||
}
|
||||
|
||||
// convertToIS adjusts the raw values from the adxl345 with the range configuration
|
||||
func (d *dataFormat) convertToIS(rawValue int32) int32 {
|
||||
func (d *dataFormat) convertToIS(rawValue int16) int16 {
|
||||
switch d.sensorRange {
|
||||
case RANGE_2G:
|
||||
return rawValue * 4 // rawValue * 2 * 1000 / 512
|
||||
@@ -188,6 +190,6 @@ func (b *bwRate) toByte() (bits uint8) {
|
||||
}
|
||||
|
||||
// readInt converts two bytes to int16
|
||||
func readIntLE(msb byte, lsb byte) int32 {
|
||||
return int32(uint16(msb) | uint16(lsb)<<8)
|
||||
func readIntLE(msb byte, lsb byte) int16 {
|
||||
return int16(uint16(msb) | uint16(lsb)<<8)
|
||||
}
|
||||
|
||||
+17
-16
@@ -8,6 +8,7 @@ import (
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
"tinygo.org/x/drivers/internal/legacy"
|
||||
)
|
||||
|
||||
// Device wraps an I2C connection to a AMG88xx device.
|
||||
@@ -48,7 +49,7 @@ func (d *Device) Configure(cfg Config) {
|
||||
|
||||
// ReadPixels returns the 64 values (8x8 grid) of the sensor converted to millicelsius
|
||||
func (d *Device) ReadPixels(buffer *[64]int16) {
|
||||
d.bus.ReadRegister(uint8(d.Address), PIXEL_OFFSET, d.data)
|
||||
legacy.ReadRegister(d.bus, uint8(d.Address), PIXEL_OFFSET, d.data)
|
||||
for i := 0; i < 64; i++ {
|
||||
buffer[i] = int16((uint16(d.data[2*i+1]) << 8) | uint16(d.data[2*i]))
|
||||
if (buffer[i] & (1 << 11)) > 0 { // temperature negative
|
||||
@@ -61,17 +62,17 @@ func (d *Device) ReadPixels(buffer *[64]int16) {
|
||||
|
||||
// SetPCTL sets the PCTL
|
||||
func (d *Device) SetPCTL(pctl uint8) {
|
||||
d.bus.WriteRegister(uint8(d.Address), PCTL, []byte{pctl})
|
||||
legacy.WriteRegister(d.bus, uint8(d.Address), PCTL, []byte{pctl})
|
||||
}
|
||||
|
||||
// SetReset sets the reset value
|
||||
func (d *Device) SetReset(rst uint8) {
|
||||
d.bus.WriteRegister(uint8(d.Address), RST, []byte{rst})
|
||||
legacy.WriteRegister(d.bus, uint8(d.Address), RST, []byte{rst})
|
||||
}
|
||||
|
||||
// SetFrameRate configures the frame rate
|
||||
func (d *Device) SetFrameRate(framerate uint8) {
|
||||
d.bus.WriteRegister(uint8(d.Address), FPSC, []byte{framerate & 0x01})
|
||||
legacy.WriteRegister(d.bus, uint8(d.Address), FPSC, []byte{framerate & 0x01})
|
||||
}
|
||||
|
||||
// SetMovingAverageMode sets the moving average mode
|
||||
@@ -80,7 +81,7 @@ func (d *Device) SetMovingAverageMode(mode bool) {
|
||||
if mode {
|
||||
value = 1
|
||||
}
|
||||
d.bus.WriteRegister(uint8(d.Address), AVE, []byte{value << 5})
|
||||
legacy.WriteRegister(d.bus, uint8(d.Address), AVE, []byte{value << 5})
|
||||
}
|
||||
|
||||
// SetInterruptLevels sets the interrupt levels
|
||||
@@ -97,8 +98,8 @@ func (d *Device) SetInterruptLevelsHysteresis(high int16, low int16, hysteresis
|
||||
if high > 4095 {
|
||||
high = 4095
|
||||
}
|
||||
d.bus.WriteRegister(uint8(d.Address), INTHL, []byte{uint8(high & 0xFF)})
|
||||
d.bus.WriteRegister(uint8(d.Address), INTHL, []byte{uint8((high & 0xFF) >> 4)})
|
||||
legacy.WriteRegister(d.bus, uint8(d.Address), INTHL, []byte{uint8(high & 0xFF)})
|
||||
legacy.WriteRegister(d.bus, uint8(d.Address), INTHL, []byte{uint8((high & 0xFF) >> 4)})
|
||||
|
||||
low = low / PIXEL_TEMP_CONVERSION
|
||||
if low < -4095 {
|
||||
@@ -107,8 +108,8 @@ func (d *Device) SetInterruptLevelsHysteresis(high int16, low int16, hysteresis
|
||||
if low > 4095 {
|
||||
low = 4095
|
||||
}
|
||||
d.bus.WriteRegister(uint8(d.Address), INTHL, []byte{uint8(low & 0xFF)})
|
||||
d.bus.WriteRegister(uint8(d.Address), INTHL, []byte{uint8((low & 0xFF) >> 4)})
|
||||
legacy.WriteRegister(d.bus, uint8(d.Address), INTHL, []byte{uint8(low & 0xFF)})
|
||||
legacy.WriteRegister(d.bus, uint8(d.Address), INTHL, []byte{uint8((low & 0xFF) >> 4)})
|
||||
|
||||
hysteresis = hysteresis / PIXEL_TEMP_CONVERSION
|
||||
if hysteresis < -4095 {
|
||||
@@ -117,32 +118,32 @@ func (d *Device) SetInterruptLevelsHysteresis(high int16, low int16, hysteresis
|
||||
if hysteresis > 4095 {
|
||||
hysteresis = 4095
|
||||
}
|
||||
d.bus.WriteRegister(uint8(d.Address), INTHL, []byte{uint8(hysteresis & 0xFF)})
|
||||
d.bus.WriteRegister(uint8(d.Address), INTHL, []byte{uint8((hysteresis & 0xFF) >> 4)})
|
||||
legacy.WriteRegister(d.bus, uint8(d.Address), INTHL, []byte{uint8(hysteresis & 0xFF)})
|
||||
legacy.WriteRegister(d.bus, uint8(d.Address), INTHL, []byte{uint8((hysteresis & 0xFF) >> 4)})
|
||||
}
|
||||
|
||||
// EnableInterrupt enables the interrupt pin on the device
|
||||
func (d *Device) EnableInterrupt() {
|
||||
d.interruptEnable = 1
|
||||
d.bus.WriteRegister(uint8(d.Address), INTC, []byte{((uint8(d.interruptMode) << 1) | d.interruptEnable) & 0x03})
|
||||
legacy.WriteRegister(d.bus, uint8(d.Address), INTC, []byte{((uint8(d.interruptMode) << 1) | d.interruptEnable) & 0x03})
|
||||
}
|
||||
|
||||
// DisableInterrupt disables the interrupt pin on the device
|
||||
func (d *Device) DisableInterrupt() {
|
||||
d.interruptEnable = 0
|
||||
d.bus.WriteRegister(uint8(d.Address), INTC, []byte{((uint8(d.interruptMode) << 1) | d.interruptEnable) & 0x03})
|
||||
legacy.WriteRegister(d.bus, uint8(d.Address), INTC, []byte{((uint8(d.interruptMode) << 1) | d.interruptEnable) & 0x03})
|
||||
}
|
||||
|
||||
// SetInterruptMode sets the interrupt mode
|
||||
func (d *Device) SetInterruptMode(mode InterruptMode) {
|
||||
d.interruptMode = mode
|
||||
d.bus.WriteRegister(uint8(d.Address), INTC, []byte{((uint8(d.interruptMode) << 1) | d.interruptEnable) & 0x03})
|
||||
legacy.WriteRegister(d.bus, uint8(d.Address), INTC, []byte{((uint8(d.interruptMode) << 1) | d.interruptEnable) & 0x03})
|
||||
}
|
||||
|
||||
// GetInterrupt reads the state of the triggered interrupts
|
||||
func (d *Device) GetInterrupt() []uint8 {
|
||||
data := make([]uint8, 8)
|
||||
d.bus.ReadRegister(uint8(d.Address), INT_OFFSET, data)
|
||||
legacy.ReadRegister(d.bus, uint8(d.Address), INT_OFFSET, data)
|
||||
return data
|
||||
}
|
||||
|
||||
@@ -154,6 +155,6 @@ func (d *Device) ClearInterrupt() {
|
||||
// ReadThermistor reads the onboard thermistor
|
||||
func (d *Device) ReadThermistor() int16 {
|
||||
data := make([]uint8, 2)
|
||||
d.bus.ReadRegister(uint8(d.Address), TTHL, data)
|
||||
legacy.ReadRegister(d.bus, uint8(d.Address), TTHL, data)
|
||||
return (int16((uint16(data[1])<<8)|uint16(data[0])) * THERMISTOR_CONVERSION) / 10
|
||||
}
|
||||
|
||||
@@ -0,0 +1,468 @@
|
||||
// Package apds9960 implements a driver for APDS-9960,
|
||||
// a digital proximity, ambient light, RGB and gesture sensor.
|
||||
//
|
||||
// Datasheet: https://cdn.sparkfun.com/assets/learn_tutorials/3/2/1/Avago-APDS-9960-datasheet.pdf
|
||||
package apds9960
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
"tinygo.org/x/drivers/internal/legacy"
|
||||
)
|
||||
|
||||
// Device wraps an I2C connection to a APDS-9960 device.
|
||||
type Device struct {
|
||||
bus drivers.I2C
|
||||
Address uint8
|
||||
mode uint8
|
||||
gesture gestureData
|
||||
}
|
||||
|
||||
// Configuration for APDS-9960 device.
|
||||
type Configuration struct {
|
||||
ProximityPulseLength uint8
|
||||
ProximityPulseCount uint8
|
||||
GesturePulseLength uint8
|
||||
GesturePulseCount uint8
|
||||
ProximityGain uint8
|
||||
GestureGain uint8
|
||||
ColorGain uint8
|
||||
ADCIntegrationCycles uint16
|
||||
LEDBoost uint16
|
||||
threshold uint8
|
||||
sensitivity uint8
|
||||
}
|
||||
|
||||
// for gesture-related data
|
||||
type gestureData struct {
|
||||
detected uint8
|
||||
threshold uint8
|
||||
sensitivity uint8
|
||||
gXDelta int16
|
||||
gYDelta int16
|
||||
gXPrevDelta int16
|
||||
gYPrevDelta int16
|
||||
received bool
|
||||
}
|
||||
|
||||
// for enabling various device function
|
||||
type enableConfig struct {
|
||||
GEN bool
|
||||
PIEN bool
|
||||
AIEN bool
|
||||
WEN bool
|
||||
PEN bool
|
||||
AEN bool
|
||||
PON bool
|
||||
}
|
||||
|
||||
// New creates a new APDS-9960 connection. The I2C bus must already be
|
||||
// configured.
|
||||
//
|
||||
// This function only creates the Device object, it does not touch the device.
|
||||
func New(bus drivers.I2C) Device {
|
||||
return Device{bus: bus, Address: ADPS9960_ADDRESS, mode: MODE_NONE}
|
||||
}
|
||||
|
||||
// Connected returns whether APDS-9960 has been found.
|
||||
// It does a "who am I" request and checks the response.
|
||||
func (d *Device) Connected() bool {
|
||||
data := []byte{0}
|
||||
legacy.ReadRegister(d.bus, d.Address, APDS9960_ID_REG, data)
|
||||
return data[0] == 0xAB
|
||||
}
|
||||
|
||||
// GetMode returns current engine mode
|
||||
func (d *Device) GetMode() uint8 {
|
||||
return d.mode
|
||||
}
|
||||
|
||||
// DisableAll turns off the device and all functions
|
||||
func (d *Device) DisableAll() {
|
||||
d.enable(enableConfig{})
|
||||
legacy.WriteRegister(d.bus, d.Address, APDS9960_GCONF4_REG, []byte{0x00})
|
||||
d.mode = MODE_NONE
|
||||
d.gesture.detected = GESTURE_NONE
|
||||
}
|
||||
|
||||
// SetProximityPulse sets proximity pulse length (4, 8, 16, 32) and count (1~64)
|
||||
// default: 16, 64
|
||||
func (d *Device) SetProximityPulse(length, count uint8) {
|
||||
legacy.WriteRegister(d.bus, d.Address, APDS9960_PPULSE_REG, []byte{getPulseLength(length)<<6 | getPulseCount(count)})
|
||||
}
|
||||
|
||||
// SetGesturePulse sets gesture pulse length (4, 8, 16, 32) and count (1~64)
|
||||
// default: 16, 64
|
||||
func (d *Device) SetGesturePulse(length, count uint8) {
|
||||
legacy.WriteRegister(d.bus, 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)
|
||||
// default: 4 (~10 ms)
|
||||
func (d *Device) SetADCIntegrationCycles(cycles uint16) {
|
||||
if cycles > 256 {
|
||||
cycles = 256
|
||||
}
|
||||
legacy.WriteRegister(d.bus, 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)
|
||||
// default: 1, 1, 4
|
||||
func (d *Device) SetGains(proximityGain, gestureGain, colorGain uint8) {
|
||||
legacy.WriteRegister(d.bus, d.Address, APDS9960_CONTROL_REG, []byte{getProximityGain(proximityGain)<<2 | getALSGain(colorGain)})
|
||||
legacy.WriteRegister(d.bus, d.Address, APDS9960_GCONF2_REG, []byte{getProximityGain(gestureGain) << 5})
|
||||
}
|
||||
|
||||
// LEDBoost sets proximity and gesture LED current level (100, 150, 200, 300 (%))
|
||||
// default: 100
|
||||
func (d *Device) LEDBoost(percent uint16) {
|
||||
var v uint8
|
||||
switch percent {
|
||||
case 100:
|
||||
v = 0
|
||||
case 150:
|
||||
v = 1
|
||||
case 200:
|
||||
v = 2
|
||||
case 300:
|
||||
v = 3
|
||||
}
|
||||
legacy.WriteRegister(d.bus, d.Address, APDS9960_CONFIG2_REG, []byte{0x01 | v<<4})
|
||||
}
|
||||
|
||||
// Setthreshold sets threshold (0~255) for detecting gestures
|
||||
// default: 30
|
||||
func (d *Device) Setthreshold(t uint8) {
|
||||
d.gesture.threshold = t
|
||||
}
|
||||
|
||||
// Setsensitivity sets sensivity (0~100) for detecting gestures
|
||||
// default: 20
|
||||
func (d *Device) Setsensitivity(s uint8) {
|
||||
if s > 100 {
|
||||
s = 100
|
||||
}
|
||||
d.gesture.sensitivity = 100 - s
|
||||
}
|
||||
|
||||
// EnableProximity starts the proximity engine
|
||||
func (d *Device) EnableProximity() {
|
||||
if d.mode != MODE_NONE {
|
||||
d.DisableAll()
|
||||
}
|
||||
d.enable(enableConfig{PON: true, PEN: true, WEN: true})
|
||||
d.mode = MODE_PROXIMITY
|
||||
}
|
||||
|
||||
// ProximityAvailable reports if proximity data is available
|
||||
func (d *Device) ProximityAvailable() bool {
|
||||
if d.mode == MODE_PROXIMITY && d.readStatus("PVALID") {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ReadProximity reads proximity data (0~255)
|
||||
func (d *Device) ReadProximity() (proximity int32) {
|
||||
if d.mode != MODE_PROXIMITY {
|
||||
return 0
|
||||
}
|
||||
data := []byte{0}
|
||||
legacy.ReadRegister(d.bus, d.Address, APDS9960_PDATA_REG, data)
|
||||
return 255 - int32(data[0])
|
||||
}
|
||||
|
||||
// EnableColor starts the color engine
|
||||
func (d *Device) EnableColor() {
|
||||
if d.mode != MODE_NONE {
|
||||
d.DisableAll()
|
||||
}
|
||||
d.enable(enableConfig{PON: true, AEN: true, WEN: true})
|
||||
d.mode = MODE_COLOR
|
||||
}
|
||||
|
||||
// ColorAvailable reports if color data is available
|
||||
func (d *Device) ColorAvailable() bool {
|
||||
if d.mode == MODE_COLOR && d.readStatus("AVALID") {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ReadColor reads color data (red, green, blue, clear color/brightness)
|
||||
func (d *Device) ReadColor() (r int32, g int32, b int32, clear int32) {
|
||||
if d.mode != MODE_COLOR {
|
||||
return
|
||||
}
|
||||
data := []byte{0, 0, 0, 0, 0, 0, 0, 0}
|
||||
legacy.ReadRegister(d.bus, d.Address, APDS9960_CDATAL_REG, data[:1])
|
||||
legacy.ReadRegister(d.bus, d.Address, APDS9960_CDATAH_REG, data[1:2])
|
||||
legacy.ReadRegister(d.bus, d.Address, APDS9960_RDATAL_REG, data[2:3])
|
||||
legacy.ReadRegister(d.bus, d.Address, APDS9960_RDATAH_REG, data[3:4])
|
||||
legacy.ReadRegister(d.bus, d.Address, APDS9960_GDATAL_REG, data[4:5])
|
||||
legacy.ReadRegister(d.bus, d.Address, APDS9960_GDATAH_REG, data[5:6])
|
||||
legacy.ReadRegister(d.bus, d.Address, APDS9960_BDATAL_REG, data[6:7])
|
||||
legacy.ReadRegister(d.bus, d.Address, APDS9960_BDATAH_REG, data[7:])
|
||||
clear = int32(uint16(data[1])<<8 | uint16(data[0]))
|
||||
r = int32(uint16(data[3])<<8 | uint16(data[2]))
|
||||
g = int32(uint16(data[5])<<8 | uint16(data[4]))
|
||||
b = int32(uint16(data[7])<<8 | uint16(data[6]))
|
||||
return
|
||||
}
|
||||
|
||||
// EnableGesture starts the gesture engine
|
||||
func (d *Device) EnableGesture() {
|
||||
if d.mode != MODE_NONE {
|
||||
d.DisableAll()
|
||||
}
|
||||
d.enable(enableConfig{PON: true, PEN: true, GEN: true, WEN: true})
|
||||
d.mode = MODE_GESTURE
|
||||
d.gesture.detected = GESTURE_NONE
|
||||
d.gesture.gXDelta = 0
|
||||
d.gesture.gYDelta = 0
|
||||
d.gesture.gXPrevDelta = 0
|
||||
d.gesture.gYPrevDelta = 0
|
||||
d.gesture.received = false
|
||||
}
|
||||
|
||||
// GestureAvailable reports if gesture data is available
|
||||
func (d *Device) GestureAvailable() bool {
|
||||
if d.mode != MODE_GESTURE {
|
||||
return false
|
||||
}
|
||||
|
||||
data := []byte{0, 0, 0, 0}
|
||||
|
||||
// check GVALID
|
||||
legacy.ReadRegister(d.bus, d.Address, APDS9960_GSTATUS_REG, data[:1])
|
||||
if data[0]&0x01 == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
// get number of data sets available in FIFO
|
||||
legacy.ReadRegister(d.bus, d.Address, APDS9960_GFLVL_REG, data[:1])
|
||||
availableDataSets := data[0]
|
||||
if availableDataSets == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
// read up, down, left and right proximity data from FIFO
|
||||
var dataSets [32][4]uint8
|
||||
for i := uint8(0); i < availableDataSets; i++ {
|
||||
legacy.ReadRegister(d.bus, d.Address, APDS9960_GFIFO_U_REG, data[:1])
|
||||
legacy.ReadRegister(d.bus, d.Address, APDS9960_GFIFO_D_REG, data[1:2])
|
||||
legacy.ReadRegister(d.bus, d.Address, APDS9960_GFIFO_L_REG, data[2:3])
|
||||
legacy.ReadRegister(d.bus, d.Address, APDS9960_GFIFO_R_REG, data[3:4])
|
||||
for j := uint8(0); j < 4; j++ {
|
||||
dataSets[i][j] = data[j]
|
||||
}
|
||||
}
|
||||
|
||||
// gesture detection process
|
||||
d.gesture.detected = GESTURE_NONE
|
||||
for i := uint8(0); i < availableDataSets; i++ {
|
||||
U := dataSets[i][0]
|
||||
D := dataSets[i][1]
|
||||
L := dataSets[i][2]
|
||||
R := dataSets[i][3]
|
||||
|
||||
// if all readings fall below threshold, it's possible that
|
||||
// a movement's just been made
|
||||
if U < d.gesture.threshold && D < d.gesture.threshold && L < d.gesture.threshold && R < d.gesture.threshold {
|
||||
d.gesture.received = true
|
||||
// if there were movement in the previous step (including the last data sets)
|
||||
if d.gesture.gXPrevDelta != 0 && d.gesture.gYPrevDelta != 0 {
|
||||
totalX := d.gesture.gXPrevDelta - d.gesture.gXDelta
|
||||
totalY := d.gesture.gYPrevDelta - d.gesture.gYDelta
|
||||
// if previous and current movement are in opposite directions (pass through one led then next)
|
||||
// and the difference is big enough, the gesture is recorded
|
||||
switch {
|
||||
case totalX < -int16(d.gesture.sensitivity):
|
||||
d.gesture.detected = GESTURE_LEFT
|
||||
case totalX > int16(d.gesture.sensitivity):
|
||||
d.gesture.detected = GESTURE_RIGHT
|
||||
case totalY > int16(d.gesture.sensitivity):
|
||||
d.gesture.detected = GESTURE_DOWN
|
||||
case totalY < -int16(d.gesture.sensitivity):
|
||||
d.gesture.detected = GESTURE_UP
|
||||
}
|
||||
d.gesture.gXDelta = 0
|
||||
d.gesture.gYDelta = 0
|
||||
d.gesture.gXPrevDelta = 0
|
||||
d.gesture.gYPrevDelta = 0
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// recording current movement
|
||||
d.gesture.gXDelta = int16(R) - int16(L)
|
||||
d.gesture.gYDelta = int16(D) - int16(U)
|
||||
if d.gesture.received {
|
||||
d.gesture.received = false
|
||||
d.gesture.gXPrevDelta = d.gesture.gXDelta
|
||||
d.gesture.gYPrevDelta = d.gesture.gYDelta
|
||||
}
|
||||
}
|
||||
|
||||
return d.gesture.detected != GESTURE_NONE
|
||||
}
|
||||
|
||||
// ReadGesture reads last gesture data
|
||||
func (d *Device) ReadGesture() (gesture int32) {
|
||||
return int32(d.gesture.detected)
|
||||
}
|
||||
|
||||
// private functions
|
||||
|
||||
func (d *Device) configureDevice(cfg Configuration) {
|
||||
d.DisableAll() // turn off everything
|
||||
|
||||
// "default" settings
|
||||
if cfg.ProximityPulseLength == 0 {
|
||||
cfg.ProximityPulseLength = 16
|
||||
}
|
||||
if cfg.ProximityPulseCount == 0 {
|
||||
cfg.ProximityPulseCount = 64
|
||||
}
|
||||
if cfg.GesturePulseLength == 0 {
|
||||
cfg.GesturePulseLength = 16
|
||||
}
|
||||
if cfg.GesturePulseCount == 0 {
|
||||
cfg.GesturePulseCount = 64
|
||||
}
|
||||
if cfg.ProximityGain == 0 {
|
||||
cfg.ProximityGain = 1
|
||||
}
|
||||
if cfg.GestureGain == 0 {
|
||||
cfg.GestureGain = 1
|
||||
}
|
||||
if cfg.ColorGain == 0 {
|
||||
cfg.ColorGain = 4
|
||||
}
|
||||
if cfg.ADCIntegrationCycles == 0 {
|
||||
cfg.ADCIntegrationCycles = 4
|
||||
}
|
||||
if cfg.threshold == 0 {
|
||||
d.gesture.threshold = 30
|
||||
}
|
||||
if cfg.sensitivity == 0 {
|
||||
d.gesture.sensitivity = 20
|
||||
}
|
||||
|
||||
d.SetProximityPulse(cfg.ProximityPulseLength, cfg.ProximityPulseCount)
|
||||
d.SetGesturePulse(cfg.GesturePulseLength, cfg.GesturePulseCount)
|
||||
d.SetGains(cfg.ProximityGain, cfg.GestureGain, cfg.ColorGain)
|
||||
d.SetADCIntegrationCycles(cfg.ADCIntegrationCycles)
|
||||
|
||||
if cfg.LEDBoost > 0 {
|
||||
d.LEDBoost(cfg.LEDBoost)
|
||||
}
|
||||
}
|
||||
|
||||
func (d *Device) enable(cfg enableConfig) {
|
||||
var gen, pien, aien, wen, pen, aen, pon uint8
|
||||
|
||||
if cfg.GEN {
|
||||
gen = 1
|
||||
}
|
||||
if cfg.PIEN {
|
||||
pien = 1
|
||||
}
|
||||
if cfg.AIEN {
|
||||
aien = 1
|
||||
}
|
||||
if cfg.WEN {
|
||||
wen = 1
|
||||
}
|
||||
if cfg.PEN {
|
||||
pen = 1
|
||||
}
|
||||
if cfg.AEN {
|
||||
aen = 1
|
||||
}
|
||||
if cfg.PON {
|
||||
pon = 1
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
if cfg.PON {
|
||||
time.Sleep(time.Millisecond * 10)
|
||||
}
|
||||
}
|
||||
|
||||
func (d *Device) readStatus(param string) bool {
|
||||
data := []byte{0}
|
||||
legacy.ReadRegister(d.bus, d.Address, APDS9960_STATUS_REG, data)
|
||||
|
||||
switch param {
|
||||
case "CPSAT":
|
||||
return data[0]>>7&0x01 == 1
|
||||
case "PGSAT":
|
||||
return data[0]>>6&0x01 == 1
|
||||
case "PINT":
|
||||
return data[0]>>5&0x01 == 1
|
||||
case "AINT":
|
||||
return data[0]>>4&0x01 == 1
|
||||
case "PVALID":
|
||||
return data[0]>>1&0x01 == 1
|
||||
case "AVALID":
|
||||
return data[0]&0x01 == 1
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func getPulseLength(l uint8) uint8 {
|
||||
switch l {
|
||||
case 4:
|
||||
return 0
|
||||
case 8:
|
||||
return 1
|
||||
case 16:
|
||||
return 2
|
||||
case 32:
|
||||
return 3
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
func getPulseCount(c uint8) uint8 {
|
||||
if c < 1 && c > 64 {
|
||||
return 0
|
||||
}
|
||||
return c - 1
|
||||
}
|
||||
|
||||
func getProximityGain(g uint8) uint8 {
|
||||
switch g {
|
||||
case 1:
|
||||
return 0
|
||||
case 2:
|
||||
return 1
|
||||
case 4:
|
||||
return 2
|
||||
case 8:
|
||||
return 3
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
func getALSGain(g uint8) uint8 {
|
||||
switch g {
|
||||
case 1:
|
||||
return 0
|
||||
case 4:
|
||||
return 1
|
||||
case 16:
|
||||
return 2
|
||||
case 64:
|
||||
return 3
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
//go:build !nano_33_ble
|
||||
|
||||
package apds9960
|
||||
|
||||
// Configure sets up the APDS-9960 device.
|
||||
func (d *Device) Configure(cfg Configuration) {
|
||||
// configure device
|
||||
d.configureDevice(cfg)
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
//go:build nano_33_ble
|
||||
|
||||
package apds9960
|
||||
|
||||
import (
|
||||
"machine"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Configure sets up the APDS-9960 device.
|
||||
func (d *Device) Configure(cfg Configuration) {
|
||||
|
||||
// Following lines are Nano 33 BLE specific, they have nothing to do with sensor per se
|
||||
machine.LSM_PWR.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
machine.LSM_PWR.High()
|
||||
machine.I2C_PULLUP.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
machine.I2C_PULLUP.High()
|
||||
// Wait a moment
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
|
||||
// configure device
|
||||
d.configureDevice(cfg)
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package apds9960
|
||||
|
||||
const (
|
||||
|
||||
// I2C address
|
||||
ADPS9960_ADDRESS = 0x39
|
||||
|
||||
// control/status registers
|
||||
APDS9960_RAM_REG = 0x00
|
||||
APDS9960_ENABLE_REG = 0x80
|
||||
APDS9960_ATIME_REG = 0x81
|
||||
APDS9960_WTIME_REG = 0x83
|
||||
APDS9960_AILTIL_REG = 0x84
|
||||
APDS9960_AILTH_REG = 0x85
|
||||
APDS9960_AIHTL_REG = 0x86
|
||||
APDS9960_AIHTH_REG = 0x87
|
||||
APDS9960_PILT_REG = 0x89
|
||||
APDS9960_PIHT_REG = 0x8B
|
||||
APDS9960_PERS_REG = 0x8C
|
||||
APDS9960_CONFIG1_REG = 0x8D
|
||||
APDS9960_PPULSE_REG = 0x8E
|
||||
APDS9960_CONTROL_REG = 0x8F
|
||||
APDS9960_CONFIG2_REG = 0x90
|
||||
APDS9960_ID_REG = 0x92
|
||||
APDS9960_STATUS_REG = 0x93
|
||||
APDS9960_CDATAL_REG = 0x94
|
||||
APDS9960_CDATAH_REG = 0x95
|
||||
APDS9960_RDATAL_REG = 0x96
|
||||
APDS9960_RDATAH_REG = 0x97
|
||||
APDS9960_GDATAL_REG = 0x98
|
||||
APDS9960_GDATAH_REG = 0x99
|
||||
APDS9960_BDATAL_REG = 0x9A
|
||||
APDS9960_BDATAH_REG = 0x9B
|
||||
APDS9960_PDATA_REG = 0x9C
|
||||
APDS9960_POFFSET_UR_REG = 0x9D
|
||||
APDS9960_POFFSET_DL_REG = 0x9E
|
||||
APDS9960_CONFIG3_REG = 0x9F
|
||||
APDS9960_GPENTH_REG = 0xA0
|
||||
APDS9960_GEXTH_REG = 0xA1
|
||||
APDS9960_GCONF1_REG = 0xA2
|
||||
APDS9960_GCONF2_REG = 0xA3
|
||||
APDS9960_GOFFSET_U_REG = 0xA4
|
||||
APDS9960_GOFFSET_D_REG = 0xA5
|
||||
APDS9960_GOFFSET_L_REG = 0xA7
|
||||
APDS9960_GOFFSET_R_REG = 0xA9
|
||||
APDS9960_GPULSE_REG = 0xA6
|
||||
APDS9960_GCONF3_REG = 0xAA
|
||||
APDS9960_GCONF4_REG = 0xAB
|
||||
APDS9960_GFLVL_REG = 0xAE
|
||||
APDS9960_GSTATUS_REG = 0xAF
|
||||
APDS9960_IFORCE_REG = 0xE4
|
||||
APDS9960_PICLEAR_REG = 0xE5
|
||||
APDS9960_CICLEAR_REG = 0xE6
|
||||
APDS9960_AICLEAR_REG = 0xE7
|
||||
APDS9960_GFIFO_U_REG = 0xFC
|
||||
APDS9960_GFIFO_D_REG = 0xFD
|
||||
APDS9960_GFIFO_L_REG = 0xFE
|
||||
APDS9960_GFIFO_R_REG = 0xFF
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
// sensor modes
|
||||
MODE_NONE = iota
|
||||
MODE_PROXIMITY
|
||||
MODE_COLOR
|
||||
MODE_GESTURE
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
// detected gestures
|
||||
GESTURE_NONE = iota
|
||||
GESTURE_UP
|
||||
GESTURE_DOWN
|
||||
GESTURE_LEFT
|
||||
GESTURE_RIGHT
|
||||
)
|
||||
@@ -0,0 +1,172 @@
|
||||
// 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))
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
// 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}
|
||||
}
|
||||
@@ -0,0 +1,198 @@
|
||||
// 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))
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
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
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
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
|
||||
}
|
||||
@@ -0,0 +1,208 @@
|
||||
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"
|
||||
)
|
||||
|
||||
// Device wraps an I2C connection to a DS3231 device.
|
||||
// Device wraps an I2C connection to an AT24CX device.
|
||||
type Device struct {
|
||||
bus drivers.I2C
|
||||
Address uint16
|
||||
|
||||
@@ -0,0 +1,258 @@
|
||||
// Package axp192 provides a driver for the axp192 I2C Enhanced single Cell
|
||||
// Li-Battery and Power System Management IC.
|
||||
//
|
||||
// http://www.x-powers.com/en.php/Info/product_detail/article_id/29
|
||||
// Datasheet: https://github.com/m5stack/M5-Schematic/blob/master/Core/AXP192%20Datasheet_v1.1_en_draft_2211.pdf
|
||||
package axp192 // import "tinygo.org/x/drivers/axp192"
|
||||
|
||||
import (
|
||||
"tinygo.org/x/drivers"
|
||||
"tinygo.org/x/drivers/internal/legacy"
|
||||
)
|
||||
|
||||
type Error uint8
|
||||
|
||||
const (
|
||||
ErrInvalidID Error = 0x1
|
||||
)
|
||||
|
||||
func (e Error) Error() string {
|
||||
switch e {
|
||||
case ErrInvalidID:
|
||||
return "Invalid chip ID"
|
||||
default:
|
||||
return "Unknown error"
|
||||
}
|
||||
}
|
||||
|
||||
type Device struct {
|
||||
bus drivers.I2C
|
||||
buf []byte
|
||||
Address uint8
|
||||
}
|
||||
|
||||
// New returns AXP192 device for the provided I2C bus using default address.
|
||||
func New(i2c drivers.I2C) *Device {
|
||||
return &Device{
|
||||
bus: i2c,
|
||||
buf: make([]byte, 2),
|
||||
Address: Address,
|
||||
}
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
}
|
||||
|
||||
// Configure the AXP192 device.
|
||||
func (d *Device) Configure(config Config) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReadPowerSupplyStatus reads power supply status.
|
||||
func (d *Device) ReadPowerSupplyStatus() uint8 {
|
||||
return d.read8bit(RegPowerSupplyStatus)
|
||||
}
|
||||
|
||||
// SetVbusIPSOutAccessManagement sets VBUS-IPSOUT access management.
|
||||
func (d *Device) SetVbusIPSOutAccessManagement(a uint8) {
|
||||
d.write1Byte(RegVbusIPSOutAccessManagement, a)
|
||||
}
|
||||
|
||||
// GetVbusIPSOutAccessManagement gets VBUS-IPSOUT access management.
|
||||
func (d *Device) GetVbusIPSOutAccessManagement() uint8 {
|
||||
return d.read8bit(RegVbusIPSOutAccessManagement)
|
||||
}
|
||||
|
||||
// SetGPIO1Control sets GPIO1 function.
|
||||
func (d *Device) SetGPIO1Control(a uint8) {
|
||||
d.write1Byte(RegGPIO1Control, a)
|
||||
}
|
||||
|
||||
// GetGPIO1Control gets GPIO1 function.
|
||||
func (d *Device) GetGPIO1Control() uint8 {
|
||||
return d.read8bit(RegGPIO1Control)
|
||||
}
|
||||
|
||||
// SetGPIO2Control sets GPIO2 function.
|
||||
func (d *Device) SetGPIO2Control(a uint8) {
|
||||
d.write1Byte(RegGPIO2Control, a)
|
||||
}
|
||||
|
||||
// GetGPIO2Control gets GPIO2 function.
|
||||
func (d *Device) GetGPIO2Control() uint8 {
|
||||
return d.read8bit(RegGPIO2Control)
|
||||
}
|
||||
|
||||
// SetGPIO20SignalStatus sets GPIO[2:0] signal status.
|
||||
func (d *Device) SetGPIO20SignalStatus(a uint8) {
|
||||
d.write1Byte(RegGPIO20SignalStatus, a)
|
||||
}
|
||||
|
||||
// GetGPIO20SignalStatus gets GPIO[2:0] signal status.
|
||||
func (d *Device) GetGPIO20SignalStatus() uint8 {
|
||||
return d.read8bit(RegGPIO20SignalStatus)
|
||||
}
|
||||
|
||||
// SetBackupBatteryChargingControl sets backup battery charge control.
|
||||
func (d *Device) SetBackupBatteryChargingControl(a uint8) {
|
||||
d.write1Byte(RegBackupBatteryChargingControl, a)
|
||||
}
|
||||
|
||||
// GetBackupBatteryChargingControl gets backup battery charge control.
|
||||
func (d *Device) GetBackupBatteryChargingControl() uint8 {
|
||||
return d.read8bit(RegBackupBatteryChargingControl)
|
||||
}
|
||||
|
||||
// SetDCDC1VoltageSet sets DC-DC1 output voltage.
|
||||
func (d *Device) SetDCDC1VoltageSet(a uint8) {
|
||||
d.write1Byte(RegDCDC1VoltageSet, a)
|
||||
}
|
||||
|
||||
// GetDCDC1VoltageSet gets DC-DC1 output voltage.
|
||||
func (d *Device) GetDCDC1VoltageSet() uint8 {
|
||||
return d.read8bit(RegDCDC1VoltageSet)
|
||||
}
|
||||
|
||||
// SetDCDC2VoltageSet sets DC-DC2 dynamic voltage parameter.
|
||||
func (d *Device) SetDCDC2VoltageSet(a uint8) {
|
||||
d.write1Byte(RegDCDC2VoltageSet, a)
|
||||
}
|
||||
|
||||
// GetDCDC2VoltageSet gets DC-DC2 dynamic voltage parameter.
|
||||
func (d *Device) GetDCDC2VoltageSet() uint8 {
|
||||
return d.read8bit(RegDCDC2VoltageSet)
|
||||
}
|
||||
|
||||
// SetDCDC3VoltageSet sets DC-DC3 output voltage.
|
||||
func (d *Device) SetDCDC3VoltageSet(a uint8) {
|
||||
d.write1Byte(RegDCDC3VoltageSet, a)
|
||||
}
|
||||
|
||||
// GetDCDC3VoltageSet gets DC-DC3 output voltage.
|
||||
func (d *Device) GetDCDC3VoltageSet() uint8 {
|
||||
return d.read8bit(RegDCDC3VoltageSet)
|
||||
}
|
||||
|
||||
// SetLDO23VoltageSet sets LDO2/3 output voltage.
|
||||
func (d *Device) SetLDO23VoltageSet(a uint8) {
|
||||
d.write1Byte(RegLDO23VoltageSet, a)
|
||||
}
|
||||
|
||||
// GetLDO23VoltageSet gets LDO2/3 output voltage.
|
||||
func (d *Device) GetLDO23VoltageSet() uint8 {
|
||||
return d.read8bit(RegLDO23VoltageSet)
|
||||
}
|
||||
|
||||
// SetDCDC13LDO23Switch sets DC-DC1/3 & LOD2/3 output control.
|
||||
func (d *Device) SetDCDC13LDO23Switch(a uint8) {
|
||||
d.write1Byte(RegDCDC13LDO23Switch, a)
|
||||
}
|
||||
|
||||
// GetDCDC13LDO23Switch gets DC-DC1/3 & LOD2/3 output control.
|
||||
func (d *Device) GetDCDC13LDO23Switch() uint8 {
|
||||
return d.read8bit(RegDCDC13LDO23Switch)
|
||||
}
|
||||
|
||||
// SetGPIO43FunctionControl sets GPIO[4:3] pin function.
|
||||
func (d *Device) SetGPIO43FunctionControl(a uint8) {
|
||||
d.write1Byte(RegGPIO43FunctionControl, a)
|
||||
}
|
||||
|
||||
// GetGPIO43FunctionControl gets GPIO[4:3] pin function.
|
||||
func (d *Device) GetGPIO43FunctionControl() uint8 {
|
||||
return d.read8bit(RegGPIO43FunctionControl)
|
||||
}
|
||||
|
||||
// SetPEKParameterSet sets PEK press key parameter.
|
||||
func (d *Device) SetPEKParameterSet(a uint8) {
|
||||
d.write1Byte(RegPEKParameterSet, a)
|
||||
}
|
||||
|
||||
// GetPEKParameterSet gets PEK press key parameter.
|
||||
func (d *Device) GetPEKParameterSet() uint8 {
|
||||
return d.read8bit(RegPEKParameterSet)
|
||||
}
|
||||
|
||||
// SetADCEnableSet sets ADC enable 1.
|
||||
func (d *Device) SetADCEnableSet(a uint8) {
|
||||
d.write1Byte(RegADCEnableSet, a)
|
||||
}
|
||||
|
||||
// GetADCEnableSet gets ADC enable 1.
|
||||
func (d *Device) GetADCEnableSet() uint8 {
|
||||
return d.read8bit(RegADCEnableSet)
|
||||
}
|
||||
|
||||
// SetGPIO43SignalStatus sets GPIO[4:3] signal status.
|
||||
func (d *Device) SetGPIO43SignalStatus(a uint8) {
|
||||
d.write1Byte(RegGPIO43SignalStatus, a)
|
||||
}
|
||||
|
||||
// GetGPIO43SignalStatus gets GPIO[4:3] signal status.
|
||||
func (d *Device) GetGPIO43SignalStatus() uint8 {
|
||||
return d.read8bit(RegGPIO43SignalStatus)
|
||||
}
|
||||
|
||||
// SetDCVoltage sets DC voltage.
|
||||
func (d *Device) SetDCVoltage(number uint8, voltage uint16) {
|
||||
if voltage < 700 {
|
||||
voltage = 0
|
||||
} else {
|
||||
voltage = (voltage - 700) / 25
|
||||
}
|
||||
|
||||
switch number {
|
||||
case 0:
|
||||
v := d.GetDCDC1VoltageSet()
|
||||
d.SetDCDC1VoltageSet((v & 0x80) | (uint8(voltage) & 0x7F))
|
||||
case 1:
|
||||
v := d.GetDCDC2VoltageSet()
|
||||
d.SetDCDC2VoltageSet((v & 0x80) | (uint8(voltage) & 0x7F))
|
||||
case 2:
|
||||
v := d.GetDCDC3VoltageSet()
|
||||
d.SetDCDC3VoltageSet((v & 0x80) | (uint8(voltage) & 0x7F))
|
||||
}
|
||||
}
|
||||
|
||||
// SetLDOVoltage sets LDO voltage.
|
||||
func (d *Device) SetLDOVoltage(number uint8, voltage uint16) {
|
||||
if voltage > 3300 {
|
||||
voltage = 15
|
||||
} else {
|
||||
voltage = (voltage / 100) - 18
|
||||
}
|
||||
|
||||
switch number {
|
||||
case 2:
|
||||
v := d.GetLDO23VoltageSet()
|
||||
d.SetLDO23VoltageSet((v & 0x0F) | (uint8(voltage) << 4))
|
||||
break
|
||||
case 3:
|
||||
v := d.GetLDO23VoltageSet()
|
||||
d.SetLDO23VoltageSet((v & 0xF0) | uint8(voltage))
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// SetLDOEnable enable LDO.
|
||||
func (d *Device) SetLDOEnable(number uint8, state bool) {
|
||||
mark := uint8(0x01)
|
||||
mark <<= number
|
||||
switch number {
|
||||
case 2:
|
||||
v := d.GetDCDC13LDO23Switch()
|
||||
d.SetDCDC13LDO23Switch(v | mark)
|
||||
case 3:
|
||||
v := d.GetDCDC13LDO23Switch()
|
||||
d.SetDCDC13LDO23Switch(v & (^mark))
|
||||
}
|
||||
}
|
||||
|
||||
func (d *Device) write1Byte(reg, data uint8) {
|
||||
legacy.WriteRegister(d.bus, d.Address, reg, []byte{data})
|
||||
}
|
||||
|
||||
func (d *Device) read8bit(reg uint8) uint8 {
|
||||
legacy.ReadRegister(d.bus, d.Address, reg, d.buf[:1])
|
||||
return d.buf[0]
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
package axp192
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
axp192orig "tinygo.org/x/drivers/axp192"
|
||||
)
|
||||
|
||||
// Device wraps an I2C connection to a AXP192 device.
|
||||
type Device struct {
|
||||
*axp192orig.Device
|
||||
LED Pin
|
||||
RST Pin
|
||||
SPK_EN Pin
|
||||
}
|
||||
|
||||
// New creates a new AXP192 connection. The I2C bus must already be
|
||||
// configured.
|
||||
//
|
||||
// This function only creates the Device object, it does not touch the device.
|
||||
func New(i2c drivers.I2C) *Device {
|
||||
d := axp192orig.New(i2c)
|
||||
|
||||
axp := &Device{
|
||||
Device: d,
|
||||
}
|
||||
axp.LED = Pin{pin: 1, axp: axp}
|
||||
axp.SPK_EN = Pin{pin: 2, axp: axp}
|
||||
axp.RST = Pin{pin: 4, axp: axp}
|
||||
|
||||
axp.begin()
|
||||
|
||||
return axp
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
}
|
||||
|
||||
// Configure sets up the device for communication
|
||||
func (d *Device) Configure(config Config) error {
|
||||
return d.Device.Configure(axp192orig.Config{})
|
||||
}
|
||||
|
||||
func (d *Device) begin() {
|
||||
d.SetVbusIPSOutAccessManagement((d.GetVbusIPSOutAccessManagement() & 0x04) | 0x02)
|
||||
d.SetGPIO1Control(d.GetGPIO1Control() & 0xF8)
|
||||
d.SetGPIO2Control(d.GetGPIO2Control() & 0xF8)
|
||||
d.SetBackupBatteryChargingControl((d.GetBackupBatteryChargingControl() & 0x1C) | 0xA2)
|
||||
d.SetESPVoltage(3350)
|
||||
d.SetLcdVoltage(3300)
|
||||
d.SetLDOVoltage(2, 3300) //Periph power voltage preset (LCD_logic, SD card)
|
||||
d.SetLDOVoltage(3, 2000) //Vibrator power voltage preset
|
||||
|
||||
d.SetLDOEnable(2, true)
|
||||
d.SetDCDC3(true) // LCD Backlight
|
||||
// GPIO4 : LCD Reset
|
||||
d.SetGPIO43FunctionControl((d.GetGPIO43FunctionControl() & 0x72) | 0x84)
|
||||
// Power On/Off Setting
|
||||
d.SetPEKParameterSet(0x4C)
|
||||
d.SetADCEnableSet(0xFF)
|
||||
|
||||
d.RST.Low()
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
d.RST.High()
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
}
|
||||
|
||||
// ToggleLED toggles LED connected to AXP192.
|
||||
func (d *Device) ToggleLED() {
|
||||
v := d.GetGPIO20SignalStatus()
|
||||
if (v & 0x02) > 0 {
|
||||
d.SetGPIO20SignalStatus(v & 0xFD)
|
||||
} else {
|
||||
d.SetGPIO20SignalStatus(v | 0x02)
|
||||
}
|
||||
}
|
||||
|
||||
// SetESPVoltage sets voltage of ESP32.
|
||||
func (d *Device) SetESPVoltage(voltage uint16) {
|
||||
if voltage >= 3000 && voltage <= 3400 {
|
||||
d.SetDCVoltage(0, voltage)
|
||||
}
|
||||
}
|
||||
|
||||
// SetLcdVoltage sets voltage of LCD.
|
||||
func (d *Device) SetLcdVoltage(voltage uint16) {
|
||||
if voltage >= 2500 && voltage <= 3300 {
|
||||
d.SetDCVoltage(2, voltage)
|
||||
}
|
||||
}
|
||||
|
||||
// SetDCDC3 enables or disables DCDC3.
|
||||
func (d *Device) SetDCDC3(State bool) {
|
||||
v := d.GetDCDC13LDO23Switch()
|
||||
if State == true {
|
||||
v = (1 << 1) | v
|
||||
} else {
|
||||
v = ^(uint8(1) << 1) & v
|
||||
}
|
||||
d.SetDCDC13LDO23Switch(v)
|
||||
}
|
||||
|
||||
// Pin is a single pin on AXP192.
|
||||
type Pin struct {
|
||||
pin uint8
|
||||
axp *Device
|
||||
}
|
||||
|
||||
// High sets this GPIO pin to high.
|
||||
func (p Pin) High() {
|
||||
switch p.pin {
|
||||
case 1: // LED
|
||||
v := p.axp.GetGPIO20SignalStatus()
|
||||
p.axp.SetGPIO20SignalStatus(v | 0x02)
|
||||
case 2: // SPK_EN
|
||||
case 4: // RST
|
||||
v := p.axp.GetGPIO43SignalStatus()
|
||||
v |= uint8(0x02)
|
||||
p.axp.SetGPIO43SignalStatus(v)
|
||||
}
|
||||
}
|
||||
|
||||
// Low sets this GPIO pin to low.
|
||||
func (p Pin) Low() {
|
||||
switch p.pin {
|
||||
case 1: // LED
|
||||
v := p.axp.GetGPIO20SignalStatus()
|
||||
p.axp.SetGPIO20SignalStatus(v & 0xFD)
|
||||
case 2: // SPK_EN
|
||||
case 4: // RST
|
||||
v := p.axp.GetGPIO43SignalStatus()
|
||||
v &= ^uint8(0x02)
|
||||
p.axp.SetGPIO43SignalStatus(v)
|
||||
}
|
||||
}
|
||||
|
||||
// Toggle switches an output pin from low to high or from high to low.
|
||||
func (p Pin) Toggle() {
|
||||
switch p.pin {
|
||||
case 1: // LED
|
||||
v := p.axp.GetGPIO20SignalStatus()
|
||||
if (v & 0x02) == 0 {
|
||||
p.axp.SetGPIO20SignalStatus(v | 0x02)
|
||||
} else {
|
||||
p.axp.SetGPIO20SignalStatus(v & 0xFD)
|
||||
}
|
||||
case 2: // SPK_EN
|
||||
case 4: // RST
|
||||
v := p.axp.GetGPIO43SignalStatus()
|
||||
if (v & 0x02) == 0 {
|
||||
v |= uint8(0x02)
|
||||
} else {
|
||||
v &= ^uint8(0x02)
|
||||
}
|
||||
p.axp.SetGPIO43SignalStatus(v)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
package axp192
|
||||
|
||||
// power supply control class
|
||||
// 0x00 Power supply status register
|
||||
// 0x01 Power supply mode/charging status register
|
||||
// 0x04 OTG VBUS status register
|
||||
// 0x06‐09 Data buffer register
|
||||
// 0x10 EXTEN & DC‐DC2 switch register
|
||||
// 0x12 DC‐DC1/3 & LDO2/3switch register
|
||||
// 0x23 DC‐DC2 voltage set register
|
||||
// 0x25 DC‐DC2 voltage slope set register
|
||||
// 0x26 DC‐DC1voltage set register
|
||||
// 0x27 DC‐DC3 voltage set register
|
||||
// 0x28 LDO2/3 voltage set register
|
||||
// 0x30 VBUS‐IPSOUT access set register
|
||||
// 0x31 VOFF power off voltage set register
|
||||
// 0x32 Power off、battery detect、CHGLED control register
|
||||
// 0x33 Charging control register1
|
||||
// 0x34 Charging control register2
|
||||
// 0x35 Backup battery charging control register
|
||||
// 0x36 PEK parameter set register
|
||||
// 0x37 DCDC switch frequency set register
|
||||
// 0x38 Battery charging under temperature warning set register
|
||||
// 0x39 Battery charging over temperature warning set register
|
||||
// 0x3A APS under voltage Level1 set register
|
||||
// 0x3B APS under voltage Level2 set register
|
||||
// 0x3C Battery discharging under temperature warning set register
|
||||
// 0x3D Battery discharging over temperature warning set register
|
||||
// 0x80 DCDC mode set register
|
||||
// 0x82 ADC enable set register 1
|
||||
// 0x83 ADC enable set register 2
|
||||
// 0x84 ADC sample frequency set, TS pin control register
|
||||
// 0x85 GPIO [3:0] input range set register
|
||||
// 0x8A Timer control register
|
||||
// 0x8B VBUS monitor set register
|
||||
// 0x8F Over temperature power off control register
|
||||
|
||||
// GPIO control class
|
||||
// 0x90 GPIO0 control register
|
||||
// 0x91 GPIO0 LDO mode output voltage set register
|
||||
// 0x92 GPIO1 control register
|
||||
// 0x93 GPIO2 control register
|
||||
// 0x94 GPIO[2:0] signal status register
|
||||
// 0x95 GPIO[4:3] function control register
|
||||
// 0x96 GPIO[4:3] signal status register
|
||||
// 0x97 GPIO[2:0] pull down control register
|
||||
// 0x98 PWM1 frequency set register
|
||||
// 0x99 PWM1 duty ratio set register 1
|
||||
// 0x9A PWM1 duty ratio set register 2
|
||||
// 0x9B PWM2 frequency set register
|
||||
// 0x9C PWM2 duty ratio set register 1
|
||||
// 0x9D PWM2 duty ratio set register 2
|
||||
// 0x9E GPIO5 control register
|
||||
|
||||
// IRQ control class
|
||||
// 0x40 IRQ enable control register 1
|
||||
// 0x41 IRQ enable control register 2
|
||||
// 0x42 IRQ enable control register 3
|
||||
// 0x43 IRQ enable control register 4
|
||||
// 0x44 IRQ status register 1
|
||||
// 0x45 IRQ status register 2
|
||||
// 0x46 IRQ status register 3
|
||||
// 0x47 IRQ status register 4
|
||||
|
||||
// ADC data class
|
||||
// 0x56 ACIN voltage ADC data high 8 bit
|
||||
// 0x57 ACIN voltage ADC data low 4 bit
|
||||
// 0x58 ACIN current ADC data high 8 bit
|
||||
// 0x59 ACIN current ADC data low 4 bit
|
||||
// 0x5A VBUS voltage ADC data high 8 bit
|
||||
// 0x5B VBUS voltage ADC data low 4 bit
|
||||
// 0x5C VBUS current ADC data high 8 bit
|
||||
// 0x5D VBUS current ADC data low 4 bit
|
||||
// 0x5E AXP192 internal temperature monitor ADC data High 8 bit
|
||||
// 0x5F AXP192 internal temperature monitor ADC data low 4 bit
|
||||
// 0x62 TS input ADC data High 8 bit,monitor battery temperature by default
|
||||
// 0x63 TS input ADC data low 4 bit,monitor battery temperature by default
|
||||
// 0x64 GPIO0 voltage ADC data high 8 bit
|
||||
// 0x65 GPIO0 voltage ADC data low 4 bit
|
||||
// 0x66 GPIO1 voltage ADC data high 8 bit
|
||||
// 0x67 GPIO1 voltage ADC data low 4 bit
|
||||
// 0x68 GPIO2 voltage ADC data high 8 bit
|
||||
// 0x69 GPIO2 voltage ADC data low 4 bit
|
||||
// 0x6A GPIO[3] voltage ADC data high 8 bit
|
||||
// 0x6B GPIO[3] voltage ADC data low 4 bit
|
||||
// 0x70 Battery instantaneous power high 8 bit
|
||||
// 0x71 Battery instantaneous power middle 8 bit
|
||||
// 0x72 Battery instantaneous power low 8 bit
|
||||
// 0x78 Battery voltage high 8 bit
|
||||
// 0x79 Battery voltage low 4 bit
|
||||
// 0x7A Battery charging current high 8 bit
|
||||
// 0x7B Battery charging current low 5 bit
|
||||
// 0x7C Battery discharging current high 8 bit
|
||||
// 0x7D Battery discharging current low 5 bit
|
||||
// 0x7E APS voltage high 8 bit
|
||||
// 0x7F APS voltage low 4 bit
|
||||
// 0xB0 Battery charging coulomb counter data register 3
|
||||
// 0xB1 Battery charging coulomb counter data register 2
|
||||
// 0xB2 Battery charging coulomb counter data register 1
|
||||
// 0xB3 Battery charging coulomb counter data register 0
|
||||
// 0xB4 Battery discharging coulomb counter data register 3
|
||||
// 0xB5 Battery discharging coulomb counter data register 2
|
||||
// 0xB6 Battery discharging coulomb counter data register 1
|
||||
// 0xB7 Battery discharging coulomb counter data register 0
|
||||
// 0xB8 Coulomb counter control register
|
||||
|
||||
const (
|
||||
// Address is default I2C address.
|
||||
Address = 0x34
|
||||
|
||||
RegPowerSupplyStatus = 0x00
|
||||
RegDCDC13LDO23Switch = 0x12
|
||||
RegVbusIPSOutAccessManagement = 0x30
|
||||
RegBackupBatteryChargingControl = 0x35
|
||||
RegDCDC2VoltageSet = 0x25
|
||||
RegDCDC1VoltageSet = 0x26
|
||||
RegDCDC3VoltageSet = 0x27
|
||||
RegLDO23VoltageSet = 0x28
|
||||
RegPEKParameterSet = 0x36
|
||||
RegADCEnableSet = 0x82
|
||||
|
||||
RegGPIO1Control = 0x92
|
||||
RegGPIO2Control = 0x93
|
||||
RegGPIO20SignalStatus = 0x94
|
||||
RegGPIO43FunctionControl = 0x95
|
||||
RegGPIO43SignalStatus = 0x96
|
||||
)
|
||||
@@ -2,7 +2,6 @@
|
||||
//
|
||||
// Datasheet:
|
||||
// https://www.mouser.com/ds/2/348/bh1750fvi-e-186247.pdf
|
||||
//
|
||||
package bh1750 // import "tinygo.org/x/drivers/bh1750"
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// Package blinkm implements a driver for the BlinkM I2C RGB LED.
|
||||
//
|
||||
// Datasheet: http://thingm.com/fileadmin/thingm/downloads/BlinkM_datasheet.pdf
|
||||
//
|
||||
package blinkm // import "tinygo.org/x/drivers/blinkm"
|
||||
|
||||
import "tinygo.org/x/drivers"
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,352 @@
|
||||
// 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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
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
|
||||
)
|
||||
+124
-13
@@ -3,13 +3,14 @@
|
||||
//
|
||||
// Datasheet:
|
||||
// https://cdn-shop.adafruit.com/datasheets/BST-BME280_DS001-10.pdf
|
||||
//
|
||||
package bme280
|
||||
|
||||
import (
|
||||
"math"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
"tinygo.org/x/drivers/internal/legacy"
|
||||
)
|
||||
|
||||
// calibrationCoefficients reads at startup and stores the calibration coefficients
|
||||
@@ -34,11 +35,27 @@ type calibrationCoefficients struct {
|
||||
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.
|
||||
type Device struct {
|
||||
bus drivers.I2C
|
||||
Address uint16
|
||||
calibrationCoefficients calibrationCoefficients
|
||||
Config Config
|
||||
}
|
||||
|
||||
// New creates a new BME280 connection. The I2C bus must already be
|
||||
@@ -52,24 +69,49 @@ func New(bus drivers.I2C) Device {
|
||||
}
|
||||
}
|
||||
|
||||
// Configure sets up the device for communication and
|
||||
// read the calibration coefficientes.
|
||||
// ConfigureWithSettings sets up the device for communication and
|
||||
// read the calibration coefficients.
|
||||
//
|
||||
// The default configuration is the Indoor Navigation settings
|
||||
// from the BME280 datasheet.
|
||||
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
|
||||
err := d.bus.ReadRegister(uint8(d.Address), REG_CALIBRATION, data[:])
|
||||
err := legacy.ReadRegister(d.bus, uint8(d.Address), REG_CALIBRATION, data[:])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var h1 [1]byte
|
||||
err = d.bus.ReadRegister(uint8(d.Address), REG_CALIBRATION_H1, h1[:])
|
||||
err = legacy.ReadRegister(d.bus, uint8(d.Address), REG_CALIBRATION_H1, h1[:])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var h2lsb [7]byte
|
||||
err = d.bus.ReadRegister(uint8(d.Address), REG_CALIBRATION_H2LSB, h2lsb[:])
|
||||
err = legacy.ReadRegister(d.bus, uint8(d.Address), REG_CALIBRATION_H2LSB, h2lsb[:])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -94,23 +136,45 @@ func (d *Device) Configure() {
|
||||
d.calibrationCoefficients.h4 = 0 + (int16(h2lsb[3]) << 4) | (int16(h2lsb[4] & 0x0F))
|
||||
d.calibrationCoefficients.h5 = 0 + (int16(h2lsb[5]) << 4) | (int16(h2lsb[4]) >> 4)
|
||||
|
||||
d.bus.WriteRegister(uint8(d.Address), CTRL_HUMIDITY_ADDR, []byte{0x3f})
|
||||
d.bus.WriteRegister(uint8(d.Address), CTRL_MEAS_ADDR, []byte{0xB7})
|
||||
d.bus.WriteRegister(uint8(d.Address), CTRL_CONFIG, []byte{0x00})
|
||||
d.Reset()
|
||||
|
||||
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.
|
||||
// It does a "who am I" request and checks the response.
|
||||
func (d *Device) Connected() bool {
|
||||
data := []byte{0}
|
||||
d.bus.ReadRegister(uint8(d.Address), WHO_AM_I, data)
|
||||
legacy.ReadRegister(d.bus, uint8(d.Address), WHO_AM_I, data)
|
||||
return data[0] == CHIP_ID
|
||||
}
|
||||
|
||||
// Reset the device
|
||||
func (d *Device) Reset() {
|
||||
d.bus.WriteRegister(uint8(d.Address), CMD_RESET, []byte{0xB6})
|
||||
legacy.WriteRegister(d.bus, 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)
|
||||
@@ -149,7 +213,8 @@ func (d *Device) ReadHumidity() (int32, error) {
|
||||
// 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
|
||||
//
|
||||
// https://github.com/adafruit/Adafruit_BME280_Library
|
||||
func (d *Device) ReadAltitude() (alt int32, err error) {
|
||||
mPa, _ := d.ReadPressure()
|
||||
atmP := float32(mPa) / 100000
|
||||
@@ -186,7 +251,17 @@ func readIntLE(msb byte, lsb byte) int16 {
|
||||
// readData does a burst read from 0xF7 to 0xF0 according to the datasheet
|
||||
// resulting in an slice with 8 bytes 0-2 = pressure / 3-5 = temperature / 6-7 = humidity
|
||||
func (d *Device) readData() (data [8]byte, err error) {
|
||||
err = d.bus.ReadRegister(uint8(d.Address), REG_PRESSURE, data[:])
|
||||
if d.Config.Mode == ModeForced {
|
||||
// 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 {
|
||||
println(err)
|
||||
return
|
||||
@@ -256,3 +331,39 @@ func (d *Device) calculateHumidity(data [8]byte, tFine int32) int32 {
|
||||
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,6 +20,50 @@ const (
|
||||
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 (
|
||||
SEALEVEL_PRESSURE float32 = 1013.25 // in hPa
|
||||
)
|
||||
|
||||
+23
-7
@@ -3,13 +3,14 @@
|
||||
//
|
||||
// Datasheet:
|
||||
// https://cdn-shop.adafruit.com/datasheets/BST-BMP180-DS000-09.pdf
|
||||
//
|
||||
package bmp180 // import "tinygo.org/x/drivers/bmp180"
|
||||
|
||||
import (
|
||||
"math"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
"tinygo.org/x/drivers/internal/legacy"
|
||||
)
|
||||
|
||||
// OversamplingMode is the oversampling ratio of the pressure measurement.
|
||||
@@ -55,7 +56,7 @@ func New(bus drivers.I2C) Device {
|
||||
// It does a "who am I" request and checks the response.
|
||||
func (d *Device) Connected() bool {
|
||||
data := []byte{0}
|
||||
d.bus.ReadRegister(uint8(d.Address), WHO_AM_I, data)
|
||||
legacy.ReadRegister(d.bus, uint8(d.Address), WHO_AM_I, data)
|
||||
return data[0] == CHIP_ID
|
||||
}
|
||||
|
||||
@@ -63,7 +64,7 @@ func (d *Device) Connected() bool {
|
||||
// read the calibration coefficients.
|
||||
func (d *Device) Configure() {
|
||||
data := make([]byte, 22)
|
||||
err := d.bus.ReadRegister(uint8(d.Address), AC1_MSB, data)
|
||||
err := legacy.ReadRegister(d.bus, uint8(d.Address), AC1_MSB, data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -124,12 +125,27 @@ func (d *Device) ReadPressure() (pressure int32, err error) {
|
||||
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
|
||||
func (d *Device) rawTemp() (int32, error) {
|
||||
d.bus.WriteRegister(uint8(d.Address), REG_CTRL, []byte{CMD_TEMP})
|
||||
legacy.WriteRegister(d.bus, uint8(d.Address), REG_CTRL, []byte{CMD_TEMP})
|
||||
time.Sleep(5 * time.Millisecond)
|
||||
data := make([]byte, 2)
|
||||
err := d.bus.ReadRegister(uint8(d.Address), REG_TEMP_MSB, data)
|
||||
err := legacy.ReadRegister(d.bus, uint8(d.Address), REG_TEMP_MSB, data)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@@ -145,10 +161,10 @@ func (d *Device) calculateB5(rawTemp int32) int32 {
|
||||
|
||||
// rawPressure returns the sensor's raw values of the pressure
|
||||
func (d *Device) rawPressure(mode OversamplingMode) (int32, error) {
|
||||
d.bus.WriteRegister(uint8(d.Address), REG_CTRL, []byte{CMD_PRESSURE + byte(mode<<6)})
|
||||
legacy.WriteRegister(d.bus, uint8(d.Address), REG_CTRL, []byte{CMD_PRESSURE + byte(mode<<6)})
|
||||
time.Sleep(pauseForReading(mode))
|
||||
data := make([]byte, 3)
|
||||
err := d.bus.ReadRegister(uint8(d.Address), REG_PRESSURE_MSB, data)
|
||||
err := legacy.ReadRegister(d.bus, uint8(d.Address), REG_PRESSURE_MSB, data)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
@@ -28,3 +28,7 @@ const (
|
||||
// ULTRAHIGHRESOLUTION is the highest oversampling mode of the pressure measurement.
|
||||
ULTRAHIGHRESOLUTION
|
||||
)
|
||||
|
||||
const (
|
||||
SEALEVEL_PRESSURE float32 = 1013.25 // in hPa
|
||||
)
|
||||
|
||||
+9
-8
@@ -4,6 +4,7 @@ import (
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
"tinygo.org/x/drivers/internal/legacy"
|
||||
)
|
||||
|
||||
// OversamplingMode is the oversampling ratio of the temperature or pressure measurement.
|
||||
@@ -64,14 +65,14 @@ func New(bus drivers.I2C) Device {
|
||||
// It does a "who am I" request and checks the response.
|
||||
func (d *Device) Connected() bool {
|
||||
data := make([]byte, 1)
|
||||
d.bus.ReadRegister(uint8(d.Address), REG_ID, data)
|
||||
legacy.ReadRegister(d.bus, uint8(d.Address), REG_ID, data)
|
||||
return data[0] == CHIP_ID
|
||||
}
|
||||
|
||||
// Reset preforms complete power-on-reset procedure.
|
||||
// It is required to call Configure afterwards.
|
||||
func (d *Device) Reset() {
|
||||
d.bus.WriteRegister(uint8(d.Address), REG_RESET, []byte{CMD_RESET})
|
||||
legacy.WriteRegister(d.bus, uint8(d.Address), REG_RESET, []byte{CMD_RESET})
|
||||
}
|
||||
|
||||
// Configure sets up the device for communication and
|
||||
@@ -85,15 +86,15 @@ func (d *Device) Configure(standby Standby, filter Filter, temp Oversampling, pr
|
||||
|
||||
// Write the configuration (standby, filter, spi 3 wire)
|
||||
config := uint(d.Standby<<5) | uint(d.Filter<<2) | 0x00
|
||||
d.bus.WriteRegister(uint8(d.Address), REG_CONFIG, []byte{byte(config)})
|
||||
legacy.WriteRegister(d.bus, uint8(d.Address), REG_CONFIG, []byte{byte(config)})
|
||||
|
||||
// Write the control (temperature oversampling, pressure oversampling,
|
||||
config = uint(d.Temperature<<5) | uint(d.Pressure<<2) | uint(d.Mode)
|
||||
d.bus.WriteRegister(uint8(d.Address), REG_CTRL_MEAS, []byte{byte(config)})
|
||||
legacy.WriteRegister(d.bus, uint8(d.Address), REG_CTRL_MEAS, []byte{byte(config)})
|
||||
|
||||
// Read Calibration data
|
||||
data := make([]byte, 24)
|
||||
err := d.bus.ReadRegister(uint8(d.Address), REG_CALI, data)
|
||||
err := legacy.ReadRegister(d.bus, uint8(d.Address), REG_CALI, data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -207,18 +208,18 @@ func (d *Device) readData(register int, n int) ([]byte, error) {
|
||||
// After the measurement in FORCED mode, the sensor will return to SLEEP mode
|
||||
if d.Mode != MODE_NORMAL {
|
||||
config := uint(d.Temperature<<5) | uint(d.Pressure<<2) | uint(MODE_FORCED)
|
||||
d.bus.WriteRegister(uint8(d.Address), REG_CTRL_MEAS, []byte{byte(config)})
|
||||
legacy.WriteRegister(d.bus, uint8(d.Address), REG_CTRL_MEAS, []byte{byte(config)})
|
||||
}
|
||||
|
||||
// Check STATUS register, wait if data is not available yet
|
||||
status := make([]byte, 1)
|
||||
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:]) {
|
||||
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:]) {
|
||||
time.Sleep(time.Millisecond)
|
||||
}
|
||||
|
||||
// Read the requested register
|
||||
data := make([]byte, n)
|
||||
err := d.bus.ReadRegister(uint8(d.Address), uint8(register), data[:])
|
||||
err := legacy.ReadRegister(d.bus, uint8(d.Address), uint8(register), data[:])
|
||||
return data, err
|
||||
}
|
||||
|
||||
|
||||
+3
-2
@@ -4,6 +4,7 @@ import (
|
||||
"errors"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
"tinygo.org/x/drivers/internal/legacy"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -240,10 +241,10 @@ func (d *Device) configurationError() bool {
|
||||
|
||||
func (d *Device) readRegister(register byte, len int) (data []byte, err error) {
|
||||
data = make([]byte, len)
|
||||
err = d.bus.ReadRegister(d.Address, register, data)
|
||||
err = legacy.ReadRegister(d.bus, d.Address, register, data)
|
||||
return
|
||||
}
|
||||
|
||||
func (d *Device) writeRegister(register byte, data byte) error {
|
||||
return d.bus.WriteRegister(d.Address, register, []byte{data})
|
||||
return legacy.WriteRegister(d.bus, d.Address, register, []byte{data})
|
||||
}
|
||||
|
||||
+6
-1
@@ -1,5 +1,4 @@
|
||||
// Package buzzer provides a very simplistic driver for a connected buzzer or low-fidelity speaker.
|
||||
//
|
||||
package buzzer // import "tinygo.org/x/drivers/buzzer"
|
||||
|
||||
import (
|
||||
@@ -55,6 +54,12 @@ func (l *Device) Tone(hz, duration float64) (err error) {
|
||||
|
||||
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 {
|
||||
if err = l.On(); err != nil {
|
||||
return
|
||||
|
||||
+3
-3
@@ -1,9 +1,9 @@
|
||||
package buzzer
|
||||
|
||||
const (
|
||||
Whole = 4
|
||||
Half = 2
|
||||
Quarter = 1
|
||||
Whole = 4.0
|
||||
Half = 2.0
|
||||
Quarter = 1.0
|
||||
Eighth = 0.500
|
||||
)
|
||||
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// See ../../image/README.md for the usage.
|
||||
|
||||
func main() {
|
||||
err := run(os.Args)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func run(args []string) error {
|
||||
if len(args) < 2 {
|
||||
return fmt.Errorf("usage: %s FILE")
|
||||
}
|
||||
|
||||
b, err := ioutil.ReadFile(args[1])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("const %s = \"\" +\n", strings.Replace(args[1], ".", "_", -1))
|
||||
|
||||
i := 0
|
||||
max := 32
|
||||
for i = 0; i < len(b); i++ {
|
||||
bb := b[i]
|
||||
if (i % max) == 0 {
|
||||
fmt.Printf(" \"")
|
||||
}
|
||||
fmt.Printf("\\x%02X", bb)
|
||||
if (i%max) == max-1 && i != len(b)-1 {
|
||||
fmt.Printf("\" + \n")
|
||||
}
|
||||
}
|
||||
if (i % max) < max-1 {
|
||||
fmt.Printf("\"\n")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
#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
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
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)
|
||||
}
|
||||
}
|
||||
+2
-27
@@ -1,3 +1,5 @@
|
||||
//go:build tinygo
|
||||
|
||||
// Package dht provides a driver for DHTXX family temperature and humidity sensors.
|
||||
//
|
||||
// [1] Datasheet DHT11: https://www.mouser.com/datasheet/2/758/DHT11-Technical-Data-Sheet-Translated-Version-1143054.pdf
|
||||
@@ -7,34 +9,10 @@
|
||||
package dht // import "tinygo.org/x/drivers/dht"
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"machine"
|
||||
"time"
|
||||
)
|
||||
|
||||
// enum type for device type
|
||||
type DeviceType uint8
|
||||
|
||||
// DeviceType specific parsing of information received from the sensor
|
||||
func (d DeviceType) extractData(buf []byte) (temp int16, hum uint16) {
|
||||
if d == DHT11 {
|
||||
temp = int16(buf[2])
|
||||
if buf[3]&0x80 > 0 {
|
||||
temp = -1 - temp
|
||||
}
|
||||
temp *= 10
|
||||
temp += int16(buf[3] & 0x0f)
|
||||
hum = 10*uint16(buf[0]) + uint16(buf[1])
|
||||
} else {
|
||||
hum = binary.LittleEndian.Uint16(buf[0:2])
|
||||
temp = int16(buf[3])<<8 + int16(buf[2]&0x7f)
|
||||
if buf[2]&0x80 > 0 {
|
||||
temp = -temp
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Celsius and Fahrenheit temperature scales
|
||||
type TemperatureScale uint8
|
||||
|
||||
@@ -54,9 +32,6 @@ const (
|
||||
startTimeout = time.Millisecond * 200
|
||||
startingLow = time.Millisecond * 20
|
||||
|
||||
DHT11 DeviceType = iota
|
||||
DHT22
|
||||
|
||||
C TemperatureScale = iota
|
||||
F
|
||||
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
package dht
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
)
|
||||
|
||||
// DeviceType is the enum type for device type
|
||||
type DeviceType uint8
|
||||
|
||||
const (
|
||||
DHT11 DeviceType = iota
|
||||
DHT22
|
||||
)
|
||||
|
||||
// extractData parses information received from the sensor.
|
||||
// The 2 first buffers are for the humidity and
|
||||
// the 2 following corresponds to the temperature.
|
||||
func (d DeviceType) extractData(buf []byte) (temp int16, hum uint16) {
|
||||
switch d {
|
||||
case DHT11:
|
||||
hum = 10*uint16(buf[0]) + uint16(buf[1])
|
||||
temp = int16(buf[2])
|
||||
if buf[3]&0x80 > 0 {
|
||||
temp = -1 - temp
|
||||
}
|
||||
temp *= 10
|
||||
temp += int16(buf[3] & 0x0f)
|
||||
case DHT22:
|
||||
hum = binary.BigEndian.Uint16(buf[0:2])
|
||||
temp = int16(buf[2]&0x7f)<<8 + int16(buf[3])
|
||||
// the first bit corresponds to the sign bit
|
||||
if buf[2]&0x80 > 0 {
|
||||
temp = -temp
|
||||
}
|
||||
default:
|
||||
// keeping this for retro-compatibility but not tested
|
||||
hum = binary.LittleEndian.Uint16(buf[0:2])
|
||||
temp = int16(buf[3])<<8 + int16(buf[2]&0x7f)
|
||||
if buf[2]&0x80 > 0 {
|
||||
temp = -temp
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package dht
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestDeviceType_extractData(t *testing.T) {
|
||||
bitStr := "0000001010001100000000010101111111101110"
|
||||
buf := bitStringToBytes(bitStr)
|
||||
|
||||
tt := []struct {
|
||||
name string
|
||||
d DeviceType
|
||||
buf []byte
|
||||
wantTemp int16
|
||||
wantHum uint16
|
||||
}{
|
||||
{
|
||||
// temp = 35.1C hum = 65.2%
|
||||
name: "DHT22", d: DHT22, buf: buf, wantTemp: 351, wantHum: 652,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tt {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
gotTemp, gotHum := tc.d.extractData(tc.buf)
|
||||
if gotTemp != tc.wantTemp {
|
||||
t.Errorf("extractData() gotTemp = %v, want %v", gotTemp, tc.wantTemp)
|
||||
}
|
||||
if gotHum != tc.wantHum {
|
||||
t.Errorf("extractData() gotHum = %v, want %v", gotHum, tc.wantHum)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func bitStringToBytes(s string) []byte {
|
||||
b := make([]byte, (len(s)+(8-1))/8)
|
||||
for i, r := range s {
|
||||
if r < '0' || r > '1' {
|
||||
panic("not in range")
|
||||
}
|
||||
b[i>>3] |= byte(r-'0') << uint(7-i&7)
|
||||
}
|
||||
return b
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// +build mimxrt1062 stm32f405 atsamd51 stm32f103xx k210 stm32f407
|
||||
//go:build mimxrt1062 || stm32f405 || atsamd51 || stm32f103xx || k210 || stm32f407
|
||||
|
||||
package dht // import "tinygo.org/x/drivers/dht"
|
||||
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// +build arduino atmega1284p nrf52840 digispark nrf52 arduino_nano nrf51 atsamd21 fe310 arduino_nano33 circuitplay_express arduino_mega2560
|
||||
//go:build !mimxrt1062 && !stm32f405 && !atsamd51 && !stm32f103xx && !k210 && !stm32f407
|
||||
|
||||
package dht // import "tinygo.org/x/drivers/dht"
|
||||
|
||||
|
||||
+5
-2
@@ -1,3 +1,5 @@
|
||||
//go:build tinygo
|
||||
|
||||
// Package dht provides a driver for DHTXX family temperature and humidity sensors.
|
||||
//
|
||||
// [1] Datasheet DHT11: https://www.mouser.com/datasheet/2/758/DHT11-Technical-Data-Sheet-Translated-Version-1143054.pdf
|
||||
@@ -8,6 +10,7 @@ package dht // import "tinygo.org/x/drivers/dht"
|
||||
|
||||
import (
|
||||
"machine"
|
||||
"runtime/interrupt"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -157,8 +160,8 @@ func (t *device) read() error {
|
||||
// interrupts
|
||||
func receiveSignals(pin machine.Pin, result []counter) {
|
||||
i := uint8(0)
|
||||
machine.UART1.Interrupt.Disable()
|
||||
defer machine.UART1.Interrupt.Enable()
|
||||
mask := interrupt.Disable()
|
||||
defer interrupt.Restore(mask)
|
||||
for ; i < 40; i++ {
|
||||
result[i*2] = expectChange(pin, false)
|
||||
result[i*2+1] = expectChange(pin, true)
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
//go:build tinygo
|
||||
|
||||
// Package dht provides a driver for DHTXX family temperature and humidity sensors.
|
||||
//
|
||||
// [1] Datasheet DHT11: https://www.mouser.com/datasheet/2/758/DHT11-Technical-Data-Sheet-Translated-Version-1143054.pdf
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
//go:build tinygo
|
||||
|
||||
package dht // import "tinygo.org/x/drivers/dht"
|
||||
|
||||
import (
|
||||
|
||||
@@ -12,3 +12,19 @@ type Displayer interface {
|
||||
// Display sends the buffer (if any) to the screen.
|
||||
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
|
||||
)
|
||||
|
||||
+7
-8
@@ -3,22 +3,22 @@
|
||||
//
|
||||
// Here is an example in TinyGo that uses the BMP180 digital barometer:
|
||||
//
|
||||
// package main
|
||||
// package main
|
||||
//
|
||||
// import (
|
||||
// import (
|
||||
// "time"
|
||||
// "machine"
|
||||
//
|
||||
// "tinygo.org/x/drivers/bmp180"
|
||||
// )
|
||||
// "tinygo.org/x/drivers/bmp180"
|
||||
// )
|
||||
//
|
||||
// func main() {
|
||||
// func main() {
|
||||
// machine.I2C0.Configure(machine.I2CConfig{})
|
||||
// sensor := bmp180.New(machine.I2C0)
|
||||
// sensor.Configure()
|
||||
//
|
||||
// connected := sensor.Connected()
|
||||
// if !connected {
|
||||
// connected := sensor.Connected()
|
||||
// if !connected {
|
||||
// println("BMP180 not detected")
|
||||
// return
|
||||
// }
|
||||
@@ -38,5 +38,4 @@
|
||||
// Each individual driver is contained within its own sub-package within this package and
|
||||
// there are no interdependencies in order to minimize the final size of compiled code that
|
||||
// uses any of these drivers.
|
||||
//
|
||||
package drivers // import "tinygo.org/x/drivers"
|
||||
|
||||
+5
-5
@@ -2,7 +2,6 @@
|
||||
//
|
||||
// Datasheet:
|
||||
// https://datasheets.maximintegrated.com/en/ds/DS1307.pdf
|
||||
//
|
||||
package ds1307 // import "tinygo.org/x/drivers/ds1307"
|
||||
|
||||
import (
|
||||
@@ -10,6 +9,7 @@ import (
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
"tinygo.org/x/drivers/internal/legacy"
|
||||
)
|
||||
|
||||
// Device wraps an I2C connection to a DS1307 device.
|
||||
@@ -45,7 +45,7 @@ func (d *Device) SetTime(t time.Time) error {
|
||||
// ReadTime returns the date and time
|
||||
func (d *Device) ReadTime() (time.Time, error) {
|
||||
data := make([]byte, 8)
|
||||
err := d.bus.ReadRegister(d.Address, uint8(TimeDate), data)
|
||||
err := legacy.ReadRegister(d.bus, d.Address, uint8(TimeDate), data)
|
||||
if err != nil {
|
||||
return time.Time{}, err
|
||||
}
|
||||
@@ -106,7 +106,7 @@ func (d *Device) Read(data []uint8) (n int, err error) {
|
||||
if int(d.AddressSRAM)+len(data)-1 > SRAMEndAddress {
|
||||
return 0, errors.New("EOF")
|
||||
}
|
||||
err = d.bus.ReadRegister(d.Address, d.AddressSRAM, data)
|
||||
err = legacy.ReadRegister(d.bus, d.Address, d.AddressSRAM, data)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@@ -125,7 +125,7 @@ func (d *Device) SetOscillatorFrequency(sqw uint8) error {
|
||||
// IsOscillatorRunning returns if the oscillator is running
|
||||
func (d *Device) IsOscillatorRunning() bool {
|
||||
data := []byte{0}
|
||||
err := d.bus.ReadRegister(d.Address, uint8(TimeDate), data)
|
||||
err := legacy.ReadRegister(d.bus, d.Address, uint8(TimeDate), data)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
@@ -135,7 +135,7 @@ func (d *Device) IsOscillatorRunning() bool {
|
||||
// SetOscillatorRunning starts/stops internal oscillator by toggling halt bit
|
||||
func (d *Device) SetOscillatorRunning(running bool) error {
|
||||
data := make([]byte, 3)
|
||||
err := d.bus.ReadRegister(d.Address, uint8(TimeDate), data)
|
||||
err := legacy.ReadRegister(d.bus, d.Address, uint8(TimeDate), data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
// 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
|
||||
}
|
||||
+46
-11
@@ -8,6 +8,7 @@ import (
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
"tinygo.org/x/drivers/internal/legacy"
|
||||
)
|
||||
|
||||
type Mode uint8
|
||||
@@ -37,7 +38,7 @@ func (d *Device) Configure() bool {
|
||||
// IsTimeValid return true/false is the time in the device is valid
|
||||
func (d *Device) IsTimeValid() bool {
|
||||
data := []byte{0}
|
||||
err := d.bus.ReadRegister(uint8(d.Address), REG_STATUS, data)
|
||||
err := legacy.ReadRegister(d.bus, uint8(d.Address), REG_STATUS, data)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
@@ -47,7 +48,7 @@ func (d *Device) IsTimeValid() bool {
|
||||
// IsRunning returns if the oscillator is running
|
||||
func (d *Device) IsRunning() bool {
|
||||
data := []uint8{0}
|
||||
err := d.bus.ReadRegister(uint8(d.Address), REG_CONTROL, data)
|
||||
err := legacy.ReadRegister(d.bus, uint8(d.Address), REG_CONTROL, data)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
@@ -57,7 +58,7 @@ func (d *Device) IsRunning() bool {
|
||||
// SetRunning starts the internal oscillator
|
||||
func (d *Device) SetRunning(isRunning bool) error {
|
||||
data := []uint8{0}
|
||||
err := d.bus.ReadRegister(uint8(d.Address), REG_CONTROL, data)
|
||||
err := legacy.ReadRegister(d.bus, uint8(d.Address), REG_CONTROL, data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -66,22 +67,32 @@ func (d *Device) SetRunning(isRunning bool) error {
|
||||
} else {
|
||||
data[0] |= 1 << EOSC
|
||||
}
|
||||
err = d.bus.WriteRegister(uint8(d.Address), REG_CONTROL, data)
|
||||
err = legacy.WriteRegister(d.bus, uint8(d.Address), REG_CONTROL, data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetTime sets the date and time in the DS3231
|
||||
// SetTime sets the date and time in the DS3231. The DS3231 hardware supports
|
||||
// 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 {
|
||||
data := []byte{0}
|
||||
err := d.bus.ReadRegister(uint8(d.Address), REG_STATUS, data)
|
||||
err := legacy.ReadRegister(d.bus, uint8(d.Address), REG_STATUS, data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
data[0] &^= 1 << OSF
|
||||
err = d.bus.WriteRegister(uint8(d.Address), REG_STATUS, data)
|
||||
err = legacy.WriteRegister(d.bus, uint8(d.Address), REG_STATUS, data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -92,6 +103,10 @@ func (d *Device) SetTime(dt time.Time) error {
|
||||
data[2] = uint8ToBCD(uint8(dt.Hour()))
|
||||
|
||||
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)
|
||||
if year >= 100 {
|
||||
year -= 100
|
||||
@@ -103,7 +118,7 @@ func (d *Device) SetTime(dt time.Time) error {
|
||||
data[5] = uint8ToBCD(uint8(dt.Month()) | centuryFlag)
|
||||
data[6] = uint8ToBCD(year)
|
||||
|
||||
err = d.bus.WriteRegister(uint8(d.Address), REG_TIMEDATE, data)
|
||||
err = legacy.WriteRegister(d.bus, uint8(d.Address), REG_TIMEDATE, data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -114,7 +129,7 @@ func (d *Device) SetTime(dt time.Time) error {
|
||||
// ReadTime returns the date and time
|
||||
func (d *Device) ReadTime() (dt time.Time, err error) {
|
||||
data := make([]uint8, 7)
|
||||
err = d.bus.ReadRegister(uint8(d.Address), REG_TIMEDATE, data)
|
||||
err = legacy.ReadRegister(d.bus, uint8(d.Address), REG_TIMEDATE, data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -136,11 +151,31 @@ func (d *Device) ReadTime() (dt time.Time, err error) {
|
||||
// ReadTemperature returns the temperature in millicelsius (mC)
|
||||
func (d *Device) ReadTemperature() (int32, error) {
|
||||
data := make([]uint8, 2)
|
||||
err := d.bus.ReadRegister(uint8(d.Address), REG_TEMP, data)
|
||||
err := legacy.ReadRegister(d.bus, uint8(d.Address), REG_TEMP, data)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return int32(data[0])*1000 + int32((data[1]>>6)*25)*10, nil
|
||||
return milliCelsius(data[0], data[1]), 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
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
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)
|
||||
}
|
||||
}
|
||||
+138
-22
@@ -2,28 +2,76 @@
|
||||
package easystepper // import "tinygo.org/x/drivers/easystepper"
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"machine"
|
||||
"time"
|
||||
)
|
||||
|
||||
// StepMode determines the coil sequence used to perform a single step
|
||||
type StepMode uint8
|
||||
|
||||
// Valid values for StepMode
|
||||
const (
|
||||
// ModeFour uses a 'four step' coil sequence (12-23-34-41). This is the default (zero-value) mode
|
||||
ModeFour StepMode = iota
|
||||
// ModeEight uses an 'eight step' coil sequence (1-12-2-23-3-34-4-41)
|
||||
ModeEight
|
||||
)
|
||||
|
||||
// stepCount is a helper function to return the number of steps in a StepMode sequence
|
||||
func (sm StepMode) stepCount() uint {
|
||||
switch sm {
|
||||
default:
|
||||
fallthrough
|
||||
case ModeFour:
|
||||
return 4
|
||||
case ModeEight:
|
||||
return 8
|
||||
}
|
||||
}
|
||||
|
||||
// DeviceConfig contains the configuration data for a single easystepper driver
|
||||
type DeviceConfig struct {
|
||||
// Pin1 ... Pin4 determines the pins to configure and use for the device
|
||||
Pin1, Pin2, Pin3, Pin4 machine.Pin
|
||||
// StepCount is the number of steps required to perform a full revolution of the stepper motor
|
||||
StepCount uint
|
||||
// RPM determines the speed of the stepper motor in 'Revolutions per Minute'
|
||||
RPM uint
|
||||
// Mode determines the coil sequence used to perform a single step
|
||||
Mode StepMode
|
||||
}
|
||||
|
||||
// DualDeviceConfig contains the configuration data for a dual easystepper driver
|
||||
type DualDeviceConfig struct {
|
||||
DeviceConfig
|
||||
// Pin5 ... Pin8 determines the pins to configure and use for the second device
|
||||
Pin5, Pin6, Pin7, Pin8 machine.Pin
|
||||
}
|
||||
|
||||
// Device holds the pins and the delay between steps
|
||||
type Device struct {
|
||||
pins [4]machine.Pin
|
||||
stepDelay int32
|
||||
stepDelay time.Duration
|
||||
stepNumber uint8
|
||||
stepMode StepMode
|
||||
}
|
||||
|
||||
// DualDevice holds information for controlling 2 motors
|
||||
type DualDevice struct {
|
||||
devices [2]Device
|
||||
devices [2]*Device
|
||||
}
|
||||
|
||||
// New returns a new easystepper driver given 4 pins, number of steps and rpm
|
||||
func New(pin1, pin2, pin3, pin4 machine.Pin, steps int32, rpm int32) Device {
|
||||
return Device{
|
||||
pins: [4]machine.Pin{pin1, pin2, pin3, pin4},
|
||||
stepDelay: 60000000 / (steps * rpm),
|
||||
// New returns a new single easystepper driver given a DeviceConfig
|
||||
func New(config DeviceConfig) (*Device, error) {
|
||||
if config.StepCount == 0 || config.RPM == 0 {
|
||||
return nil, errors.New("config.StepCount and config.RPM must be > 0")
|
||||
}
|
||||
return &Device{
|
||||
pins: [4]machine.Pin{config.Pin1, config.Pin2, config.Pin3, config.Pin4},
|
||||
stepDelay: time.Second * 60 / time.Duration((config.StepCount * config.RPM)),
|
||||
stepMode: config.Mode,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Configure configures the pins of the Device
|
||||
@@ -34,17 +82,23 @@ func (d *Device) Configure() {
|
||||
}
|
||||
|
||||
// NewDual returns a new dual easystepper driver given 8 pins, number of steps and rpm
|
||||
func NewDual(pin1, pin2, pin3, pin4, pin5, pin6, pin7, pin8 machine.Pin, steps int32, rpm int32) DualDevice {
|
||||
var dual DualDevice
|
||||
dual.devices[0] = Device{
|
||||
pins: [4]machine.Pin{pin1, pin2, pin3, pin4},
|
||||
stepDelay: 60000000 / (steps * rpm),
|
||||
func NewDual(config DualDeviceConfig) (*DualDevice, error) {
|
||||
// Create the first device
|
||||
dev1, err := New(config.DeviceConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dual.devices[1] = Device{
|
||||
pins: [4]machine.Pin{pin5, pin6, pin7, pin8},
|
||||
stepDelay: 60000000 / (steps * rpm),
|
||||
// Create the second device
|
||||
config.DeviceConfig.Pin1 = config.Pin5
|
||||
config.DeviceConfig.Pin2 = config.Pin6
|
||||
config.DeviceConfig.Pin3 = config.Pin7
|
||||
config.DeviceConfig.Pin4 = config.Pin8
|
||||
dev2, err := New(config.DeviceConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return dual
|
||||
// Return composite dual device
|
||||
return &DualDevice{devices: [2]*Device{dev1, dev2}}, nil
|
||||
}
|
||||
|
||||
// Configure configures the pins of the DualDevice
|
||||
@@ -64,7 +118,7 @@ func (d *Device) Move(steps int32) {
|
||||
var s int32
|
||||
d.stepMotor(d.stepNumber)
|
||||
for s = int32(d.stepNumber); s < steps; s++ {
|
||||
time.Sleep(time.Duration(d.stepDelay) * time.Microsecond)
|
||||
time.Sleep(d.stepDelay)
|
||||
d.moveDirectionSteps(direction, s)
|
||||
}
|
||||
}
|
||||
@@ -101,7 +155,7 @@ func (d *DualDevice) Move(stepsA, stepsB int32) {
|
||||
stepsA += int32(d.devices[max].stepNumber)
|
||||
minStep = int32(d.devices[min].stepNumber)
|
||||
for s := int32(d.devices[max].stepNumber); s < stepsA; s++ {
|
||||
time.Sleep(time.Duration(d.devices[0].stepDelay) * time.Microsecond)
|
||||
time.Sleep(d.devices[0].stepDelay)
|
||||
d.devices[max].moveDirectionSteps(directions[max], s)
|
||||
|
||||
if ((s * stepsB) / stepsA) > minStep {
|
||||
@@ -119,6 +173,18 @@ func (d *DualDevice) Off() {
|
||||
|
||||
// stepMotor changes the pins' state to the correct step
|
||||
func (d *Device) stepMotor(step uint8) {
|
||||
switch d.stepMode {
|
||||
default:
|
||||
fallthrough
|
||||
case ModeFour:
|
||||
d.stepMotor4(step)
|
||||
case ModeEight:
|
||||
d.stepMotor8(step)
|
||||
}
|
||||
}
|
||||
|
||||
// stepMotor4 changes the pins' state to the correct step in 4-step mode
|
||||
func (d *Device) stepMotor4(step uint8) {
|
||||
switch step {
|
||||
case 0:
|
||||
d.pins[0].High()
|
||||
@@ -148,13 +214,63 @@ func (d *Device) stepMotor(step uint8) {
|
||||
d.stepNumber = step
|
||||
}
|
||||
|
||||
// stepMotor8 changes the pins' state to the correct step in 8-step mode
|
||||
func (d *Device) stepMotor8(step uint8) {
|
||||
switch step {
|
||||
case 0:
|
||||
d.pins[0].High()
|
||||
d.pins[2].Low()
|
||||
d.pins[1].Low()
|
||||
d.pins[3].Low()
|
||||
case 1:
|
||||
d.pins[0].High()
|
||||
d.pins[2].High()
|
||||
d.pins[1].Low()
|
||||
d.pins[3].Low()
|
||||
case 2:
|
||||
d.pins[0].Low()
|
||||
d.pins[2].High()
|
||||
d.pins[1].Low()
|
||||
d.pins[3].Low()
|
||||
case 3:
|
||||
d.pins[0].Low()
|
||||
d.pins[2].High()
|
||||
d.pins[1].High()
|
||||
d.pins[3].Low()
|
||||
case 4:
|
||||
d.pins[0].Low()
|
||||
d.pins[2].Low()
|
||||
d.pins[1].High()
|
||||
d.pins[3].Low()
|
||||
case 5:
|
||||
d.pins[0].Low()
|
||||
d.pins[2].Low()
|
||||
d.pins[1].High()
|
||||
d.pins[3].High()
|
||||
case 6:
|
||||
d.pins[0].Low()
|
||||
d.pins[2].Low()
|
||||
d.pins[1].Low()
|
||||
d.pins[3].High()
|
||||
case 7:
|
||||
d.pins[0].High()
|
||||
d.pins[2].Low()
|
||||
d.pins[1].Low()
|
||||
d.pins[3].High()
|
||||
}
|
||||
d.stepNumber = step
|
||||
}
|
||||
|
||||
// moveDirectionSteps uses the direction to calculate the correct step and change the motor to it.
|
||||
// Direction true: 0, 1, 2, 3, 0, 1, 2, ...
|
||||
// Direction false: 0, 3, 2, 1, 0, 3, 2, ...
|
||||
// Direction true: (4-step mode) 0, 1, 2, 3, 0, 1, 2, ...
|
||||
// Direction false: (4-step mode) 0, 3, 2, 1, 0, 3, 2, ...
|
||||
// Direction true: (8-step mode) 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, ...
|
||||
// Direction false: (8-step mode) 0, 7, 6, 5, 4, 3, 2, 1, 0, 7, 6, ...
|
||||
func (d *Device) moveDirectionSteps(direction bool, step int32) {
|
||||
modulus := int32(d.stepMode.stepCount())
|
||||
if direction {
|
||||
d.stepMotor(uint8(step % 4))
|
||||
d.stepMotor(uint8(step % modulus))
|
||||
} else {
|
||||
d.stepMotor(uint8((step + 2*(step%2)) % 4))
|
||||
d.stepMotor(uint8(((-step % modulus) + modulus) % modulus))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
package encoders
|
||||
|
||||
type QuadratureDevice struct {
|
||||
cfg QuadratureConfig
|
||||
impl quadratureImpl
|
||||
}
|
||||
|
||||
type QuadratureConfig struct {
|
||||
Precision int
|
||||
}
|
||||
|
||||
type quadratureImpl interface {
|
||||
configure(cfg QuadratureConfig) error
|
||||
readValue() int
|
||||
writeValue(int)
|
||||
}
|
||||
|
||||
func (enc *QuadratureDevice) Configure(cfg QuadratureConfig) error {
|
||||
if cfg.Precision < 1 {
|
||||
cfg.Precision = 4
|
||||
}
|
||||
enc.cfg = cfg
|
||||
return enc.impl.configure(cfg)
|
||||
}
|
||||
|
||||
// Position returns the stored int value for the encoder
|
||||
func (enc *QuadratureDevice) Position() int {
|
||||
return enc.impl.readValue() / enc.cfg.Precision
|
||||
}
|
||||
|
||||
// SetPosition overwrites the currently stored value with the specified int value
|
||||
func (enc *QuadratureDevice) SetPosition(v int) {
|
||||
enc.impl.writeValue(v * enc.cfg.Precision)
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
//go:build tinygo && (rp2040 || stm32 || k210 || esp32c3 || nrf || sam || (avr && (atmega328p || atmega328pb)))
|
||||
|
||||
// Implementation based on:
|
||||
// https://gist.github.com/aykevl/3fc1683ed77bb0a9c07559dfe857304a
|
||||
|
||||
// Note: build constraints in this file list targets that define machine.PinToggle.
|
||||
// If this is supported for additional targets in the future, they can be added above.
|
||||
|
||||
package encoders
|
||||
|
||||
import (
|
||||
"machine"
|
||||
"runtime/volatile"
|
||||
)
|
||||
|
||||
var (
|
||||
states = []int8{0, -1, 1, 0, 1, 0, 0, -1, -1, 0, 0, 1, 0, 1, -1, 0}
|
||||
)
|
||||
|
||||
// NewQuadratureViaInterrupt returns a rotary encoder device that uses GPIO
|
||||
// interrupts and a lookup table to keep track of quadrature state changes.
|
||||
//
|
||||
// This constructur is only available for TinyGo targets for which machine.PinToggle
|
||||
// is defined as a valid interrupt type.
|
||||
func NewQuadratureViaInterrupt(pinA, pinB machine.Pin) *QuadratureDevice {
|
||||
return &QuadratureDevice{impl: &quadInterruptImpl{pinA: pinA, pinB: pinB, oldAB: 0b00000011}}
|
||||
}
|
||||
|
||||
type quadInterruptImpl struct {
|
||||
pinA machine.Pin
|
||||
pinB machine.Pin
|
||||
|
||||
// precision int
|
||||
|
||||
oldAB int
|
||||
value volatile.Register32
|
||||
}
|
||||
|
||||
func (enc *quadInterruptImpl) configure(cfg QuadratureConfig) error {
|
||||
enc.pinA.Configure(machine.PinConfig{Mode: machine.PinInputPullup})
|
||||
enc.pinA.SetInterrupt(machine.PinToggle, enc.interrupt)
|
||||
|
||||
enc.pinB.Configure(machine.PinConfig{Mode: machine.PinInputPullup})
|
||||
enc.pinB.SetInterrupt(machine.PinToggle, enc.interrupt)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (enc *quadInterruptImpl) interrupt(pin machine.Pin) {
|
||||
aHigh, bHigh := enc.pinA.Get(), enc.pinB.Get()
|
||||
enc.oldAB <<= 2
|
||||
if aHigh {
|
||||
enc.oldAB |= 1 << 1
|
||||
}
|
||||
if bHigh {
|
||||
enc.oldAB |= 1
|
||||
}
|
||||
enc.writeValue(enc.readValue() + int(states[enc.oldAB&0x0f]))
|
||||
}
|
||||
|
||||
// readValue gets the value using volatile operations and returns it as an int
|
||||
func (enc *quadInterruptImpl) readValue() int {
|
||||
return int(enc.value.Get())
|
||||
}
|
||||
|
||||
// writeValue set the value to the specified int using volatile operations
|
||||
func (enc *quadInterruptImpl) writeValue(v int) {
|
||||
enc.value.Set(uint32(v))
|
||||
}
|
||||
+295
-34
@@ -16,41 +16,301 @@
|
||||
// AT command set:
|
||||
// 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"
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"machine"
|
||||
"net"
|
||||
"net/netip"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
"tinygo.org/x/drivers/net"
|
||||
"tinygo.org/x/drivers/netdev"
|
||||
"tinygo.org/x/drivers/netlink"
|
||||
)
|
||||
|
||||
// Device wraps UART connection to the ESP8266/ESP32.
|
||||
type Device struct {
|
||||
bus drivers.UART
|
||||
type Config struct {
|
||||
// UART config
|
||||
Uart *machine.UART
|
||||
Tx machine.Pin
|
||||
Rx machine.Pin
|
||||
}
|
||||
|
||||
type socket struct {
|
||||
inUse bool
|
||||
protocol int
|
||||
laddr netip.AddrPort
|
||||
}
|
||||
|
||||
type Device struct {
|
||||
cfg *Config
|
||||
uart *machine.UART
|
||||
// command responses that come back from the ESP8266/ESP32
|
||||
response []byte
|
||||
|
||||
// data received from a TCP/UDP connection forwarded by the ESP8266/ESP32
|
||||
socketdata []byte
|
||||
data []byte
|
||||
socket socket
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
// ActiveDevice is the currently configured Device in use. There can only be one.
|
||||
var ActiveDevice *Device
|
||||
|
||||
// New returns a new espat driver. Pass in a fully configured UART bus.
|
||||
func New(b drivers.UART) *Device {
|
||||
return &Device{bus: b, response: make([]byte, 512), socketdata: make([]byte, 0, 1024)}
|
||||
func NewDevice(cfg *Config) *Device {
|
||||
return &Device{
|
||||
cfg: cfg,
|
||||
response: make([]byte, 1500),
|
||||
data: make([]byte, 0, 1500),
|
||||
}
|
||||
}
|
||||
|
||||
// Configure sets up the device for communication.
|
||||
func (d Device) Configure() {
|
||||
ActiveDevice = &d
|
||||
net.ActiveDevice = ActiveDevice
|
||||
func (d *Device) NetConnect(params *netlink.ConnectParams) error {
|
||||
|
||||
if len(params.Ssid) == 0 {
|
||||
return netlink.ErrMissingSSID
|
||||
}
|
||||
|
||||
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)) {
|
||||
fmt.Printf("\r\n%s\r\n", netlink.ErrNotSupported)
|
||||
}
|
||||
|
||||
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) (int, netip.AddrPort, error) {
|
||||
return -1, netip.AddrPort{}, 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.
|
||||
@@ -58,7 +318,7 @@ func (d *Device) Connected() bool {
|
||||
d.Execute(Test)
|
||||
|
||||
// handle response here, should include "OK"
|
||||
_, err := d.Response(100)
|
||||
_, err := d.Response(1000)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
@@ -67,12 +327,12 @@ func (d *Device) Connected() bool {
|
||||
|
||||
// Write raw bytes to the UART.
|
||||
func (d *Device) Write(b []byte) (n int, err error) {
|
||||
return d.bus.Write(b)
|
||||
return d.uart.Write(b)
|
||||
}
|
||||
|
||||
// Read raw bytes from the UART.
|
||||
func (d *Device) Read(b []byte) (n int, err error) {
|
||||
return d.bus.Read(b)
|
||||
return d.uart.Read(b)
|
||||
}
|
||||
|
||||
// how long in milliseconds to pause after sending AT commands
|
||||
@@ -101,9 +361,10 @@ func (d Device) Set(cmd, params string) error {
|
||||
// Version returns the ESP8266/ESP32 firmware version info.
|
||||
func (d Device) Version() []byte {
|
||||
d.Execute(Version)
|
||||
r, err := d.Response(100)
|
||||
r, err := d.Response(2000)
|
||||
if err != nil {
|
||||
return []byte("unknown")
|
||||
//return []byte("unknown")
|
||||
return []byte(err.Error())
|
||||
}
|
||||
return r
|
||||
}
|
||||
@@ -133,16 +394,16 @@ func (d *Device) ReadSocket(b []byte) (n int, err error) {
|
||||
d.Response(300)
|
||||
|
||||
count := len(b)
|
||||
if len(b) >= len(d.socketdata) {
|
||||
if len(b) >= len(d.data) {
|
||||
// copy it all, then clear socket data
|
||||
count = len(d.socketdata)
|
||||
copy(b, d.socketdata[:count])
|
||||
d.socketdata = d.socketdata[:0]
|
||||
count = len(d.data)
|
||||
copy(b, d.data[:count])
|
||||
d.data = d.data[:0]
|
||||
} else {
|
||||
// copy all we can, then keep the remaining socket data around
|
||||
copy(b, d.socketdata[:count])
|
||||
copy(d.socketdata, d.socketdata[count:])
|
||||
d.socketdata = d.socketdata[:len(d.socketdata)-count]
|
||||
copy(b, d.data[:count])
|
||||
copy(d.data, d.data[count:])
|
||||
d.data = d.data[:len(d.data)-count]
|
||||
}
|
||||
|
||||
return count, nil
|
||||
@@ -158,11 +419,11 @@ func (d *Device) Response(timeout int) ([]byte, error) {
|
||||
retries := timeout / pause
|
||||
|
||||
for {
|
||||
size = d.bus.Buffered()
|
||||
size = d.uart.Buffered()
|
||||
|
||||
if size > 0 {
|
||||
end += size
|
||||
d.bus.Read(d.response[start:end])
|
||||
d.uart.Read(d.response[start:end])
|
||||
|
||||
// if "+IPD" then read socket data
|
||||
if strings.Contains(string(d.response[:end]), "+IPD") {
|
||||
@@ -205,18 +466,18 @@ func (d *Device) parseIPD(end int) error {
|
||||
val := string(d.response[s+5 : e])
|
||||
|
||||
// TODO: verify count
|
||||
_, err := strconv.Atoi(val)
|
||||
v, err := strconv.Atoi(val)
|
||||
if err != nil {
|
||||
// not expected data here. what to do?
|
||||
return err
|
||||
}
|
||||
|
||||
// load up the socket data
|
||||
d.socketdata = append(d.socketdata, d.response[e+1:end]...)
|
||||
d.data = append(d.data, d.response[e+1:e+1+v]...)
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsSocketDataAvailable returns of there is socket data available
|
||||
func (d *Device) IsSocketDataAvailable() bool {
|
||||
return len(d.socketdata) > 0 || d.bus.Buffered() > 0
|
||||
return len(d.data) > 0 || d.uart.Buffered() > 0
|
||||
}
|
||||
|
||||
+2
-2
@@ -29,7 +29,7 @@ func (d *Device) GetDNS(domain string) (string, error) {
|
||||
return "", errors.New("Invalid domain lookup result")
|
||||
}
|
||||
res := strings.Split(r[1], "\r\n")
|
||||
return res[0], nil
|
||||
return strings.Trim(res[0], `"`), nil
|
||||
}
|
||||
|
||||
// ConnectTCPSocket creates a new TCP socket connection for the ESP8266/ESP32.
|
||||
@@ -51,7 +51,7 @@ func (d *Device) ConnectTCPSocket(addr, port string) error {
|
||||
// ConnectUDPSocket creates a new UDP connection for the ESP8266/ESP32.
|
||||
func (d *Device) ConnectUDPSocket(addr, sendport, listenport string) error {
|
||||
protocol := "UDP"
|
||||
val := "\"" + protocol + "\",\"" + addr + "\"," + sendport + "," + listenport + ",2"
|
||||
val := "\"" + protocol + "\",\"" + addr + "\"," + sendport + "," + listenport + ",0"
|
||||
err := d.Set(TCPConnect, val)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
+1
-4
@@ -44,10 +44,7 @@ func (d *Device) ConnectToAP(ssid, pwd string, ws int) error {
|
||||
d.Set(ConnectAP, val)
|
||||
|
||||
_, err := d.Response(ws * 1000)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return err
|
||||
}
|
||||
|
||||
// DisconnectFromAP disconnects the ESP8266/ESP32 from the current access point.
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
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})
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/apds9960"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
// use Nano 33 BLE Sense's internal I2C bus
|
||||
machine.I2C1.Configure(machine.I2CConfig{
|
||||
SCL: machine.SCL1_PIN,
|
||||
SDA: machine.SDA1_PIN,
|
||||
Frequency: machine.TWI_FREQ_400KHZ,
|
||||
})
|
||||
|
||||
sensor := apds9960.New(machine.I2C1)
|
||||
|
||||
sensor.Configure(apds9960.Configuration{}) // use default settings
|
||||
|
||||
if !sensor.Connected() {
|
||||
println("APDS-9960 not connected!")
|
||||
return
|
||||
}
|
||||
|
||||
sensor.EnableColor() // enable color engine
|
||||
|
||||
for {
|
||||
|
||||
if sensor.ColorAvailable() {
|
||||
r, g, b, c := sensor.ReadColor()
|
||||
println("Red =", r, "\tGreen =", g, "\tBlue =", b, "\tClear =", c)
|
||||
}
|
||||
time.Sleep(time.Millisecond * 100)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/apds9960"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
// use Nano 33 BLE Sense's internal I2C bus
|
||||
machine.I2C1.Configure(machine.I2CConfig{
|
||||
SCL: machine.SCL1_PIN,
|
||||
SDA: machine.SDA1_PIN,
|
||||
Frequency: machine.TWI_FREQ_400KHZ,
|
||||
})
|
||||
|
||||
sensor := apds9960.New(machine.I2C1)
|
||||
|
||||
sensor.Configure(apds9960.Configuration{}) // use default settings
|
||||
|
||||
if !sensor.Connected() {
|
||||
println("APDS-9960 not connected!")
|
||||
return
|
||||
}
|
||||
|
||||
sensor.EnableGesture() // enable gesture engine
|
||||
|
||||
for {
|
||||
|
||||
// wave your hand (not too slow) about 10 cm above the sensor
|
||||
if sensor.GestureAvailable() {
|
||||
|
||||
gesture := sensor.ReadGesture()
|
||||
print("Detected gesture: ")
|
||||
switch gesture {
|
||||
case apds9960.GESTURE_UP: // the nRF52 chip is "up"
|
||||
println("Up")
|
||||
case apds9960.GESTURE_DOWN:
|
||||
println("Down")
|
||||
case apds9960.GESTURE_LEFT:
|
||||
println("Left")
|
||||
case apds9960.GESTURE_RIGHT:
|
||||
println("Right")
|
||||
}
|
||||
}
|
||||
// note: the delay shouldn't be too long, otherwise new gesture data might be lost
|
||||
time.Sleep(time.Millisecond * 250)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/apds9960"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
// use Nano 33 BLE Sense's internal I2C bus
|
||||
machine.I2C1.Configure(machine.I2CConfig{
|
||||
SCL: machine.SCL1_PIN,
|
||||
SDA: machine.SDA1_PIN,
|
||||
Frequency: machine.TWI_FREQ_400KHZ,
|
||||
})
|
||||
|
||||
sensor := apds9960.New(machine.I2C1)
|
||||
|
||||
// use default settings
|
||||
sensor.Configure(apds9960.Configuration{})
|
||||
|
||||
if !sensor.Connected() {
|
||||
println("APDS-9960 not connected!")
|
||||
return
|
||||
}
|
||||
|
||||
sensor.EnableProximity() // enable proximity engine
|
||||
|
||||
for {
|
||||
|
||||
if sensor.ProximityAvailable() {
|
||||
p := sensor.ReadProximity()
|
||||
println("Proximity:", p)
|
||||
}
|
||||
time.Sleep(time.Millisecond * 100)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
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)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
"time"
|
||||
|
||||
axp192 "tinygo.org/x/drivers/axp192/m5stack-core2-axp192"
|
||||
"tinygo.org/x/drivers/i2csoft"
|
||||
)
|
||||
|
||||
func main() {
|
||||
i2c := i2csoft.New(machine.SCL0_PIN, machine.SDA0_PIN)
|
||||
i2c.Configure(i2csoft.I2CConfig{Frequency: 100e3})
|
||||
|
||||
axp := axp192.New(i2c)
|
||||
led := axp.LED
|
||||
|
||||
for {
|
||||
led.Low()
|
||||
time.Sleep(time.Millisecond * 500)
|
||||
|
||||
led.High()
|
||||
time.Sleep(time.Millisecond * 500)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
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,6 +27,9 @@ func main() {
|
||||
pressure, _ := sensor.ReadPressure()
|
||||
println("Pressure", float32(pressure)/100000, "hPa")
|
||||
|
||||
altitude, _ := sensor.ReadAltitude()
|
||||
println("Altitude", altitude, "meters")
|
||||
|
||||
time.Sleep(2 * time.Second)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
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())
|
||||
}
|
||||
@@ -12,7 +12,7 @@ func main() {
|
||||
dhtSensor := dht.New(pin, dht.DHT11)
|
||||
for {
|
||||
temp, hum, err := dhtSensor.Measurements()
|
||||
if err != nil {
|
||||
if err == nil {
|
||||
fmt.Printf("Temperature: %02d.%d°C, Humidity: %02d.%d%%\n", temp/10, temp%10, hum/10, hum%10)
|
||||
} else {
|
||||
fmt.Printf("Could not take measurements from the sensor: %s\n", err.Error())
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
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 MAG3110 I2C magnetometer.
|
||||
// Connects to an DS3231 I2C Real Time Clock (RTC).
|
||||
package main
|
||||
|
||||
import (
|
||||
|
||||
@@ -8,7 +8,11 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
motor := easystepper.New(machine.P13, machine.P15, machine.P14, machine.P16, 200, 75)
|
||||
config := easystepper.DeviceConfig{
|
||||
Pin1: machine.P13, Pin2: machine.P15, Pin3: machine.P14, Pin4: machine.P16,
|
||||
StepCount: 200, RPM: 75, Mode: easystepper.ModeFour,
|
||||
}
|
||||
motor, _ := easystepper.New(config)
|
||||
motor.Configure()
|
||||
|
||||
for {
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
//go:build macropad_rp2040
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
|
||||
"tinygo.org/x/drivers/encoders"
|
||||
)
|
||||
|
||||
var (
|
||||
enc = encoders.NewQuadratureViaInterrupt(machine.ROT_A, machine.ROT_B)
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
enc.Configure(encoders.QuadratureConfig{
|
||||
Precision: 4,
|
||||
})
|
||||
|
||||
for oldValue := 0; ; {
|
||||
if newValue := enc.Position(); newValue != oldValue {
|
||||
println("value: ", newValue)
|
||||
oldValue = newValue
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,143 +0,0 @@
|
||||
// This is a console to a ESP8266/ESP32 running on the device UART1.
|
||||
// Allows you to type AT commands from your computer via the microcontroller.
|
||||
//
|
||||
// In other words:
|
||||
// Your computer <--> UART0 <--> MCU <--> UART1 <--> ESP8266 <--> INTERNET
|
||||
//
|
||||
// More information on the Espressif AT command set at:
|
||||
// https://www.espressif.com/sites/default/files/documentation/4a-esp8266_at_instruction_set_en.pdf
|
||||
//
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/espat"
|
||||
)
|
||||
|
||||
// change actAsAP to true to act as an access point instead of connecting to one.
|
||||
const actAsAP = false
|
||||
|
||||
// access point info
|
||||
const ssid = "YOURSSID"
|
||||
const pass = "YOURPASS"
|
||||
|
||||
// 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 + "'")
|
||||
|
||||
adaptor.SetWifiMode(espat.WifiModeClient)
|
||||
adaptor.ConnectToAP(ssid, pass, 10)
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
@@ -1,129 +0,0 @@
|
||||
// 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
|
||||
|
||||
// access point info
|
||||
const ssid = "YOURSSID"
|
||||
const pass = "YOURPASS"
|
||||
|
||||
// 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 + "'")
|
||||
|
||||
adaptor.SetWifiMode(espat.WifiModeClient)
|
||||
adaptor.ConnectToAP(ssid, pass, 10)
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
@@ -1,108 +0,0 @@
|
||||
// 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"
|
||||
)
|
||||
|
||||
// access point info
|
||||
const ssid = "YOURSSID"
|
||||
const pass = "YOURPASS"
|
||||
|
||||
// IP address of the listener aka "hub". Replace with your own info.
|
||||
const hubIP = "0.0.0.0"
|
||||
|
||||
// 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 + "'")
|
||||
|
||||
adaptor.SetWifiMode(espat.WifiModeClient)
|
||||
adaptor.ConnectToAP(ssid, pass, 10)
|
||||
|
||||
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,142 +0,0 @@
|
||||
// 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"
|
||||
)
|
||||
|
||||
// access point info
|
||||
const ssid = "YOURSSID"
|
||||
const pass = "YOURPASS"
|
||||
|
||||
// IP address of the MQTT broker to use. Replace with your own info.
|
||||
const server = "tcp://test.mosquitto.org:1883"
|
||||
|
||||
//const server = "ssl://test.mosquitto.org:8883"
|
||||
|
||||
// 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 + "'")
|
||||
|
||||
adaptor.SetWifiMode(espat.WifiModeClient)
|
||||
adaptor.ConnectToAP(ssid, pass, 10)
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
@@ -1,163 +0,0 @@
|
||||
// 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"
|
||||
)
|
||||
|
||||
// access point info
|
||||
const ssid = "YOURSSID"
|
||||
const pass = "YOURPASS"
|
||||
|
||||
// IP address of the MQTT broker to use. Replace with your own info.
|
||||
//const server = "tcp://test.mosquitto.org:1883"
|
||||
|
||||
const server = "ssl://test.mosquitto.org:8883"
|
||||
|
||||
// change these to connect to a different UART or pins for the ESP8266/ESP32
|
||||
var (
|
||||
// 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 + "'")
|
||||
|
||||
adaptor.SetWifiMode(espat.WifiModeClient)
|
||||
adaptor.ConnectToAP(ssid, pass, 10)
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
@@ -1,111 +0,0 @@
|
||||
// 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"
|
||||
)
|
||||
|
||||
// access point info
|
||||
const ssid = "YOURSSID"
|
||||
const pass = "YOURPASS"
|
||||
|
||||
// IP address of the server aka "hub". Replace with your own info.
|
||||
const serverIP = "0.0.0.0"
|
||||
|
||||
// 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 + "'")
|
||||
|
||||
adaptor.SetWifiMode(espat.WifiModeClient)
|
||||
adaptor.ConnectToAP(ssid, pass, 10)
|
||||
|
||||
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,23 @@
|
||||
//go:build m5stack_core2
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
|
||||
"tinygo.org/x/drivers/ft6336"
|
||||
"tinygo.org/x/drivers/i2csoft"
|
||||
"tinygo.org/x/drivers/touch"
|
||||
)
|
||||
|
||||
// InitDisplay initializes the display of each board.
|
||||
func initDevices() (touch.Pointer, error) {
|
||||
i2c := i2csoft.New(machine.SCL0_PIN, machine.SDA0_PIN)
|
||||
i2c.Configure(i2csoft.I2CConfig{Frequency: 100e3})
|
||||
|
||||
resistiveTouch := ft6336.New(i2c, machine.Pin(39))
|
||||
resistiveTouch.Configure(ft6336.Config{})
|
||||
resistiveTouch.SetPeriodActive(0x00)
|
||||
|
||||
return resistiveTouch, nil
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
touchScreen, _ := initDevices()
|
||||
|
||||
for {
|
||||
touch := touchScreen.ReadTouchPoint()
|
||||
if touch.Z > 0 {
|
||||
//X and Y are 16 bit with 12 bit resolution and need to be scaled for the display size
|
||||
//Z is 24 bit and is typically > 2000 for a touch
|
||||
println("touch:", touch.X, touch.Y, touch.Z)
|
||||
//Example of scaling for m5stack-core2's 320x240 display with 320x270 touch area
|
||||
println("screen:", (touch.X*320)>>16, (touch.Y*270)>>16)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
//go:build m5stack_core2
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"image/color"
|
||||
"machine"
|
||||
|
||||
axp192 "tinygo.org/x/drivers/axp192/m5stack-core2-axp192"
|
||||
"tinygo.org/x/drivers/ft6336"
|
||||
"tinygo.org/x/drivers/i2csoft"
|
||||
"tinygo.org/x/drivers/ili9341"
|
||||
"tinygo.org/x/drivers/touch"
|
||||
)
|
||||
|
||||
// InitDisplay initializes the display of each board.
|
||||
func initDevices() (touchPaintDisplay, touch.Pointer, error) {
|
||||
machine.SPI2.Configure(machine.SPIConfig{
|
||||
SCK: machine.LCD_SCK_PIN,
|
||||
SDO: machine.LCD_SDO_PIN,
|
||||
SDI: machine.LCD_SDI_PIN,
|
||||
Frequency: 40e6,
|
||||
})
|
||||
|
||||
i2c := i2csoft.New(machine.SCL0_PIN, machine.SDA0_PIN)
|
||||
i2c.Configure(i2csoft.I2CConfig{Frequency: 100e3})
|
||||
|
||||
axp := axp192.New(i2c)
|
||||
led := axp.LED
|
||||
led.Low()
|
||||
|
||||
display := ili9341.NewSPI(
|
||||
machine.SPI2,
|
||||
machine.LCD_DC_PIN,
|
||||
machine.LCD_SS_PIN,
|
||||
machine.NoPin,
|
||||
)
|
||||
|
||||
// configure display
|
||||
display.Configure(ili9341.Config{
|
||||
Width: 320,
|
||||
Height: 240,
|
||||
DisplayInversion: true,
|
||||
})
|
||||
display.FillScreen(color.RGBA{255, 255, 255, 255})
|
||||
|
||||
display.SetRotation(ili9341.Rotation0Mirror)
|
||||
|
||||
resistiveTouch := ft6336.New(i2c, machine.Pin(39))
|
||||
resistiveTouch.Configure(ft6336.Config{})
|
||||
resistiveTouch.SetPeriodActive(0x00)
|
||||
|
||||
return display, resistiveTouch, nil
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"image/color"
|
||||
"math"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
"tinygo.org/x/drivers/touch"
|
||||
)
|
||||
|
||||
type touchPaintDisplay interface {
|
||||
drivers.Displayer
|
||||
FillRectangle(x, y, width, height int16, c color.RGBA) error
|
||||
DrawRectangle(x, y, w, h int16, c color.RGBA) error
|
||||
}
|
||||
|
||||
var (
|
||||
white = color.RGBA{255, 255, 255, 255}
|
||||
black = color.RGBA{0, 0, 0, 255}
|
||||
red = color.RGBA{255, 0, 0, 255}
|
||||
green = color.RGBA{0, 255, 0, 255}
|
||||
blue = color.RGBA{0, 0, 255, 255}
|
||||
magenta = color.RGBA{255, 0, 255, 255}
|
||||
yellow = color.RGBA{255, 255, 0, 255}
|
||||
cyan = color.RGBA{0, 255, 255, 255}
|
||||
|
||||
oldColor color.RGBA
|
||||
currentColor color.RGBA
|
||||
)
|
||||
|
||||
const (
|
||||
penRadius = 3
|
||||
boxSize = 30
|
||||
|
||||
Xmin = 0
|
||||
Xmax = 0xFFFF
|
||||
Ymin = 0
|
||||
Ymax = 0xFFFF
|
||||
)
|
||||
|
||||
func main() {
|
||||
display, resistiveTouch, _ := initDevices()
|
||||
|
||||
// fill the background and activate the backlight
|
||||
width, height := display.Size()
|
||||
display.FillRectangle(0, 0, width, height, black)
|
||||
|
||||
// make color selection boxes
|
||||
display.FillRectangle(0, 0, boxSize, boxSize, red)
|
||||
display.FillRectangle(boxSize, 0, boxSize, boxSize, yellow)
|
||||
display.FillRectangle(boxSize*2, 0, boxSize, boxSize, green)
|
||||
display.FillRectangle(boxSize*3, 0, boxSize, boxSize, cyan)
|
||||
display.FillRectangle(boxSize*4, 0, boxSize, boxSize, blue)
|
||||
display.FillRectangle(boxSize*5, 0, boxSize, boxSize, magenta)
|
||||
display.FillRectangle(boxSize*6, 0, boxSize, boxSize, black)
|
||||
display.FillRectangle(boxSize*7, 0, boxSize, boxSize, white)
|
||||
|
||||
// set the initial color to red and draw a box to highlight it
|
||||
oldColor = red
|
||||
currentColor = red
|
||||
display.DrawRectangle(0, 0, boxSize, boxSize, white)
|
||||
|
||||
last := touch.Point{}
|
||||
|
||||
// loop and poll for touches, including performing debouncing
|
||||
debounce := 0
|
||||
for {
|
||||
|
||||
point := resistiveTouch.ReadTouchPoint()
|
||||
touch := touch.Point{}
|
||||
if point.Z>>6 > 100 {
|
||||
rawX := mapval(point.X, Xmin, Xmax, 0, int(width))
|
||||
rawY := mapval(point.Y, Ymin, Ymax, 0, int(height))
|
||||
touch.X = rawX
|
||||
touch.Y = rawY
|
||||
touch.Z = 1
|
||||
} else {
|
||||
touch.X = 0
|
||||
touch.Y = 0
|
||||
touch.Z = 0
|
||||
}
|
||||
|
||||
if last.Z != touch.Z {
|
||||
debounce = 0
|
||||
last = touch
|
||||
} else if math.Abs(float64(touch.X-last.X)) > 4 ||
|
||||
math.Abs(float64(touch.Y-last.Y)) > 4 {
|
||||
debounce = 0
|
||||
last = touch
|
||||
} else if debounce > 1 {
|
||||
debounce = 0
|
||||
HandleTouch(display, last)
|
||||
} else if touch.Z > 0 {
|
||||
debounce++
|
||||
} else {
|
||||
last = touch
|
||||
debounce = 0
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// based on Arduino's "map" function
|
||||
func mapval(x int, inMin int, inMax int, outMin int, outMax int) int {
|
||||
return (x-inMin)*(outMax-outMin)/(inMax-inMin) + outMin
|
||||
}
|
||||
|
||||
func HandleTouch(display touchPaintDisplay, touch touch.Point) {
|
||||
|
||||
if int16(touch.Y) < boxSize {
|
||||
oldColor = currentColor
|
||||
x := int16(touch.X)
|
||||
switch {
|
||||
case x < boxSize:
|
||||
currentColor = red
|
||||
case x < boxSize*2:
|
||||
currentColor = yellow
|
||||
case x < boxSize*3:
|
||||
currentColor = green
|
||||
case x < boxSize*4:
|
||||
currentColor = cyan
|
||||
case x < boxSize*5:
|
||||
currentColor = blue
|
||||
case x < boxSize*6:
|
||||
currentColor = magenta
|
||||
case x < boxSize*7:
|
||||
currentColor = black
|
||||
case x < boxSize*8:
|
||||
currentColor = white
|
||||
}
|
||||
|
||||
if oldColor == currentColor {
|
||||
return
|
||||
}
|
||||
|
||||
display.DrawRectangle((x/boxSize)*boxSize, 0, boxSize, boxSize, white)
|
||||
switch oldColor {
|
||||
case red:
|
||||
x = 0
|
||||
case yellow:
|
||||
x = boxSize
|
||||
case green:
|
||||
x = boxSize * 2
|
||||
case cyan:
|
||||
x = boxSize * 3
|
||||
case blue:
|
||||
x = boxSize * 4
|
||||
case magenta:
|
||||
x = boxSize * 5
|
||||
case black:
|
||||
x = boxSize * 6
|
||||
case white:
|
||||
x = boxSize * 7
|
||||
}
|
||||
display.FillRectangle(int16(x), 0, boxSize, boxSize, oldColor)
|
||||
|
||||
}
|
||||
|
||||
if (int16(touch.Y) - penRadius) > boxSize {
|
||||
display.FillRectangle(
|
||||
int16(touch.X), int16(touch.Y), penRadius*2, penRadius*2, currentColor)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
"time"
|
||||
|
||||
"image/color"
|
||||
|
||||
"tinygo.org/x/drivers/gc9a01"
|
||||
)
|
||||
|
||||
func main() {
|
||||
machine.SPI0.Configure(machine.SPIConfig{
|
||||
Frequency: 80000000,
|
||||
})
|
||||
display := gc9a01.New(machine.SPI0, machine.P6, machine.P7, machine.P8, machine.P9)
|
||||
display.Configure(gc9a01.Config{Orientation: gc9a01.HORIZONTAL, Width: 240, Height: 240})
|
||||
|
||||
width, height := display.Size()
|
||||
|
||||
white := color.RGBA{255, 255, 255, 255}
|
||||
red := color.RGBA{255, 0, 0, 255}
|
||||
blue := color.RGBA{0, 0, 255, 255}
|
||||
green := color.RGBA{0, 255, 0, 255}
|
||||
black := color.RGBA{0, 0, 0, 255}
|
||||
|
||||
display.FillScreen(black)
|
||||
|
||||
display.FillRectangle(0, 0, width/2, height/2, white)
|
||||
display.FillRectangle(width/2, 0, width/2, height/2, red)
|
||||
display.FillRectangle(0, height/2, width/2, height/2, green)
|
||||
display.FillRectangle(width/2, height/2, width/2, height/2, blue)
|
||||
display.FillRectangle(width/4, height/4, width/2, height/2, black)
|
||||
|
||||
for {
|
||||
time.Sleep(time.Hour)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/hts221"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
// use Nano 33 BLE Sense's internal I2C bus
|
||||
machine.I2C1.Configure(machine.I2CConfig{
|
||||
SCL: machine.SCL1_PIN,
|
||||
SDA: machine.SDA1_PIN,
|
||||
Frequency: machine.TWI_FREQ_400KHZ,
|
||||
})
|
||||
|
||||
sensor := hts221.New(machine.I2C1)
|
||||
|
||||
sensor.Configure() // power on and calibrate
|
||||
|
||||
if !sensor.Connected() {
|
||||
println("HTS221 not connected!")
|
||||
return
|
||||
}
|
||||
|
||||
for {
|
||||
h, _ := sensor.ReadHumidity()
|
||||
t, _ := sensor.ReadTemperature()
|
||||
println("h =", float32(h)/100.0, "% / t =", float32(t)/1000.0, "*C")
|
||||
time.Sleep(time.Second)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"machine"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/adt7410"
|
||||
"tinygo.org/x/drivers/i2csoft"
|
||||
)
|
||||
|
||||
func main() {
|
||||
i2c := i2csoft.New(machine.SCL_PIN, machine.SDA_PIN)
|
||||
i2c.Configure(i2csoft.I2CConfig{
|
||||
Frequency: 400e3,
|
||||
})
|
||||
|
||||
sensor := adt7410.New(i2c)
|
||||
sensor.Configure()
|
||||
|
||||
for {
|
||||
temp := sensor.ReadTempF()
|
||||
fmt.Printf("temperature: %f\r\n", temp)
|
||||
time.Sleep(time.Second)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
// +build atsamd21
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
|
||||
"tinygo.org/x/drivers/ili9341"
|
||||
)
|
||||
|
||||
var (
|
||||
display = ili9341.NewSPI(
|
||||
machine.SPI0,
|
||||
machine.D0,
|
||||
machine.D1,
|
||||
machine.D2,
|
||||
)
|
||||
|
||||
backlight = machine.D3
|
||||
)
|
||||
|
||||
func init() {
|
||||
machine.SPI0.Configure(machine.SPIConfig{
|
||||
SCK: machine.SPI0_SCK_PIN,
|
||||
SDO: machine.SPI0_SDO_PIN,
|
||||
SDI: machine.SPI0_SDI_PIN,
|
||||
Frequency: 24000000,
|
||||
})
|
||||
}
|
||||
@@ -2,9 +2,9 @@ package main
|
||||
|
||||
import (
|
||||
"image/color"
|
||||
"machine"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/examples/ili9341/initdisplay"
|
||||
"tinygo.org/x/drivers/ili9341"
|
||||
)
|
||||
|
||||
@@ -16,16 +16,15 @@ var (
|
||||
green = color.RGBA{0, 255, 0, 255}
|
||||
)
|
||||
|
||||
var (
|
||||
display *ili9341.Device
|
||||
)
|
||||
|
||||
func main() {
|
||||
display = initdisplay.InitDisplay()
|
||||
|
||||
backlight.Configure(machine.PinConfig{machine.PinOutput})
|
||||
|
||||
display.Configure(ili9341.Config{})
|
||||
width, height := display.Size()
|
||||
|
||||
display.FillScreen(black)
|
||||
backlight.High()
|
||||
|
||||
display.FillRectangle(0, 0, width/2, height/2, white)
|
||||
display.FillRectangle(width/2, 0, width/2, height/2, red)
|
||||
display.FillRectangle(0, height/2, width/2, height/2, green)
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
// +build pyportal
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
|
||||
"tinygo.org/x/drivers/ili9341"
|
||||
)
|
||||
|
||||
var (
|
||||
display = ili9341.NewParallel(
|
||||
machine.LCD_DATA0,
|
||||
machine.TFT_WR,
|
||||
machine.TFT_DC,
|
||||
machine.TFT_CS,
|
||||
machine.TFT_RESET,
|
||||
machine.TFT_RD,
|
||||
)
|
||||
|
||||
backlight = machine.TFT_BACKLIGHT
|
||||
)
|
||||
@@ -1,29 +0,0 @@
|
||||
// +build wioterminal
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
|
||||
"tinygo.org/x/drivers/ili9341"
|
||||
)
|
||||
|
||||
var (
|
||||
display = ili9341.NewSPI(
|
||||
machine.SPI3,
|
||||
machine.LCD_DC,
|
||||
machine.LCD_SS_PIN,
|
||||
machine.LCD_RESET,
|
||||
)
|
||||
|
||||
backlight = machine.LCD_BACKLIGHT
|
||||
)
|
||||
|
||||
func init() {
|
||||
machine.SPI3.Configure(machine.SPIConfig{
|
||||
SCK: machine.LCD_SCK_PIN,
|
||||
SDO: machine.LCD_SDO_PIN,
|
||||
SDI: machine.LCD_SDI_PIN,
|
||||
Frequency: 40000000,
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
//go:build atsamd21
|
||||
|
||||
package initdisplay
|
||||
|
||||
import (
|
||||
"machine"
|
||||
|
||||
"tinygo.org/x/drivers/ili9341"
|
||||
)
|
||||
|
||||
func InitDisplay() *ili9341.Device {
|
||||
machine.SPI0.Configure(machine.SPIConfig{
|
||||
SCK: machine.SPI0_SCK_PIN,
|
||||
SDO: machine.SPI0_SDO_PIN,
|
||||
SDI: machine.SPI0_SDI_PIN,
|
||||
Frequency: 24000000,
|
||||
})
|
||||
|
||||
// configure backlight
|
||||
backlight := machine.D3
|
||||
backlight.Configure(machine.PinConfig{machine.PinOutput})
|
||||
|
||||
display := ili9341.NewSPI(
|
||||
machine.SPI0,
|
||||
machine.D0,
|
||||
machine.D1,
|
||||
machine.D2,
|
||||
)
|
||||
|
||||
// configure display
|
||||
display.Configure(ili9341.Config{})
|
||||
|
||||
backlight.High()
|
||||
|
||||
display.SetRotation(ili9341.Rotation270)
|
||||
|
||||
return display
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
//go:build feather_m0 || feather_m4 || feather_m4_can || feather_nrf52840 || feather_nrf52840_sense || feather_stm32f405 || feather_rp2040
|
||||
|
||||
package initdisplay
|
||||
|
||||
import (
|
||||
"machine"
|
||||
|
||||
"tinygo.org/x/drivers/ili9341"
|
||||
)
|
||||
|
||||
func InitDisplay() *ili9341.Device {
|
||||
machine.D5.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
machine.D6.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
|
||||
machine.SPI0.Configure(machine.SPIConfig{
|
||||
SCK: machine.SPI0_SCK_PIN,
|
||||
SDO: machine.SPI0_SDO_PIN,
|
||||
SDI: machine.SPI0_SDI_PIN,
|
||||
Frequency: 40000000,
|
||||
})
|
||||
|
||||
// configure backlight
|
||||
backlight := machine.D9
|
||||
backlight.Configure(machine.PinConfig{machine.PinOutput})
|
||||
|
||||
display := ili9341.NewSPI(
|
||||
machine.SPI0,
|
||||
machine.D10, // LCD_DC,
|
||||
machine.D11, // LCD_SS_PIN,
|
||||
machine.D12, // LCD_RESET,
|
||||
)
|
||||
|
||||
// configure display
|
||||
display.Configure(ili9341.Config{})
|
||||
|
||||
backlight.High()
|
||||
|
||||
display.SetRotation(ili9341.Rotation270)
|
||||
|
||||
return display
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
//go:build m5stack
|
||||
|
||||
package initdisplay
|
||||
|
||||
import (
|
||||
"machine"
|
||||
|
||||
"tinygo.org/x/drivers/ili9341"
|
||||
)
|
||||
|
||||
func InitDisplay() *ili9341.Device {
|
||||
machine.SPI2.Configure(machine.SPIConfig{
|
||||
SCK: machine.SPI0_SCK_PIN,
|
||||
SDO: machine.SPI0_SDO_PIN,
|
||||
SDI: machine.SPI0_SDI_PIN,
|
||||
Frequency: 40e6,
|
||||
})
|
||||
|
||||
// configure backlight
|
||||
backlight := machine.LCD_BL_PIN
|
||||
backlight.Configure(machine.PinConfig{machine.PinOutput})
|
||||
|
||||
display := ili9341.NewSPI(
|
||||
machine.SPI2,
|
||||
machine.LCD_DC_PIN,
|
||||
machine.LCD_SS_PIN,
|
||||
machine.LCD_RST_PIN,
|
||||
)
|
||||
|
||||
// configure display
|
||||
display.Configure(ili9341.Config{
|
||||
Width: 320,
|
||||
Height: 240,
|
||||
DisplayInversion: true,
|
||||
})
|
||||
|
||||
backlight.High()
|
||||
|
||||
display.SetRotation(ili9341.Rotation0Mirror)
|
||||
|
||||
return display
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
//go:build m5stack_core2
|
||||
|
||||
package initdisplay
|
||||
|
||||
import (
|
||||
"image/color"
|
||||
"machine"
|
||||
|
||||
axp192 "tinygo.org/x/drivers/axp192/m5stack-core2-axp192"
|
||||
"tinygo.org/x/drivers/i2csoft"
|
||||
"tinygo.org/x/drivers/ili9341"
|
||||
)
|
||||
|
||||
// InitDisplay initializes the display of each board.
|
||||
func InitDisplay() *ili9341.Device {
|
||||
machine.SPI2.Configure(machine.SPIConfig{
|
||||
SCK: machine.LCD_SCK_PIN,
|
||||
SDO: machine.LCD_SDO_PIN,
|
||||
SDI: machine.LCD_SDI_PIN,
|
||||
Frequency: 40e6,
|
||||
})
|
||||
|
||||
i2c := i2csoft.New(machine.SCL0_PIN, machine.SDA0_PIN)
|
||||
i2c.Configure(i2csoft.I2CConfig{Frequency: 100e3})
|
||||
|
||||
axp := axp192.New(i2c)
|
||||
led := axp.LED
|
||||
led.Low()
|
||||
|
||||
display := ili9341.NewSPI(
|
||||
machine.SPI2,
|
||||
machine.LCD_DC_PIN,
|
||||
machine.LCD_SS_PIN,
|
||||
machine.NoPin,
|
||||
)
|
||||
|
||||
// configure display
|
||||
display.Configure(ili9341.Config{
|
||||
Width: 320,
|
||||
Height: 240,
|
||||
DisplayInversion: true,
|
||||
})
|
||||
display.FillScreen(color.RGBA{255, 255, 255, 255})
|
||||
|
||||
display.SetRotation(ili9341.Rotation0Mirror)
|
||||
|
||||
return display
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
//go:build pyportal
|
||||
|
||||
package initdisplay
|
||||
|
||||
import (
|
||||
"machine"
|
||||
|
||||
"tinygo.org/x/drivers/ili9341"
|
||||
)
|
||||
|
||||
func InitDisplay() *ili9341.Device {
|
||||
display := ili9341.NewParallel(
|
||||
machine.LCD_DATA0,
|
||||
machine.TFT_WR,
|
||||
machine.TFT_DC,
|
||||
machine.TFT_CS,
|
||||
machine.TFT_RESET,
|
||||
machine.TFT_RD,
|
||||
)
|
||||
|
||||
// configure backlight
|
||||
backlight := machine.TFT_BACKLIGHT
|
||||
backlight.Configure(machine.PinConfig{machine.PinOutput})
|
||||
|
||||
// configure display
|
||||
display.Configure(ili9341.Config{})
|
||||
|
||||
backlight.High()
|
||||
|
||||
display.SetRotation(ili9341.Rotation270)
|
||||
|
||||
return display
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user