Compare commits

...

88 Commits

Author SHA1 Message Date
deadprogram d224b5d648 feature: some additional changes to drivers.Pin HAL for clarity and readability.
Signed-off-by: deadprogram <ron@hybridgroup.com>
2025-09-20 08:30:51 +02:00
Patricio Whittingslow 97ed81556b whoops, forgot to change easystepper method calls 2025-09-20 08:30:51 +02:00
Patricio Whittingslow 8ac285e821 legacy.PinOutput->pin.Output and add High+Low methods on drivers.PinOutput and use them in drivers 2025-09-20 08:30:51 +02:00
Patricio Whittingslow 2e007a6de7 rename busy fields to isBusy 2025-09-20 08:30:51 +02:00
Patricio Whittingslow 807ae3da9b missed high or low ~voltage~ level 2025-09-20 08:30:51 +02:00
Patricio Whittingslow c815f2560e apply @gen2thomas suggestions 2025-09-20 08:30:51 +02:00
Patricio Whittingslow 5d1bc2fc36 pins.go -> pin.go 2025-09-20 08:30:51 +02:00
soypat a9a59a6241 bugfix: output misconfigured 2025-09-20 08:30:51 +02:00
soypat c3f8435bc3 fix pullups not available on fe310 2025-09-20 08:30:51 +02:00
soypat 67b669f192 finish rewrites! 2025-09-20 08:30:51 +02:00
soypat cd37668592 more drivers are now cross platform 2025-09-20 08:30:51 +02:00
soypat 7d3404f060 apply suggestions from team 2025-09-20 08:30:51 +02:00
soypat cf81c5ab02 add PinInput and PinOutput HAL 2025-09-20 08:30:51 +02:00
soypat 3fa08112db add regmap package to facilitate heapless driver development 2025-09-14 08:12:32 -04:00
Bryan Souza b639f7b12e added support for P25Q16H flash chip for xiao-ble target; 2025-09-14 08:11:14 -04:00
Bryan Souza 28d625abfd added support for W25Q80DV flash chip for xiao-ble target; 2025-09-14 08:11:14 -04:00
deadprogram 228e57cf98 release: prepare for 0.33.0 drivers release
Signed-off-by: deadprogram <ron@hybridgroup.com>
2025-08-19 21:57:30 +02:00
deadprogram 6cf1eb86e5 fix: correct smoke tests for Adafruit Seesaw
Signed-off-by: deadprogram <ron@hybridgroup.com>
2025-08-18 14:51:03 +02:00
JP Hastings-Spital 857ab80ae6 feat: add support for seesaw encoders
Adds the necessary function addresses for reading and writing encoders on a seesaw.
Also provides two helper functions to make this easier.
2025-08-18 14:15:48 +02:00
Russel Hunter Yukawa a31ba26a6c Fix gps time calculation (#785)
* Change test case to match the date patterns where the bug reproduces
* Fix RMC date and time calculation
2025-08-13 09:39:18 +02:00
Bryan Souza 303ec94529 added support for LSM303DLHC e-Compass; (#783)
fixed the spelling in the Connection error message; Initial support for LSM303DLHC added;
Added LSM303DLHC to smoketest and added an example;
Removed unnecessary comments;
fixed format error;
squashed and ready for merge;
2025-08-11 08:47:22 +02:00
Artur Nasyrov 833990f44d Add ens160 i2c driver
Driver for ENS160 sensor:
https://www.sciosense.com/wp-content/uploads/2023/12/ENS160-Datasheet.pdf
2025-08-10 10:05:18 +02:00
Ayke van Laethem 28d87eb0c5 ws2812: add RP2350 support
Adding 150MHz support for the RP2350
2025-08-10 10:05:18 +02:00
Yurii Soldak ae9e8f915e ssd1306: avoid unnecessary heap allocations (#767)
* ssd1306: avoid unnecessary heap allocations

* ssd1306: extract i2c and spi bus implementations

* ssd1306: refactor tests -- show fps and heap usage

* ssd1306: bring back the lost exported methods

* Adjust examples

* Fix smoketests for ssd1306
2025-08-10 10:05:18 +02:00
JP Hastings-Spital 45fad80c3e feat: allow gps init with address
Adafruit's Mini GPS PA1010D Module works with this device driver, but requires 0x10 as the address, rather than 0x42.

This change allows the device to be initialised with whatever i2c address is needed, while maintaining backward compatibility.

Adds new constants to allow easy configuration of both the ublox device and the PA1010D.
2025-08-10 10:05:18 +02:00
Yurii Soldak 0304d30b78 lsm6ds3tr: avoid unnecessary heap allocations (#766)
* lsm6ds3tr: avoid unnecessary heap allocations
* lsm6ds3tr: use helper functions, for readability
* lsm6ds3tr: return slice of the internal buffer on readBytes
2025-08-10 10:05:18 +02:00
Ron Evans 7de0a0814e Revert "add regmap package to facilitate heapless driver development (#768)" (#776)
This reverts commit 80356fd9d9.
2025-07-14 11:16:56 -03:00
Patricio Whittingslow 80356fd9d9 add regmap package to facilitate heapless driver development (#768) 2025-07-13 09:58:49 -03:00
deadprogram c4ff8242a7 all: updates for drivers release v0.32.0
Signed-off-by: deadprogram <ron@hybridgroup.com>
2025-06-15 22:36:34 +02:00
soypat 82c41dbf14 add driver design pointer to CONTRIBUTING.md 2025-05-28 11:03:09 +02:00
Hikmatulloh Hari Mukti c4168864fd bmp280: remove alloc on read sensor data 2025-05-28 10:29:08 +02:00
Leon Matthews dbc9022f6a ws2812: add 200MHz support for the Cortex-M0/rp2040 2025-05-20 13:09:35 +02:00
Mateusz Nowak d41bc0b85f fix: remove time.Sleep from SSD1306 SPI transfer code 2025-05-20 12:43:13 +02:00
Craig Swank e7f90166ad tmc2209 bug fixes (#755)
* Make write buffer big enough for crc
* crc according to datasheet
* fix build
* a correct crc func already exists
2025-05-20 12:27:30 +02:00
deadprogram 156d6e7c9c chore: update all SPI usage to use either *machine.SPI or drivers.SPI to accomodate recent switch in machine package
Signed-off-by: deadprogram <ron@hybridgroup.com>
2025-03-11 12:43:28 -07:00
deadprogram 6e04decf19 Prepare for release 0.30
Signed-off-by: deadprogram <ron@hybridgroup.com>
2025-03-04 10:11:02 -08:00
deadprogram afb1ea39c2 license: update year
Signed-off-by: deadprogram <ron@hybridgroup.com>
2025-03-04 10:11:02 -08:00
henryleduc 382e79a748 fix(dhcp): fix typo in DHCP error message 2025-03-04 09:25:06 -08:00
Scott Feldman 06dd60fba2 Add wifi driver comboat for Elecrow W5 rp2040 and rp2350 devices (#741)
* Add wifi driver comboat for Elecrow W5 rp2040 and rp2350 devices

Combo-AT driver uses AT command set to talk to onboard rtl8720d wifi
device.  The driver supports UDP/TCP/TLS clients in Wifi STA mode.
Support for UDP/TCP servers is not supported, currently.

https://aithinker-combo-guide.readthedocs.io/en/latest/docs/instruction/index.html
https://aithinker-combo-guide.readthedocs.io/en/latest/docs/command-set/index.html
https://aithinker-combo-guide.readthedocs.io/en/latest/docs/command-examples/index.html

* Add wifi driver comboat for Elecrow W5 rp2040 and rp2350 devices

Combo-AT driver uses AT command set to talk to onboard rtl8720d wifi
device.  The driver supports UDP/TCP/TLS clients in Wifi STA mode.
Support for UDP/TCP servers is not supported, currently.

https://aithinker-combo-guide.readthedocs.io/en/latest/docs/instruction/index.html
https://aithinker-combo-guide.readthedocs.io/en/latest/docs/command-set/index.html
https://aithinker-combo-guide.readthedocs.io/en/latest/docs/command-examples/index.html

* switch to comboat_fw build tag for examples/net's that work with comboat driver
2025-02-21 11:24:15 +01:00
Scott Feldman 27a7d5031e move to latest golang.org/x/net v0.33.0 (#732)
v0.33.0 has fixes for:

CVE-2024-45338: Non-linear parsing of case-insensitive content in golang.org/x/net/html
CVE-2023-45288: net/http, x/net/http2: close connections when receiving too many headers

Note: requires changes from tinygo PR#4685.
2025-02-18 23:34:09 +01:00
John Hobbs 6281f36662 MAX6675 example & smoke test 2025-02-13 18:26:26 +01:00
John Hobbs ccfcfa837e Add MAX6675 device 2025-02-13 18:26:26 +01:00
Amken USA 87c205fd65 Added TMC2209 support (#727)
TMC2209: Added TMC2209 support
2025-01-28 12:42:18 +01:00
Anastasios Papalyras f57b5ecee9 Add sharpmem (#724)
sharpmem: add implementation of sharpmem display driver

* Implement Configure, Clear and ClearBuffer, add some tests, add documentation/comments
* Reverse white
* Inverted bits, fix and improve ClearBuffer, cleanup
* Refine doc comment
* Driver refactor, optimizations toggle, additional tests & support for all SKUs
* Fix address overflow padding, add wire-level tests for assumed address encoding
* Minor rename
* Cleanup and doc fixes
* Add device configs
* Bounds check
* Add example and smoketest entry
* Use uf2 output file format
* Refine example
2025-01-28 12:17:12 +01:00
Amken USA 486949686c Added TMC5160 support (#725)
TMC5160: Added TMC5160 support

* Added example code for tmc5160 and smoke test
* Update go.mod
* Updated mod file
* Cleaned up commented out code and updated readme.
* ran go fmt
* Fixed setrampspeed func
* Removed spi1 pin setup in example.go. Renamed SPIComm to spicomm.go
* Removed unused test file
* Removed commented line
2025-01-18 07:49:00 +01:00
Triston Whetten 00578a3a81 Fixed bug in aht20 driver 2025-01-18 07:45:09 +01:00
ehime-iyokan 17f273243d hub75: fix data buffering 2025-01-04 16:20:10 +01:00
deadprogram 0623bb425c chore: update microphone driver to use latest i2s interface
Signed-off-by: deadprogram <ron@hybridgroup.com>
2024-12-18 16:42:00 +01:00
deadprogram 019bbbe5fb Prepare for release 0.29
Signed-off-by: deadprogram <ron@hybridgroup.com>
2024-10-30 17:02:40 +00:00
deadprogram d04e68bef8 bugfix: correct casting of return value from adc as it was always overflowing the 8 bit result
Signed-off-by: deadprogram <ron@hybridgroup.com>
2024-10-29 07:28:29 +01:00
Ayke van Laethem 76a4276b5d pixels: do more extensive testing for roundtrip bugs between Set and Get 2024-10-28 09:07:15 +00:00
Ayke van Laethem 7d7efe25e7 pixel: fix Monochrome setPixel
Set/setPixel and Get weren't using the same indices. I've taken the ones
used in Get and applied them to setPixel too.
This fixes testImageNoise for the monochrome image.
2024-10-28 09:07:15 +00:00
deadprogram 0186d0905d pixel: remove monochrome image from random noise test
Signed-off-by: deadprogram <ron@hybridgroup.com>
2024-10-28 05:58:53 +00:00
deadprogram 2f3b5ca59a pixel: correct and clarify code for monochrome get/set pixels
Signed-off-by: deadprogram <ron@hybridgroup.com>
2024-10-28 05:58:53 +00:00
deadprogram ecae5e28ad pixel: add NewImageFromBytes() function to allow creating image from existing slice
Signed-off-by: deadprogram <ron@hybridgroup.com>
2024-10-28 05:58:53 +00:00
Daniel Esteban bcf3b84654 Revert "pixel: add NewImageFromBytes function (#713)" (#714)
This reverts commit 4fb5d7a0e5.
2024-10-28 01:11:52 +01:00
Ron Evans 4fb5d7a0e5 pixel: add NewImageFromBytes function (#713)
* pixel: add NewImageFromBytes() function to allow creating image from existing slice

Signed-off-by: deadprogram <ron@hybridgroup.com>

* pixel: correct and clarify code for monochrome get/set pixels

Signed-off-by: deadprogram <ron@hybridgroup.com>

---------

Signed-off-by: deadprogram <ron@hybridgroup.com>
2024-10-28 00:59:39 +01:00
deadprogram f12454d4f7 uc8151: add FillRectangle() and SetScroll() functions to satisfy tinyterm.Displayer interface
Signed-off-by: deadprogram <ron@hybridgroup.com>
2024-10-27 23:23:31 +00:00
deadprogram 829ae09651 ssd1306: add FillRectangle() and SetScroll() functions to satisfy tinyterm.Displayer interface
Signed-off-by: deadprogram <ron@hybridgroup.com>
2024-10-28 00:09:01 +01:00
Ayke van Laethem 30f540c29f touch: add capacitive touch sensing on normal GPIO pins
Tested on the following chips/boards:

  * RP2040 (Raspberry Pi Pico)
  * ATSAMD21 (Adafruit PyBadge)
  * NRF52840 (PCA10056 developer board)
  * ESP8266 (NodeMCU)
  * ATmega328p (Arduino Uno)
  * ESP32C3 (WaveShare ESP-C3-32S-Kit)
  * FE310 (SiFive HiFive1 rev B)

The sensitivity threshold in the example may need to be adjusted per
board though, the default value of 100 typically recognizes when a cable
is being touched but the RP2040 for example is capable of doing much
more precise measurements if the power supply is sufficiently
noise-free.
2024-10-25 06:28:03 +01:00
Warren Guy 6d431e0726 Add I2C INA219 driver (#705)
INA219: add INA219 driver
2024-10-24 07:45:33 +02:00
Daniel Esteban f308f8fce0 Add sponsor button to key repositories 2024-10-23 16:22:42 +01:00
PWND0U 1c2b802f47 servo: Add function SetAngleWithMicroseconds (#695)
servo: Add function `SetAngleWithMicroseconds`,Adjust the angle by customizing the control pulse width
2024-10-23 15:56:41 +02:00
Marcin Białoń ce80e7582f epd1in54: add Waveshare 1.54inch B/W e-Paper display (#704)
epd1in54: add Waveshare 1.54inch B/W e-Paper display
2024-10-23 13:43:30 +02:00
HattoriHanzo031 109cab4f3b onewire improvements 2024-10-23 12:32:32 +01:00
Greg Herlein 07216d3051 expanded README to discuss need to change variables in examples 2024-08-25 20:31:26 +02:00
たぬき d688fa3f3f ssd1306: Add function SetFlip and GetFlip (#702)
* ssd1306: Add SetRotation function
2024-08-19 01:45:59 +02:00
Greg Herlein dd44f9220b removed what seems like an accidental home directory replace in go.mod 2024-08-14 20:00:38 +02:00
Ron Evans ee3842f639 pcf8591: add ADC only implementation for I2C ADC/DAC (#690)
Signed-off-by: deadprogram <ron@hybridgroup.com>
2024-07-01 22:06:25 +09:00
deadprogram 1bf1a11067 all: prepare release v0.28.0
Signed-off-by: deadprogram <ron@hybridgroup.com>
2024-06-18 11:38:02 +02:00
leongross 848f5def03 nit: increase verbosity of espat NetNotify error (#684)
espat: increase verbosity of espat NetNotify error
2024-06-14 17:36:07 +02:00
Thomas 4a0bcba87c epd2in66b: Waveshare 2.66inch E-Paper Display Module (B) for Raspberry Pi Pico (#673)
epd2in66b: add working driver

Co-authored-by: Thomas Richner <thomas.richner@oviva.com>
2024-06-10 13:16:45 +02:00
Ayke van Laethem 7d983647ad pixel: fix Image[Monochrome].Set for larger images
For bigger images, the pixel index might not fit in a int16. Therefore,
int is needed during the calculation.

While fixing this bug, I've added a few tests that verify the Image
implementation by creating images, filling them with random data, and
then checking whether they still contain the same data. This test failed
before the patch.
2024-05-25 17:09:48 +02:00
deadprogram 831982ad33 servo: add function SetAngle() to simplify API for most common use case
Signed-off-by: deadprogram <ron@hybridgroup.com>
2024-05-25 08:34:52 +02:00
deadprogram 9063f46313 uc8151: improvements to speed and also add flicker-free mode based on @antirez code example
Signed-off-by: deadprogram <ron@hybridgroup.com>
2024-05-13 22:45:37 +02:00
gmsec 2a621dc3b1 Add driver for MCP9808 i2c temperature sensor (#676)
mcp9808: add support for temperature sensor
2024-05-12 10:42:03 +02:00
deadprogram 7dbca2a543 uc8151: correct DrawBitmap() also refactor SendCommand() and SendData() for clarity
Signed-off-by: deadprogram <ron@hybridgroup.com>
2024-05-09 15:37:57 +02:00
deadprogram 50b6f18cc2 uc8151: update to support all functions needed by tinygl and board package Displayer interface
Signed-off-by: deadprogram <ron@hybridgroup.com>
2024-05-08 16:35:21 +02:00
sago35 bb0e6b8ba8 Fix typo and move initialization of neo to init() 2024-05-06 11:19:19 +02:00
sago35 5eeba138a4 Simplify examples/ws2812 2024-05-06 11:19:19 +02:00
deadprogram 1095629f62 ssd1306: add Sleep() function for Displayer interface
Signed-off-by: deadprogram <ron@hybridgroup.com>
2024-04-24 13:00:20 +02:00
deadprogram bf0b383176 ssd1306: add rotation functions for Displayer interface
Signed-off-by: deadprogram <ron@hybridgroup.com>
2024-04-24 13:00:20 +02:00
Elias Naur f206f6f56f ft6336: ignore bogus touch events
At least one ft6336 device reports all 255 values from the first read
after reset or poweron, even after waiting the specified 300ms reset
delay.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
2024-04-21 12:00:03 +02:00
deadprogram a0293643b3 ssd1306: add DrawBitmap() function to complete Displayer interface
Signed-off-by: deadprogram <ron@hybridgroup.com>
2024-04-21 11:52:21 +02:00
deadprogram 60801ba852 pixel: add support for Monochrome types such as the SSD1306 display
Signed-off-by: deadprogram <ron@hybridgroup.com>
2024-04-13 15:50:30 +02:00
deadprogram a74770b2e6 rtl8720dn: implement ConnectModeAP
Signed-off-by: deadprogram <ron@hybridgroup.com>
2024-04-01 07:40:46 +02:00
deadprogram 3ed09d3fe1 wifinina: implement ConnectModeAP
Signed-off-by: deadprogram <ron@hybridgroup.com>
2024-04-01 07:40:46 +02:00
sago35 22581dfd58 encoders: add atsamd21, atsamd51, atsame5x 2024-02-29 15:29:41 +01:00
179 changed files with 13093 additions and 1260 deletions
+2
View File
@@ -0,0 +1,2 @@
# These are supported funding model platforms
open_collective: tinygo
+160
View File
@@ -1,3 +1,163 @@
0.33.0
---
- **new devices**
- **ens160**
- Add ens160 i2c driver
- **lsm303dlhc**
- added support for LSM303DLHC e-Compass; (#783)
- **seesaw**
- add support for Adafruit Seesaw encoders
- **enhancements**
- **ws2812**
- add RP2350 support
- **ssd1306**
- avoid unnecessary heap allocations (#767)
- **gps**
- allow gps init with address
- **lsm6ds3tr**
- avoid unnecessary heap allocations (#766)
- **bugfixes**
- **gps**
- Fix gps time calculation (#785)
0.32.0
---
- **enhancements**
- **bmp280**
- remove alloc on read sensor data
- **ws2812**
- add 200MHz support for the Cortex-M0/rp2040
- **bugfixes**
- **ssd1306**
- remove time.Sleep from SSD1306 SPI transfer code
- **tmc2209**
- tmc2209 bug fixes (#755)
- **docs**
- **contributing**
- add driver design pointer to CONTRIBUTING.md
0.31.0
---
---
- **enhancements**
- **spi**
- update all SPI usage to use either *machine.SPI or drivers.SPI
0.30.0
---
- **new devices**
- **comboat**
- Add wifi driver comboat for Elecrow W5 rp2040 and rp2350 devices (#741)
- **max6675**
- Add MAX6675 device
- **TMC2209**
- Added TMC2209 support (#727)
- **TMC5160**
- Added TMC5160 support (#725)
- **sharpmem**
- Add sharpmem (#724)
- **enhancements**
- **net**
- move to latest golang.org/x/net v0.33.0 (#732)
- **microphone**
- update microphone driver to use latest i2s interface
- **bugfixes**
- **net**
- fix typo in DHCP error message
- **aht20**
- Fixed bug in aht20 driver
- **hub75**
- fix data buffering
0.29.0
---
- **new devices**
- **epd1in54**
- Waveshare 1.54inch B/W e-Paper display (#704)
- **touch**
- add capacitive touch sensing on normal GPIO pins
- **INA219**
- I2C INA219 driver (#705)
- **pcf8591**
- add ADC only implementation for I2C ADC/DAC (#690)
- **enhancements**
- **pixel**
- add NewImageFromBytes() function to allow creating image from existing slice
- **servo**
- Add function `SetAngleWithMicroseconds` (#695)
- **onewire**
- onewire improvements
- **ssd1306**
- Add function `SetFlip` and `GetFlip` (#702)
- **uc8151**
- add FillRectangle() and SetScroll() functions to satisfy tinyterm.Displayer interface
- **ssd1306**
- add FillRectangle() and SetScroll() functions to satisfy tinyterm.Displayer interface
- **bugfixes**
- **pixel**
- fix Monochrome setPixel
- **docs**
- **readme**
- discuss need to change variables in examples
- **sponsor**
- Add sponsor button to key repositories
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**
+3
View File
@@ -8,6 +8,9 @@ We would like your help to make this project better, so we appreciate any contri
We'd love to get your feedback on getting started with TinyGo. Run into any difficulty, confusion, or anything else? You are not alone. We want to know about your experience, so we can help the next people. Please open a Github issue with your questions, or you can also get in touch directly with us on our Slack channel at [https://gophers.slack.com/messages/CDJD3SUP6](https://gophers.slack.com/messages/CDJD3SUP6).
### Driver design
Before porting or writing a driver from scratch please read **[Driver Design for TinyGo](https://tinygo.org/docs/guides/driver-design)**.
### One of the TinyGo drivers is not working as you expect
Please open a Github issue with your problem, and we will be happy to assist.
+1 -1
View File
@@ -1,4 +1,4 @@
Copyright (c) 2018-2024 The TinyGo Authors. All rights reserved.
Copyright (c) 2018-2025 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
+1 -1
View File
@@ -19,7 +19,7 @@ rwildcard=$(foreach d,$(wildcard $1*),$(call rwildcard,$d/,$2) $(filter $(subst
# 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
EXCLUDE_TESTS = image waveshare-epd/epd2in66b
TESTS = $(filter-out $(addsuffix /%,$(EXCLUDE_TESTS)),$(HAS_TESTS))
unit-test:
+24 -2
View File
@@ -3,7 +3,7 @@
[![PkgGoDev](https://pkg.go.dev/badge/tinygo.org/x/drivers)](https://pkg.go.dev/tinygo.org/x/drivers) [![Build](https://github.com/tinygo-org/drivers/actions/workflows/build.yml/badge.svg?branch=dev)](https://github.com/tinygo-org/drivers/actions/workflows/build.yml)
This package provides a collection of 102 different hardware drivers for devices such as sensors and displays that can be used together with [TinyGo](https://tinygo.org).
This package provides a collection of 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/
@@ -16,7 +16,7 @@ go get tinygo.org/x/drivers
## How to use
Here is an example in TinyGo that uses the BMP180 digital barometer:
Here is an example in TinyGo that uses the BMP180 digital barometer. This example should work on any board that supports I2C:
```go
package main
@@ -53,6 +53,28 @@ func main() {
}
```
## Examples Using GPIO or SPI
If compiling these examples directly you are likely to need to make minor changes to the defined variables to map the pins for the board you are using. For example, this block in main.go:
```golang
var (
spi = machine.SPI0
csPin = machine.D5
)
```
It might not be obvious, but you need to change these to match how you wired your specific board. Constants are [defined for each supported microcontroller](https://tinygo.org/docs/reference/microcontrollers/).
For example, to change the definitions for use on a Raspberry Pi Pico using typical wiring, you might need to do this:
```golang
var (
spi = machine.SPI0
csPin = machine.GP17
)
```
## Contributing
Your contributions are welcome!
+2 -2
View File
@@ -29,7 +29,7 @@ func New(bus drivers.I2C) Device {
func (d *Device) Configure() {
// Check initialization state
status := d.Status()
if status&0x08 == 1 {
if status&STATUS_CALIBRATED == 1 {
// Device is initialized
return
}
@@ -69,7 +69,7 @@ func (d *Device) Read() error {
}
// If measurement complete, store values
if data[0]&0x04 != 0 && data[0]&0x80 == 0 {
if data[0]&STATUS_CALIBRATED != 0 && data[0]&STATUS_BUSY == 0 {
d.humidity = uint32(data[1])<<12 | uint32(data[2])<<4 | uint32(data[3])>>4
d.temp = (uint32(data[3])&0xF)<<16 | uint32(data[4])<<8 | uint32(data[5])
return nil
+6 -3
View File
@@ -5,9 +5,9 @@ package apa102 // import "tinygo.org/x/drivers/apa102"
import (
"image/color"
"machine"
"tinygo.org/x/drivers"
"tinygo.org/x/drivers/internal/legacy"
)
const (
@@ -37,8 +37,11 @@ func New(b drivers.SPI) *Device {
// NewSoftwareSPI returns a new APA102 driver that will use a software based
// implementation of the SPI protocol.
func NewSoftwareSPI(sckPin, sdoPin machine.Pin, delay uint32) *Device {
return New(&bbSPI{SCK: sckPin, SDO: sdoPin, Delay: delay})
func NewSoftwareSPI(sckPin, sdoPin drivers.PinOutput, delay uint32) *Device {
return New(&bbSPI{SCK: sckPin.Set, SDO: sdoPin.Set, Delay: delay, configurePins: func() {
legacy.ConfigurePinOut(sckPin)
legacy.ConfigurePinOut(sdoPin)
}})
}
// WriteColors writes the given RGBA color slice out using the APA102 protocol.
+12 -6
View File
@@ -1,6 +1,9 @@
package apa102
import "machine"
import (
"tinygo.org/x/drivers"
"tinygo.org/x/drivers/internal/legacy"
)
// bbSPI is a dumb bit-bang implementation of SPI protocol that is hardcoded
// to mode 0 and ignores trying to receive data. Just enough for the APA102.
@@ -8,15 +11,18 @@ import "machine"
// most purposes other than the APA102 package. It might be desirable to make
// this more generic and include it in the TinyGo "machine" package instead.
type bbSPI struct {
SCK machine.Pin
SDO machine.Pin
Delay uint32
SCK drivers.PinOutputFunc
SDO drivers.PinOutputFunc
Delay uint32
configurePins func()
}
// Configure sets up the SCK and SDO pins as outputs and sets them low
func (s *bbSPI) Configure() {
s.SCK.Configure(machine.PinConfig{Mode: machine.PinOutput})
s.SDO.Configure(machine.PinConfig{Mode: machine.PinOutput})
if s.configurePins == nil {
panic(legacy.ErrConfigBeforeInstantiated)
}
s.configurePins()
s.SCK.Low()
s.SDO.Low()
if s.Delay == 0 {
+30 -23
View File
@@ -1,31 +1,35 @@
package bmi160
import (
"machine"
"time"
"tinygo.org/x/drivers"
"tinygo.org/x/drivers/internal/legacy"
)
// DeviceSPI is the SPI interface to a BMI160 accelerometer/gyroscope. There is
// also an I2C interface, but it is not yet supported.
type DeviceSPI struct {
// Chip select pin
CSB machine.Pin
csb drivers.PinOutputFunc
buf [7]byte
// SPI bus (requires chip select to be usable).
Bus drivers.SPI
bus drivers.SPI
configurePins func()
}
// NewSPI returns a new device driver. The pin and SPI interface are not
// touched, provide a fully configured SPI object and call Configure to start
// using this device.
func NewSPI(csb machine.Pin, spi drivers.SPI) *DeviceSPI {
func NewSPI(csb drivers.PinOutput, spi drivers.SPI) *DeviceSPI {
return &DeviceSPI{
CSB: csb, // chip select
Bus: spi,
csb: csb.Set, // chip select
bus: spi,
configurePins: func() {
legacy.ConfigurePinOut(csb)
},
}
}
@@ -33,8 +37,11 @@ func NewSPI(csb machine.Pin, spi drivers.SPI) *DeviceSPI {
// configures the BMI160, but it does not configure the SPI interface (it is
// assumed to be up and running).
func (d *DeviceSPI) Configure() error {
d.CSB.Configure(machine.PinConfig{Mode: machine.PinOutput})
d.CSB.High()
if d.configurePins == nil {
return legacy.ErrConfigBeforeInstantiated
}
d.configurePins()
d.csb.High()
// The datasheet recommends doing a register read from address 0x7F to get
// SPI communication going:
@@ -86,9 +93,9 @@ func (d *DeviceSPI) ReadTemperature() (temperature int32, err error) {
data[0] = 0x80 | reg_TEMPERATURE_0
data[1] = 0
data[2] = 0
d.CSB.Low()
err = d.Bus.Tx(data, data)
d.CSB.High()
d.csb.Low()
err = d.bus.Tx(data, data)
d.csb.High()
if err != nil {
return
}
@@ -123,9 +130,9 @@ func (d *DeviceSPI) ReadAcceleration() (x int32, y int32, z int32, err error) {
for i := 1; i < len(data); i++ {
data[i] = 0
}
d.CSB.Low()
err = d.Bus.Tx(data, data)
d.CSB.High()
d.csb.Low()
err = d.bus.Tx(data, data)
d.csb.High()
if err != nil {
return
}
@@ -153,9 +160,9 @@ func (d *DeviceSPI) ReadRotation() (x int32, y int32, z int32, err error) {
for i := 1; i < len(data); i++ {
data[i] = 0
}
d.CSB.Low()
err = d.Bus.Tx(data, data)
d.CSB.High()
d.csb.Low()
err = d.bus.Tx(data, data)
d.csb.High()
if err != nil {
return
}
@@ -201,9 +208,9 @@ func (d *DeviceSPI) readRegister(address uint8) uint8 {
data := d.buf[:2]
data[0] = 0x80 | address
data[1] = 0
d.CSB.Low()
d.Bus.Tx(data, data)
d.CSB.High()
d.csb.Low()
d.bus.Tx(data, data)
d.csb.High()
return data[1]
}
@@ -217,7 +224,7 @@ func (d *DeviceSPI) writeRegister(address, data uint8) {
buf[0] = address
buf[1] = data
d.CSB.Low()
d.Bus.Tx(buf, buf)
d.CSB.High()
d.csb.Low()
d.bus.Tx(buf, buf)
d.csb.High()
}
+7 -8
View File
@@ -23,6 +23,7 @@ type Filter uint
type Device struct {
bus drivers.I2C
Address uint16
buf [6]byte
cali calibrationCoefficients
Temperature Oversampling
Pressure Oversampling
@@ -134,8 +135,8 @@ func (d *Device) PrintCali() {
// ReadTemperature returns the temperature in celsius milli degrees (°C/1000).
func (d *Device) ReadTemperature() (temperature int32, err error) {
data, err := d.readData(REG_TEMP, 3)
if err != nil {
data := d.buf[:3]
if err = d.readData(REG_TEMP, data); err != nil {
return
}
@@ -158,8 +159,8 @@ func (d *Device) ReadTemperature() (temperature int32, err error) {
// ReadPressure returns the pressure in milli pascals (mPa).
func (d *Device) ReadPressure() (pressure int32, err error) {
// First 3 bytes are Pressure, last 3 bytes are Temperature
data, err := d.readData(REG_PRES, 6)
if err != nil {
data := d.buf[:6]
if err = d.readData(REG_PRES, data); err != nil {
return
}
@@ -203,7 +204,7 @@ func (d *Device) ReadPressure() (pressure int32, err error) {
}
// readData reads n number of bytes of the specified register
func (d *Device) readData(register int, n int) ([]byte, error) {
func (d *Device) readData(register int, data []byte) error {
// If not in normal mode, set the mode to FORCED mode, to prevent incorrect measurements
// After the measurement in FORCED mode, the sensor will return to SLEEP mode
if d.Mode != MODE_NORMAL {
@@ -218,9 +219,7 @@ func (d *Device) readData(register int, n int) ([]byte, error) {
}
// Read the requested register
data := make([]byte, n)
err := legacy.ReadRegister(d.bus, uint8(d.Address), uint8(register), data[:])
return data, err
return legacy.ReadRegister(d.bus, uint8(d.Address), uint8(register), data[:])
}
// convert3Bytes converts three bytes to int32
+7 -7
View File
@@ -2,22 +2,22 @@
package buzzer // import "tinygo.org/x/drivers/buzzer"
import (
"machine"
"time"
"tinygo.org/x/drivers"
)
// Device wraps a GPIO connection to a buzzer.
type Device struct {
pin machine.Pin
pin drivers.PinOutputFunc
High bool
BPM float64
}
// New returns a new buzzer driver given which pin to use
func New(pin machine.Pin) Device {
func New(pin drivers.PinOutput) Device {
return Device{
pin: pin,
pin: pin.Set,
High: false,
BPM: 96.0,
}
@@ -25,14 +25,14 @@ func New(pin machine.Pin) Device {
// On sets the buzzer to a high state.
func (l *Device) On() (err error) {
l.pin.Set(true)
l.pin.High()
l.High = true
return
}
// Off sets the buzzer to a low state.
func (l *Device) Off() (err error) {
l.pin.Set(false)
l.pin.Low()
l.High = false
return
}
+711
View File
@@ -0,0 +1,711 @@
// Package comboat implements WiFi driver for the Aithinker-Combo-AT WiFi
// device found on the Elecrow W5 rp2040 and rp2350 devices. Ths WiFi device
// is a RTL8720d variant. The driver interface is via AT command set over UART
// (see reference docs below).
//
// NOTE: the driver doesn't support UDP/TCP server connections in STA mode,
// currently. UDP/TCP/TLS client connections are supported in STA mode.
//
// https://aithinker-combo-guide.readthedocs.io/en/latest/docs/instruction/index.html
// https://aithinker-combo-guide.readthedocs.io/en/latest/docs/command-set/index.html
// https://aithinker-combo-guide.readthedocs.io/en/latest/docs/command-examples/index.html
package comboat // import "tinygo.org/x/drivers/comboat"
import (
"bytes"
"errors"
"fmt"
"io"
"machine"
"net"
"net/netip"
"strconv"
"sync"
"time"
"tinygo.org/x/drivers"
"tinygo.org/x/drivers/netdev"
"tinygo.org/x/drivers/netlink"
)
type Config struct {
BaudRate uint32
Uart *machine.UART
Tx machine.Pin
Rx machine.Pin
}
type socket struct {
protocol int
id string
rx chan []byte
remainder []byte
laddr netip.AddrPort // Set in Bind()
}
type device struct {
cfg *Config
uart *machine.UART
uartMu sync.Mutex
mac net.HardwareAddr
ip netip.Addr
gateway netip.Addr
buf [1500]byte
pos int
last []byte
ok chan bool
txReady chan bool
accept chan string
err chan error
sockets [8]*socket
sync.Mutex
}
func NewDevice(cfg *Config) *device {
return &device{
cfg: cfg,
ok: make(chan bool),
txReady: make(chan bool),
accept: make(chan string),
err: make(chan error),
}
}
func logDebug(msg string) {
//println("[DEBUG] " + msg)
}
func logError(msg string) {
println("[ERROR] " + msg)
}
func split(resp []byte, part int, del, on string) string {
parts := bytes.Split(resp, []byte(del))
if part >= len(parts) {
return "Split parts error getting " + on
}
return string(parts[part])
}
func (d *device) getFWVersion() string {
return split(d.last, 1, ":", "FW version")
}
func (d *device) saveMAC() {
raw := split(d.last, 1, ":", "MAC")
if len(raw) > 11 {
macStr := fmt.Sprintf("%s:%s:%s:%s:%s:%s",
raw[0:2], raw[2:4], raw[4:6],
raw[6:8], raw[8:10], raw[10:12])
d.mac, _ = net.ParseMAC(macStr)
}
}
var countryCodes = map[int]string{
1: "JP Japan",
2: "American Samoa",
3: "CA Canada",
4: "US",
5: "CN China",
6: "Hong Kong, China",
7: "Taiwan, China",
8: "MO Macau, China",
9: "IL Israel",
10: "Singapore",
11: "KR South Korea",
12: "TR Türkiye",
13: "AU Australia",
14: "ZA South Africa",
15: "BR Brazil",
}
func (d *device) getCountry() (code string) {
code = split(d.last, 1, ":", "county code")
codeNum, err := strconv.Atoi(code)
if err != nil {
return
}
if val, ok := countryCodes[codeNum]; ok {
code = val
}
return
}
func (d *device) saveIP() {
ipStr := split(d.last, 7, ",", "IP address")
gwStr := split(d.last, 8, ",", "gateway address")
d.ip, _ = netip.ParseAddr(ipStr)
d.gateway, _ = netip.ParseAddr(gwStr)
}
func (d *device) execute(cmd string, timeout int) (err error) {
logDebug("EXECUTE " + cmd)
d.uartMu.Lock()
_, err = d.uart.Write([]byte(cmd + "\r\n"))
d.uartMu.Unlock()
if err != nil {
return
}
t := time.NewTicker(time.Duration(timeout) * time.Millisecond)
defer t.Stop()
select {
case <-t.C:
return errors.New("Timed out")
case <-d.ok:
return
case err = <-d.err:
return
}
}
func (d *device) send(cmd string, timeout int) (err error) {
logDebug("EXECUTE " + cmd)
d.uartMu.Lock()
_, err = d.uart.Write([]byte(cmd + "\r\n"))
d.uartMu.Unlock()
if err != nil {
return
}
t := time.NewTicker(time.Duration(timeout) * time.Millisecond)
defer t.Stop()
select {
case <-t.C:
return errors.New("Timed out")
case <-d.txReady:
return
case err = <-d.err:
return
}
}
func (d *device) findSocket(id string) (*socket, error) {
for _, s := range d.sockets {
if s.id == id {
return s, nil
}
}
return nil, errors.New("Socket not found with id: " + id)
}
func (d *device) getSocket(sockfd int) (*socket, error) {
if sockfd < 0 || sockfd+1 > len(d.sockets) {
return nil, netdev.ErrInvalidSocketFd
}
if d.sockets[sockfd] == nil {
return nil, netdev.ErrInvalidSocketFd
}
return d.sockets[sockfd], nil
}
func (d *device) handle(event []byte) {
logDebug("GOT EVENT " + string(event))
switch {
// SocketDisconnect,<id>
case bytes.HasPrefix(event, []byte("SocketDisconnect")):
id := split(event, 1, ",", "SocketDisconnect")
s, err := d.findSocket(id)
if err == nil {
close(s.rx) // Sends io.EOF
}
// SocketSeed,<id>,<server id>
case bytes.HasPrefix(event, []byte("SocketSeed,2,1")):
//d.uart.Write([]byte("AT+SOCKET?" + "\r\n"))
}
}
func (d *device) processUART() {
if d.pos == 1 && d.buf[0] == '>' {
d.pos = 0
logDebug("GOT >")
d.txReady <- true
}
sofar := d.buf[:d.pos]
if !bytes.HasSuffix(sofar, []byte("\r\n")) {
return
}
// Strip CR/LF off end
sofar = sofar[:len(sofar)-2]
switch {
case bytes.HasPrefix(sofar, []byte("+EVENT:SocketDown")):
// +EVENT:SocketDown,<id>,<length>,<data>
parts := bytes.SplitN(sofar, []byte(","), 4)
if len(parts) != 4 {
logError("Error parsing +EVENT:SocketDown: " + string(sofar))
d.pos = 0
return
}
id := string(parts[1])
length, err := strconv.Atoi(string(parts[2]))
if err != nil {
logError("Error parsing length from: " + string(parts[2]))
d.pos = 0
return
}
if length != len(parts[3]) {
// This can happen if <data> actually contains a CR/LF.
// Return without resetting d.pos to continue reading
// in the full <data>.
return
}
s, err := d.findSocket(id)
if err != nil {
logError(err.Error())
d.pos = 0
return
}
logDebug("GOT +EVENT:SocketDown," + id + "," + string(parts[2]))
d.pos = 0
data := make([]byte, len(parts[3]))
copy(data, parts[3])
s.rx <- data
case bytes.HasPrefix(sofar, []byte("OK")):
d.pos = 0
logDebug("GOT OK")
d.ok <- true
case bytes.HasPrefix(sofar, []byte("ERROR")):
d.pos = 0
logDebug("GOT ERROR")
errStr := getErrStr(d.last)
d.err <- errors.New(errStr)
case bytes.HasPrefix(sofar, []byte("+EVENT:")):
d.pos = 0
event := sofar[len("+EVENT:"):]
d.handle(event)
default:
// Catch everything else and store in d.last
d.pos = 0
size := len(sofar)
if size > 0 {
d.last = make([]byte, size)
copy(d.last, sofar[:size])
logDebug("GOT LINE " + string(d.last))
}
}
}
func (d *device) serviceUART() {
for {
d.uartMu.Lock()
for d.uart.Buffered() > 0 {
if d.pos >= len(d.buf) {
println("Trying to write past buffer")
d.pos = 0
break
}
var err error
d.buf[d.pos], err = d.uart.ReadByte()
if err == nil {
d.pos++
d.processUART()
}
}
d.uartMu.Unlock()
time.Sleep(10 * time.Millisecond)
}
}
func (d *device) NetConnect(params *netlink.ConnectParams) error {
d.Lock()
defer d.Unlock()
d.uart = d.cfg.Uart
d.uart.Configure(machine.UARTConfig{
BaudRate: d.cfg.BaudRate,
TX: d.cfg.Tx,
RX: d.cfg.Rx,
})
go d.serviceUART()
fmt.Printf("\r\n")
fmt.Printf("TinyGo Combo-AT WiFi network device driver\r\n")
fmt.Printf("\r\n")
fmt.Printf("Driver version : %s\r\n", drivers.Version)
if len(params.Ssid) == 0 {
return netlink.ErrMissingSSID
}
// AT Test to see if device is alive
if err := d.execute("AT", 1000); err != nil {
return err
}
// Disable echo
if err := d.execute("ATE0", 1000); err != nil {
return err
}
// Get FW version
if err := d.execute("AT+GMR", 1000); err != nil {
return err
}
fmt.Printf("Combo-AT firmware version : %s\r\n", d.getFWVersion())
// Get/save MAC addresses
if err := d.execute("AT+CIPSTAMAC_DEF?", 1000); err != nil {
return err
}
d.saveMAC()
fmt.Printf("MAC address : %s\r\n", d.mac.String())
// Set country code US
if err := d.execute("AT+WCOUNTRY=4", 1000); err != nil {
return err
}
// Get country code
if err := d.execute("AT+WCOUNTRY?", 1000); err != nil {
return err
}
fmt.Printf("WiFi country code : %s\r\n", d.getCountry())
// Set Wi-Fi working mode to STA and save to flash
if err := d.execute("AT+WMODE=1,1", 1000); err != nil {
return err
}
// Connect to Wifi AP (keep trying until connected)
fmt.Printf("\r\n")
cmd := "AT+WJAP=" + params.Ssid + "," + params.Passphrase
for {
fmt.Printf("Connecting to WiFi SSID '%s'...", params.Ssid)
if err := d.execute(cmd, 20000); err != nil {
fmt.Printf("FAILED (%s)\r\n", err.Error())
continue
}
break
}
fmt.Printf("CONNECTED\r\n")
// Automatically reconnect to Wi-Fi after power on
if err := d.execute("AT+WAUTOCONN=1", 1000); err != nil {
return err
}
// Get/save IP/gateway addresses
if err := d.execute("AT+WJAP?", 1000); err != nil {
return err
}
d.saveIP()
fmt.Printf("\r\n")
fmt.Printf("DHCP-assigned IP : %s\r\n", d.ip)
fmt.Printf("DHCP-assigned gateway : %s\r\n", d.gateway)
fmt.Printf("\r\n")
// Set socket receiving mode to active
if err := d.execute("AT+SOCKETRECVCFG=1", 1000); err != nil {
return err
}
return nil
}
func (d *device) NetDisconnect() {
d.Lock()
defer d.Unlock()
// Disconnect from WiFi AP
d.execute("AT+WDISCONNECT", 1000)
}
func (d *device) NetNotify(cb func(netlink.Event)) {
fmt.Printf("\r\n%s\r\n", netlink.ErrNotSupported)
}
func (d *device) GetHardwareAddr() (net.HardwareAddr, error) {
return d.mac, nil
}
func (d *device) _getHostByName(name string) (ip netip.Addr, err error) {
if err = d.execute("AT+WDOMAIN="+name, 1000); err != nil {
return
}
ipStr := split(d.last, 1, ":", "host by name")
return netip.ParseAddr(ipStr)
}
func (d *device) GetHostByName(name string) (ip netip.Addr, err error) {
// If it's already a dotted-network address, and not a host name,
// return it
ip, err = netip.ParseAddr(name)
if err == nil {
return
}
d.Lock()
defer d.Unlock()
return d._getHostByName(name)
}
func (d *device) Addr() (netip.Addr, error) {
return d.ip, nil
}
func (d *device) Socket(domain, stype, 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
}
d.Lock()
defer d.Unlock()
// Search for empty slot in sockets array
for fd, s := range d.sockets {
if s == nil {
// Found one
d.sockets[fd] = &socket{
protocol: protocol,
rx: make(chan []byte, 10),
}
return fd, nil
}
}
return -1, netdev.ErrNoMoreSockets
}
func (d *device) Bind(sockfd int, ip netip.AddrPort) error {
d.Lock()
defer d.Unlock()
s, err := d.getSocket(sockfd)
if err != nil {
return err
}
s.laddr = ip
return nil
}
func (d *device) Connect(sockfd int, host string, ip netip.AddrPort) error {
var addr string
var cmd string
d.Lock()
defer d.Unlock()
s, err := d.getSocket(sockfd)
if err != nil {
return err
}
if host == "" {
addr = ip.Addr().String()
} else {
ip, err := d._getHostByName(host)
if err != nil {
return err
}
addr = ip.String()
}
port := strconv.Itoa(int(ip.Port()))
switch s.protocol {
case netdev.IPPROTO_UDP:
cmd = "AT+SOCKET=2," + addr + "," + port
case netdev.IPPROTO_TCP:
cmd = "AT+SOCKET=4," + addr + "," + port
case netdev.IPPROTO_TLS:
cmd = "AT+SOCKET=7," + addr + "," + port
}
if cmd == "" {
return netdev.ErrProtocolNotSupported
}
if err := d.execute(cmd, 20000); err != nil {
return err
}
s.id = split(d.last, 1, "=", "connection ID")
return nil
}
func (d *device) Listen(sockfd, backlog int) error {
// TODO Creating a TCP server socket isn't working when in STA mode,
// TODO returning error "Socket bind error".
// TODO The reference example shows a TCP server example in AP mode.
/*
var cmd string
d.Lock()
defer d.Unlock()
s, err := d.getSocket(sockfd)
if err != nil {
return err
}
port := strconv.Itoa(int(s.laddr.Port()))
switch s.protocol {
case netdev.IPPROTO_UDP:
cmd = "AT+SOCKET=1," + port
case netdev.IPPROTO_TCP:
cmd = "AT+SOCKET=3," + port
}
if cmd == "" {
return netdev.ErrProtocolNotSupported
}
if err := d.execute(cmd, 20000); err != nil {
return err
}
s.id = split(d.last, 1, "=", "connection ID")
*/
return netdev.ErrNotSupported
}
func (d *device) Accept(sockfd int) (int, netip.AddrPort, error) {
return 0, netip.AddrPort{}, netdev.ErrNotSupported
}
func (d *device) Send(sockfd int, buf []byte, flags int, deadline time.Time) (int, error) {
d.Lock()
defer d.Unlock()
s, err := d.getSocket(sockfd)
if err != nil {
return 0, err
}
cmd := fmt.Sprintf("AT+SOCKETSEND=%s,%d", s.id, len(buf))
if err := d.send(cmd, 1000); err != nil {
return 0, err
}
// AT+SOCKETSEND will sub-packet send data into 1024-byte chunks,
// automatically, so send the full buffer in one shot, even if it's
// bigger than 1024 bytes.
d.uartMu.Lock()
n, err := d.uart.Write(buf)
d.uartMu.Unlock()
if err != nil {
return 0, err
}
// Expecting "OK" after good send, or "ERROR"
t := time.NewTicker(time.Duration(1000) * time.Millisecond)
defer t.Stop()
select {
case <-t.C:
return 0, errors.New("Timed out")
case <-d.ok:
return n, nil
case err = <-d.err:
return 0, err
}
}
func (d *device) Recv(sockfd int, buf []byte, flags int, deadline time.Time) (int, error) {
d.Lock()
defer d.Unlock()
s, err := d.getSocket(sockfd)
if err != nil {
return 0, err
}
// 1. Use leftover data first
if len(s.remainder) > 0 {
n := copy(buf, s.remainder)
s.remainder = s.remainder[n:]
return n, nil
}
// 2. Get new data from the channel
data, ok := <-s.rx
if !ok {
// Socket closed, return EOF
return 0, io.EOF
}
// 3. Copy data, handle leftovers
n := copy(buf, data)
if n < len(data) {
s.remainder = data[n:]
}
return n, nil
}
func (d *device) Close(sockfd int) error {
d.Lock()
defer d.Unlock()
s, err := d.getSocket(sockfd)
if err != nil {
return err
}
// Delete socket only if connection was successful (s.id is set)
if s.id != "" {
cmd := fmt.Sprintf("AT+SOCKETDEL=%s", s.id)
if err = d.execute(cmd, 1000); err != nil {
return err
}
}
d.sockets[sockfd] = nil
return nil
}
func (d *device) SetSockOpt(sockfd, level, opt int, value interface{}) error {
return netdev.ErrNotSupported
}
+86
View File
@@ -0,0 +1,86 @@
package comboat
import (
"bytes"
"strconv"
)
var errStrings = map[int]string{
// System framework related error codes
0: "success",
1: "The command is not supported (the combo framework contains the command but the current platform has not transplanted or adapted to support it)",
2: "The command parameters contain unsupported operations (the current platform only supports some operations for this command)",
3: "The instruction format is incorrect (this refers to the wrong number of parameters, for example, two parameters are required, but only one parameter is entered)",
4: "Parameter error (the content of the parameter is wrong, for example, a number between 0 and 9 is required, but 10 or xyz is passed in, which is a parameter error)",
5: "Parameter length error (command length exceeds the maximum supported length)",
31: "The current command has not ended and needs to report the status asynchronously. This value is used by the state machine to determine the use of the command and no message is returned.",
32: "Unknown error (or unhandled error type)",
// Common error codes
33: "malloc error",
34: "Failed to read buf",
35: "Failed to write buf",
36: "Configuration error (configuration error loaded from memory, for example, we set port -1 for OTA upgrade, and check port error when executing AT+OTA, then configuration error will be reported)",
37: "Failed to create task",
38: "Flash read and write failure",
39: "Serial port configuration error, unsupported baud rate",
40: "Serial port configuration error, unsupported data bits",
41: "Serial port configuration error, unsupported stop bit",
42: "Serial port configuration error, unsupported parity bit",
43: "Serial port configuration error, unsupported flow control",
44: "Serial port configuration failed",
45: "Wrong username/password",
46: "Low power mode error or unsupported low power mode",
47: "Uninitialized configuration data error (including io mapping data)",
63: "General error code (without other information)",
// Wi-Fi related error codes
64: "Wi-Fi not initialized or initialization failed",
65: "Wi-Fi mode error (unable to connect to Wi-Fi in single AP mode)",
66: "Wi-Fi connection failed",
67: "Wi-Fi connection successful, error in obtaining IP (DHCP)",
68: "Failed to obtain encryption method",
69: "The specified AP was not found.",
70: "Wi-Fi scan start failed",
71: "Wi-Fi scan timeout",
72: "Failed to enable AP hotspot",
73: "Failed to obtain the Wi-Fi information of the router or the AP information that you enabled yourself",
74: "The network card (STA/AP) is not running",
75: "Wi-Fi country code error (unsupported Wi-Fi country code)",
76: "The current network configuration mode is wrong.",
95: "Wi-Fi connection unknown error",
// Socket related error codes
96: "Failed to create socket",
97: "Socket connection failed",
98: "DNS Failure",
99: "The socket status is wrong (for example, TCP is not connected yet)",
100: "Socket type error",
101: "Socket send failed",
102: "Socket receive failed",
103: "Socket monitoring thread creation failed",
104: "Socket bind error",
105: "The current connection cannot be transparently linked (wrong socket type or number)",
106: "PING test failed (all packets lost)",
107: "Wi-Fi country code error (unsupported Wi-Fi country code)",
108: "SSL Config Error",
109: "SSL verification error (usually caused by unsupported SSL encryption type or certificate error)",
127: "Unknown socket error",
}
func getErrStr(errLine []byte) (errStr string) {
errStr = "Can't parse ERROR response"
tokens := bytes.Split(errLine, []byte(":"))
if len(tokens) > 1 {
errCode, err := strconv.Atoi(string(tokens[1]))
if err == nil {
errStr = errStrings[errCode]
}
}
return
}
+2 -2
View File
@@ -19,7 +19,7 @@ type OneWireDevice interface {
Write(uint8)
Read() uint8
Select([]uint8) error
Сrc8([]uint8, int) uint8
Сrc8([]uint8) uint8
}
// Device wraps a connection to an 1-Wire devices.
@@ -69,7 +69,7 @@ func (d Device) ReadTemperatureRaw(romid []uint8) ([]uint8, error) {
for i := 0; i < 9; i++ {
spb[i] = d.owd.Read()
}
if d.owd.Сrc8(spb, 8) != spb[8] {
if d.owd.Сrc8(spb) != 0 {
return nil, errReadTemperature
}
return spb[:2:2], nil
+4 -67
View File
@@ -2,9 +2,9 @@
package easystepper // import "tinygo.org/x/drivers/easystepper"
import (
"errors"
"machine"
"time"
"tinygo.org/x/drivers"
)
// StepMode determines the coil sequence used to perform a single step
@@ -30,28 +30,10 @@ func (sm StepMode) stepCount() uint {
}
}
// 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
pins [4]drivers.PinOutputFunc
config func()
stepDelay time.Duration
stepNumber uint8
stepMode StepMode
@@ -62,51 +44,6 @@ type DualDevice struct {
devices [2]*Device
}
// 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
func (d *Device) Configure() {
for _, pin := range d.pins {
pin.Configure(machine.PinConfig{Mode: machine.PinOutput})
}
}
// NewDual returns a new dual easystepper driver given 8 pins, number of steps and rpm
func NewDual(config DualDeviceConfig) (*DualDevice, error) {
// Create the first device
dev1, err := New(config.DeviceConfig)
if err != nil {
return nil, err
}
// 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 composite dual device
return &DualDevice{devices: [2]*Device{dev1, dev2}}, nil
}
// Configure configures the pins of the DualDevice
func (d *DualDevice) Configure() {
d.devices[0].Configure()
d.devices[1].Configure()
}
// Move rotates the motor the number of given steps
// (negative steps will rotate it the opposite direction)
func (d *Device) Move(steps int32) {
+26
View File
@@ -0,0 +1,26 @@
package easystepper
import (
"errors"
"time"
"tinygo.org/x/drivers"
)
func NewCrossPlatform(stepcount, rpm uint, mode StepMode, pins [4]drivers.PinOutputFunc) (*Device, error) {
if stepcount == 0 || rpm == 0 {
return nil, errors.New("zero rpm and/or stepcount")
}
for i := range pins {
if pins[i] == nil {
return nil, errors.New("nil pin")
}
}
d := &Device{
pins: pins,
stepDelay: time.Second * 60 / time.Duration((stepcount * rpm)),
stepMode: mode,
config: func() {},
}
return d, nil
}
+83
View File
@@ -0,0 +1,83 @@
//go:build baremetal
package easystepper
import (
"errors"
"machine"
"time"
"tinygo.org/x/drivers"
"tinygo.org/x/drivers/internal/legacy"
)
// 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]drivers.PinOutputFunc{config.Pin1.Set, config.Pin2.Set, config.Pin3.Set, config.Pin4.Set},
stepDelay: time.Second * 60 / time.Duration((config.StepCount * config.RPM)),
stepMode: config.Mode,
config: func() {
legacy.ConfigurePinOut(config.Pin1)
legacy.ConfigurePinOut(config.Pin2)
legacy.ConfigurePinOut(config.Pin3)
legacy.ConfigurePinOut(config.Pin4)
},
}, nil
}
// Configure configures the pins of the Device
func (d *Device) Configure() {
if d.config == nil {
panic(legacy.ErrConfigBeforeInstantiated)
}
d.config()
}
// Configure configures the pins of the DualDevice
func (d *DualDevice) Configure() {
d.devices[0].Configure()
d.devices[1].Configure()
}
// NewDual returns a new dual easystepper driver given 8 pins, number of steps and rpm
func NewDual(config DualDeviceConfig) (*DualDevice, error) {
// Create the first device
dev1, err := New(config.DeviceConfig)
if err != nil {
return nil, err
}
// 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 composite dual device
return &DualDevice{devices: [2]*Device{dev1, dev2}}, nil
}
// 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
}
+1 -1
View File
@@ -1,4 +1,4 @@
//go:build tinygo && (rp2040 || stm32 || k210 || esp32c3 || nrf || (avr && (atmega328p || atmega328pb)))
//go:build tinygo && (rp2040 || stm32 || k210 || esp32c3 || nrf || sam || (avr && (atmega328p || atmega328pb)))
// Implementation based on:
// https://gist.github.com/aykevl/3fc1683ed77bb0a9c07559dfe857304a
+225
View File
@@ -0,0 +1,225 @@
// Package ens160 provides a driver for the ScioSense ENS160 digital gas sensor.
//
// Datasheet: https://www.sciosense.com/wp-content/uploads/2023/12/ENS160-Datasheet.pdf
package ens160
import (
"encoding/binary"
"errors"
"time"
"tinygo.org/x/drivers"
)
const (
defaultTimeout = 30 * time.Millisecond
shortTimeout = 1 * time.Millisecond
)
// Conversion constants for environment data compensation.
const (
kelvinOffsetMilli = 273150 // 273.15 K in milli-units
tempRawFactor = 64 // As per datasheet for TEMP_IN
humRawFactor = 512 // As per datasheet for RH_IN
milliFactor = 1000 // For converting from milli-units
roundingTerm = milliFactor / 2 // For rounding before integer division
)
// validityStrings provides human-readable descriptions for validity flags.
var validityStrings = [...]string{
ValidityNormalOperation: "normal operation",
ValidityWarmUpPhase: "warm-up phase, wait ~3 minutes for valid data",
ValidityInitialStartUpPhase: "initial start-up phase, wait ~1 hour for valid data",
ValidityInvalidOutput: "invalid output",
}
// Device wraps an I2C connection to an ENS160 device.
type Device struct {
bus drivers.I2C // I²C implementation
addr uint16 // 7bit bus address, promoted to uint16 per drivers.I2C
// shadow registers / last measurements
lastTvocPPB uint16
lastEco2PPM uint16
lastAqiUBA uint8
lastValidity uint8 // Store the latest validity status
// preallocated buffers
wbuf [5]byte // longest write: reg + 4bytes (TEMP+RH)
rbuf [5]byte // longest read: DATA burst (5bytes)
}
// New returns a new ENS160 driver.
func New(bus drivers.I2C, addr uint16) *Device {
if addr == 0 {
addr = DefaultAddress
}
return &Device{
bus: bus,
addr: addr,
lastValidity: ValidityInvalidOutput,
}
}
// Connected returns whether a ENS160 has been found.
func (d *Device) Connected() bool {
d.wbuf[0] = regPartID
err := d.bus.Tx(d.addr, d.wbuf[:1], d.rbuf[:2])
return err == nil && d.rbuf[0] == LowPartID && d.rbuf[1] == HighPartID
}
// Configure sets up the device for reading.
func (d *Device) Configure() error {
// 1. Soft-reset. The device will automatically enter IDLE mode.
if err := d.write1(regOpMode, ModeReset); err != nil {
return err
}
time.Sleep(defaultTimeout)
// 2. Clear GPR registers, then go to STANDARD mode.
if err := d.write1(regCommand, cmdClrGPR); err != nil {
return err
}
time.Sleep(defaultTimeout)
if err := d.write1(regOpMode, ModeStandard); err != nil {
return err
}
time.Sleep(defaultTimeout)
return nil
}
// calculateTempRaw converts temperature from milli-degrees Celsius to the sensor's raw format.
func calculateTempRaw(tempMilliC int32) uint16 {
// Clip temperature
const (
minC = -40 * 1000
maxC = 85 * 1000
)
if tempMilliC < minC {
tempMilliC = minC
} else if tempMilliC > maxC {
tempMilliC = maxC
}
// Integer fixed-point conversion to format required by the sensor.
// Formula from datasheet: T_IN = (T_ambient_C + 273.15) * 64
return uint16((((tempMilliC + kelvinOffsetMilli) * tempRawFactor) + roundingTerm) / milliFactor)
}
// calculateHumRaw converts relative humidity from milli-percent to the sensor's raw format.
func calculateHumRaw(rhMilliPct int32) uint16 {
// Clip humidity
if rhMilliPct < 0 {
rhMilliPct = 0
} else if rhMilliPct > 100*1000 {
rhMilliPct = 100 * 1000
}
// Integer fixed-point conversion to format required by the sensor.
// Formula from datasheet: RH_IN = (RH_ambient_% * 512)
return uint16(((rhMilliPct * humRawFactor) + roundingTerm) / milliFactor)
}
// SetEnvDataMilli sets the ambient temperature and humidity for compensation.
//
// tempMilliC is the temperature in milli-degrees Celsius.
// rhMilliPct is the relative humidity in milli-percent.
func (d *Device) SetEnvDataMilli(tempMilliC, rhMilliPct int32) error {
tempRaw := calculateTempRaw(tempMilliC)
humRaw := calculateHumRaw(rhMilliPct)
d.wbuf[0] = regTempIn // start address (autoincrement)
binary.LittleEndian.PutUint16(d.wbuf[1:3], tempRaw)
binary.LittleEndian.PutUint16(d.wbuf[3:5], humRaw)
return d.bus.Tx(d.addr, d.wbuf[:5], nil)
}
// Update refreshes the concentration measurements.
func (d *Device) Update(which drivers.Measurement) error {
if which&drivers.Concentration == 0 {
return nil // nothing requested
}
const maxTries = 1000
var (
status uint8
validity uint8
)
var gotData bool
// Poll DEVICE_STATUS until NEWDAT or timeout
for range maxTries {
var err error
status, err = d.read1(regStatus)
if err != nil {
return err
}
if status&statusSTATER != 0 {
return errors.New("ENS160: error (STATER set)")
}
validity = (status & statusValidityMask) >> statusValidityShift
if status&statusNEWDAT != 0 {
gotData = true
break // Always break when data available
}
time.Sleep(shortTimeout)
}
if !gotData {
return errors.New("ENS160: timeout waiting for NEWDAT")
}
// Burst-read data regardless of validity state
d.wbuf[0] = regAQI
if err := d.bus.Tx(d.addr, d.wbuf[:1], d.rbuf[:5]); err != nil {
return errors.New("ENS160: burst read failed")
}
d.lastAqiUBA = d.rbuf[0]
d.lastTvocPPB = binary.LittleEndian.Uint16(d.rbuf[1:3])
d.lastEco2PPM = binary.LittleEndian.Uint16(d.rbuf[3:5])
d.lastValidity = validity // Store the validity status
return nil
}
// TVOC returns the last totalVOC concentration in partsperbillion.
func (d *Device) TVOC() uint16 { return d.lastTvocPPB }
// ECO2 returns the last equivalent CO₂ concentration in partspermillion.
func (d *Device) ECO2() uint16 { return d.lastEco2PPM }
// AQI returns the last AirQuality Index according to UBA (15).
func (d *Device) AQI() uint8 { return d.lastAqiUBA }
// Validity returns the current operating state of the sensor.
func (d *Device) Validity() uint8 {
return d.lastValidity
}
// ValidityString returns a human-readable string describing the current validity status.
func (d *Device) ValidityString() string {
if int(d.lastValidity) < len(validityStrings) {
return validityStrings[d.lastValidity]
}
return "unknown"
}
// write1 writes a single byte to a register.
func (d *Device) write1(reg, val uint8) error {
d.wbuf[0] = reg
d.wbuf[1] = val
return d.bus.Tx(d.addr, d.wbuf[:2], nil)
}
// read1 reads a single byte from a register.
func (d *Device) read1(reg uint8) (uint8, error) {
d.wbuf[0] = reg
if err := d.bus.Tx(d.addr, d.wbuf[:1], d.rbuf[:1]); err != nil {
return 0, err
}
return d.rbuf[0], nil
}
+54
View File
@@ -0,0 +1,54 @@
package ens160
import (
"testing"
)
func TestCalculateTempRaw(t *testing.T) {
testCases := []struct {
name string
tempMilliC int32
expectedRaw uint16
}{
{"25°C", 25000, 19082},
{"-10.5°C", -10500, 16810},
{"Min temp", -40000, 14922},
{"Below min", -50000, 14922},
{"Max temp", 85000, 22922},
{"Above max", 90000, 22922},
{"Zero", 0, 17482},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
raw := calculateTempRaw(tc.tempMilliC)
if raw != tc.expectedRaw {
t.Errorf("expected %d, got %d", tc.expectedRaw, raw)
}
})
}
}
func TestCalculateHumRaw(t *testing.T) {
testCases := []struct {
name string
rhMilliPct int32
expectedRaw uint16
}{
{"50%", 50000, 25600},
{"0%", 0, 0},
{"100%", 100000, 51200},
{"Below 0%", -10000, 0},
{"Above 100%", 110000, 51200},
{"33.3%", 33300, 17050},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
raw := calculateHumRaw(tc.rhMilliPct)
if raw != tc.expectedRaw {
t.Errorf("expected %d, got %d", tc.expectedRaw, raw)
}
})
}
}
+65
View File
@@ -0,0 +1,65 @@
package ens160
// DefaultAddress is the default I2C address for the ENS160 when the ADDR pin is
// connected to high (3.3V). When connected to low (GND), the address is 0x52.
const DefaultAddress = 0x53
// Registers
const (
regPartID = 0x00
regOpMode = 0x10
regConfig = 0x11
regCommand = 0x12
regTempIn = 0x13
regRhIn = 0x15
regStatus = 0x20
regAQI = 0x21
regTVOC = 0x22
regECO2 = 0x24
regDataT = 0x30
regDataRH = 0x32
regMISR = 0x38
regGPRWrite = 0x40
regGPRRead = 0x48
)
// Operating modes
const (
ModeDeepSleep = 0x00
ModeIdle = 0x01
ModeStandard = 0x02
ModeReset = 0xF0
)
// Status register bits
const (
statusSTATAS = 1 << 7
statusSTATER = 1 << 6
statusValidityMask = 0x0C
statusValidityShift = 2
statusNEWDAT = 1 << 1
statusNEWGPR = 1 << 0
)
// Validity flags
const (
ValidityNormalOperation = 0x00
ValidityWarmUpPhase = 0x01 // need ~3 minutes until valid data
ValidityInitialStartUpPhase = 0x02 // need ~1 hour until valid data
ValidityInvalidOutput = 0x03
)
// Commands
const (
cmdNOP = 0x00
cmdGetAppVer = 0x0E
cmdClrGPR = 0xCC
)
// Part IDs
const (
LowPartID = 0x60
HighPartID = 0x01
)
+1 -2
View File
@@ -122,7 +122,7 @@ func (d *Device) NetDisconnect() {
}
func (d *Device) NetNotify(cb func(netlink.Event)) {
// Not supported
fmt.Printf("\r\n%s\r\n", netlink.ErrNotSupported)
}
func (d *Device) GetHostByName(name string) (netip.Addr, error) {
@@ -473,7 +473,6 @@ func (d *Device) parseIPD(end int) error {
}
// load up the socket data
//d.data = append(d.data, d.response[e+1:end]...)
d.data = append(d.data, d.response[e+1:e+1+v]...)
return nil
}
+56
View File
@@ -0,0 +1,56 @@
// This example demonstrates ENS160 usage.
//
// Wiring:
// - VCC to 3.3V, GND to ground
// - SDA to board SDA, SCL to board SCL
package main
import (
"time"
"machine"
"tinygo.org/x/drivers"
"tinygo.org/x/drivers/ens160"
)
func main() {
err := machine.I2C0.Configure(machine.I2CConfig{
Frequency: 400 * machine.KHz,
})
if err != nil {
println("Failed to configure I2C:", err)
}
dev := ens160.New(machine.I2C0, ens160.DefaultAddress)
connected := dev.Connected()
if !connected {
println("ENS160 not detected")
return
}
println("ENS160 detected")
if err := dev.Configure(); err != nil {
println("Failed to configure ENS160:", err)
}
for {
err := dev.Update(drivers.Concentration)
if err != nil {
println("Error reading ENS160: %v\n", err)
time.Sleep(5 * time.Second)
continue
}
println(
"AQI:", dev.AQI(),
"TVOC:", dev.TVOC(),
"eCO2:", dev.ECO2(),
"Validity:", dev.ValidityString(),
)
time.Sleep(2 * time.Second)
}
}
+1 -1
View File
@@ -10,7 +10,7 @@ import (
func main() {
console_example.RunFor(
flash.NewSPI(
&machine.SPI1,
machine.SPI1,
machine.SPI1_SDO_PIN,
machine.SPI1_SDI_PIN,
machine.SPI1_SCK_PIN,
+1 -1
View File
@@ -10,7 +10,7 @@ import (
func main() {
println("GPS I2C Example")
machine.I2C0.Configure(machine.I2CConfig{})
ublox := gps.NewI2C(machine.I2C0)
ublox := gps.NewI2CWithAddress(machine.I2C0, gps.UBLOX_I2C_ADDRESS)
parser := gps.NewParser()
var fix gps.Fix
for {
+29
View File
@@ -0,0 +1,29 @@
package main
import (
"machine"
"time"
"tinygo.org/x/drivers/ina219"
)
func main() {
machine.I2C0.Configure(machine.I2CConfig{})
dev := ina219.New(machine.I2C0)
dev.Configure()
for {
busVoltage, shuntVoltage, current, power, err := dev.Measurements()
if err != nil {
println("Error reading measurements", err)
}
println("Bus Voltage:", busVoltage, "V")
println("Shunt Voltage:", shuntVoltage/100, "mV")
println("Current:", current, "mA")
println("Power:", power, "mW")
time.Sleep(10 * time.Millisecond)
}
}
+58
View File
@@ -0,0 +1,58 @@
package main
import (
"machine"
"time"
"tinygo.org/x/drivers/lsm303dlhc"
)
func main() {
// LSM303DLHC is connected to the I2C0 bus on Adafruit Feather M4 via pins: 20(SDA) and 21(SCL).
machine.I2C0.Configure(machine.I2CConfig{})
sensor := lsm303dlhc.New(machine.I2C0)
//default settings
err := sensor.Configure(lsm303dlhc.Configuration{
AccelPowerMode: lsm303dlhc.ACCEL_POWER_NORMAL,
AccelRange: lsm303dlhc.ACCEL_RANGE_2G,
AccelDataRate: lsm303dlhc.ACCEL_DATARATE_100HZ,
MagPowerMode: lsm303dlhc.MAG_POWER_NORMAL,
MagSystemMode: lsm303dlhc.MAG_SYSTEM_CONTINUOUS,
MagDataRate: lsm303dlhc.MAG_DATARATE_10HZ,
})
if err != nil {
for {
println("Failed to configure", err.Error())
time.Sleep(time.Second)
}
}
for {
accel_x, accel_y, accel_z, err := sensor.ReadAcceleration()
if err != nil {
println("Failed to read accel", err.Error())
}
println("ACCEL_X:", accel_x, " ACCEL_Y:", accel_y, " ACCEL_Z:", accel_z)
mag_x, mag_y, mag_z, err := sensor.ReadMagneticField()
if err != nil {
println("Failed to read mag", err.Error())
}
println("MAG_X:", mag_x, " MAG_Y:", mag_y, " MAG_Z:", mag_z)
pitch, roll, _ := sensor.ReadPitchRoll()
println("Pitch:", float32(pitch), " Roll:", float32(roll))
heading, _ := sensor.ReadCompass()
println("Heading:", float32(heading), "degrees")
temp, _ := sensor.ReadTemperature()
println("Temperature:", float32(temp)/1000, "*C")
println("\n")
time.Sleep(time.Millisecond * 250)
}
}
+34
View File
@@ -0,0 +1,34 @@
package main
import (
"fmt"
"machine"
"time"
"tinygo.org/x/drivers/max6675"
)
// example for reading temperature from a thermocouple
func main() {
// Pins are for an Adafruit Feather nRF52840 Express
machine.D5.Configure(machine.PinConfig{Mode: machine.PinOutput})
machine.D5.High()
machine.SPI0.Configure(machine.SPIConfig{
Frequency: 1_000_000,
SCK: machine.SPI0_SCK_PIN,
SDI: machine.SPI0_SDI_PIN,
})
thermocouple := max6675.NewDevice(machine.SPI0, machine.D5)
for {
temp, err := thermocouple.Read()
if err != nil {
println(err)
return
}
fmt.Printf("%0.02f C : %0.02f F\n", temp, (temp*9/5)+32)
time.Sleep(time.Second)
}
}
+48
View File
@@ -0,0 +1,48 @@
package main
import (
"fmt"
"machine"
"time"
"tinygo.org/x/drivers/mcp9808"
)
func main() {
//tinygo monitor
time.Sleep(time.Millisecond * 5000)
//Configure I2C (in this case, I2C0 on RPI Pico), and wire the module accordingly
machine.I2C0.Configure(machine.I2CConfig{
SCL: machine.GP1,
SDA: machine.GP0,
})
//Create sensor
sensor := mcp9808.New(machine.I2C0)
if !sensor.Connected() {
println("MCP9808 not found")
return
} else {
println("MCP9808 found")
}
time.Sleep(time.Millisecond * 1000)
//Set resolution
sensor.SetResolution(mcp9808.Maximum)
time.Sleep(time.Millisecond * 1000)
//Read temp.
temp, err := sensor.ReadTemperature()
if err != nil {
println("MCP9808 error reading temperature")
println(err.Error())
return
} else {
fmt.Printf("Temperature: %.2f \n", temp)
}
return
}
+1 -1
View File
@@ -9,7 +9,7 @@
// examples/net/webclient (for HTTP)
// examples/net/tlsclient (for HTTPS)
//go:build ninafw || wioterminal
//go:build ninafw || wioterminal || comboat_fw
package main
+1 -1
View File
@@ -9,7 +9,7 @@
// examples/net/webclient (for HTTP)
// examples/net/tlsclient (for HTTPS)
//go:build ninafw || wioterminal
//go:build ninafw || wioterminal || comboat_fw
package main
+1 -1
View File
@@ -9,7 +9,7 @@
// examples/net/webclient (for HTTP)
// examples/net/tlsclient (for HTTPS)
//go:build ninafw || wioterminal
//go:build ninafw || wioterminal || comboat_fw
package main
+1 -1
View File
@@ -9,7 +9,7 @@
// examples/net/webclient (for HTTP)
// examples/net/tlsclient (for HTTPS)
//go:build ninafw || wioterminal
//go:build ninafw || wioterminal || comboat_fw
package main
+1 -1
View File
@@ -4,7 +4,7 @@
// Note: It may be necessary to increase the stack size when using
// paho.mqtt.golang. Use the -stack-size=4KB command line option.
//go:build ninafw || wioterminal || challenger_rp2040
//go:build ninafw || wioterminal || challenger_rp2040 || comboat_fw
package main
+1 -1
View File
@@ -4,7 +4,7 @@
// Note: It may be necessary to increase the stack size when using
// paho.mqtt.golang. Use the -stack-size=4KB command line option.
//go:build ninafw || wioterminal || challenger_rp2040
//go:build ninafw || wioterminal || challenger_rp2040 || comboat_fw
package main
+1 -1
View File
@@ -3,7 +3,7 @@
// It creates a UDP connection to request the current time and parse the
// response from a NTP server. The system time is set to NTP time.
//go:build ninafw || wioterminal || challenger_rp2040
//go:build ninafw || wioterminal || challenger_rp2040 || comboat_fw
package main
+1 -1
View File
@@ -1,4 +1,4 @@
//go:build ninafw || wioterminal
//go:build ninafw || wioterminal || comboat_fw
package main
+1 -1
View File
@@ -31,7 +31,7 @@
// func. This forces segments to connect and run concurrently, which is a good
// test of the underlying driver's ability to handle concurrent connections.
//go:build ninafw || wioterminal
//go:build ninafw || wioterminal || comboat_fw
package main
+1 -1
View File
@@ -4,7 +4,7 @@
//
// nc -lk 8080
//go:build ninafw || wioterminal || challenger_rp2040
//go:build ninafw || wioterminal || challenger_rp2040 || comboat_fw
package main
+1 -1
View File
@@ -5,7 +5,7 @@
//
// nc -lk 8080
//go:build ninafw || wioterminal || challenger_rp2040 || pico
//go:build ninafw || wioterminal || challenger_rp2040 || comboat_fw
package main
+1 -1
View File
@@ -5,7 +5,7 @@
//
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security
//go:build ninafw || wioterminal
//go:build ninafw || wioterminal || comboat_fw
package main
+1 -1
View File
@@ -17,7 +17,7 @@
// }
// ---------------------------------------------------------------------------
//go:build ninafw || wioterminal
//go:build ninafw || wioterminal || comboat_fw
package main
+1 -1
View File
@@ -6,7 +6,7 @@
// Note: It may be necessary to increase the stack size when using
// "golang.org/x/net/websocket". Use the -stack-size=4KB command line option.
//go:build ninafw || wioterminal
//go:build ninafw || wioterminal || comboat_fw
package main
+28
View File
@@ -0,0 +1,28 @@
// Connects to a pcf8591 ADC via I2C.
package main
import (
"machine"
"time"
"tinygo.org/x/drivers/pcf8591"
)
var (
i2c = machine.I2C0
)
func main() {
i2c.Configure(machine.I2CConfig{})
adc := pcf8591.New(i2c)
adc.Configure()
// get "CH0" aka "machine.ADC" interface to channel 0 from ADC.
p := adc.CH0
for {
val := p.Get()
println(val)
time.Sleep(50 * time.Millisecond)
}
}
+1 -1
View File
@@ -7,7 +7,7 @@ import (
)
func init() {
spi = &machine.SPI0
spi = machine.SPI0
sckPin = machine.SPI0_SCK_PIN
sdoPin = machine.SPI0_SDO_PIN
sdiPin = machine.SPI0_SDI_PIN
+1 -1
View File
@@ -7,7 +7,7 @@ import (
)
func init() {
spi = &machine.SPI1
spi = machine.SPI1
sckPin = machine.SDCARD_SCK_PIN
sdoPin = machine.SDCARD_SDO_PIN
sdiPin = machine.SDCARD_SDI_PIN
+1 -1
View File
@@ -7,7 +7,7 @@ import (
)
func init() {
spi = &machine.SPI0
spi = machine.SPI0
sckPin = machine.SPI0_SCK_PIN
sdoPin = machine.SPI0_SDO_PIN
sdiPin = machine.SPI0_SDI_PIN
+1 -1
View File
@@ -7,7 +7,7 @@ import (
)
func init() {
spi = &machine.SDCARD_SPI
spi = machine.SDCARD_SPI
sckPin = machine.SDCARD_SCK_PIN
sdoPin = machine.SDCARD_SDO_PIN
sdiPin = machine.SDCARD_SDI_PIN
+1 -1
View File
@@ -7,7 +7,7 @@ import (
)
func init() {
spi = &machine.SPI0
spi = machine.SPI0
sckPin = machine.SPI0_SCK_PIN
sdoPin = machine.SPI0_SDO_PIN
sdiPin = machine.SPI0_SDI_PIN
+1 -1
View File
@@ -7,7 +7,7 @@ import (
)
func init() {
spi = &machine.SPI0
spi = machine.SPI0
sckPin = machine.SPI0_SCK_PIN
sdoPin = machine.SPI0_SDO_PIN
sdiPin = machine.SPI0_SDI_PIN
+1 -1
View File
@@ -7,7 +7,7 @@ import (
)
func init() {
spi = &machine.SPI2
spi = machine.SPI2
sckPin = machine.SCK2
sdoPin = machine.SDO2
sdiPin = machine.SDI2
+35
View File
@@ -0,0 +1,35 @@
package main
import (
"machine"
"time"
"tinygo.org/x/drivers/seesaw"
)
// example reading the position of a rotary encoder (4991) powered by a seesaw
// https://learn.adafruit.com/adafruit-i2c-qt-rotary-encoder/arduino
func main() {
// This assumes you are using an Adafruit QT Py RP2040 for its Stemma QT connector
// https://www.adafruit.com/product/4900
i2c := machine.I2C1
i2c.Configure(machine.I2CConfig{
SCL: machine.I2C1_QT_SCL_PIN,
SDA: machine.I2C1_QT_SDA_PIN,
})
dev := seesaw.New(i2c)
dev.Address = 0x36
for {
time.Sleep(time.Second)
pos, err := dev.GetEncoderPosition(0, false)
if err != nil {
println(err)
continue
}
println(pos)
}
}
+19 -13
View File
@@ -25,19 +25,25 @@ func main() {
return
}
println("setting to 0°")
s.SetMicroseconds(1000)
time.Sleep(3 * time.Second)
println("setting to 45°")
s.SetMicroseconds(1500)
time.Sleep(3 * time.Second)
println("setting to 90°")
s.SetMicroseconds(2000)
time.Sleep(3 * time.Second)
for {
time.Sleep(time.Second)
println("setting to 0°")
s.SetAngle(0)
time.Sleep(3 * time.Second)
println("setting to 45°")
s.SetAngle(45)
time.Sleep(3 * time.Second)
println("setting to 90°")
s.SetAngle(90)
time.Sleep(3 * time.Second)
println("setting to 135°")
s.SetAngle(135)
time.Sleep(3 * time.Second)
println("setting to 180°")
s.SetAngle(180)
time.Sleep(3 * time.Second)
}
}
+90
View File
@@ -0,0 +1,90 @@
package main
import (
"image/color"
"machine"
"math/rand/v2"
"time"
"tinygo.org/x/drivers/sharpmem"
)
var (
// example wiring using a nice!view and nice!nano:
// (view) (nano)
// MOSI --> P0.24
// SCK ---> P0.22
// GND ---> GND
// VCC ---> 3.3V
// CS ----> P0.06
spi = machine.SPI0
sckPin = machine.SPI0_SCK_PIN // SCK
sdoPin = machine.SPI0_SDO_PIN // MOSI
sdiPin = machine.SPI0_SDI_PIN // (any pin)
csPin = machine.P0_06 // CS
)
func main() {
time.Sleep(time.Second)
err := spi.Configure(machine.SPIConfig{
Frequency: 2000000,
SCK: sckPin,
SDO: sdoPin,
SDI: sdiPin,
Mode: 0,
LSBFirst: true,
})
if err != nil {
println("spi.Configure() failed, error:", err.Error())
return
}
csPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
display := sharpmem.New(spi, csPin)
cfg := sharpmem.ConfigLS011B7DH03
display.Configure(cfg)
// clear the display before first use
err = display.Clear()
if err != nil {
println("display.Clear() failed, error:", err.Error())
return
}
// random boxes pop into and out of existence
for {
x0 := int16(rand.IntN(int(cfg.Width - 7)))
y0 := int16(rand.IntN(int(cfg.Height - 7)))
for x2 := int16(0); x2 < 16; x2++ {
x2 := x2
c := color.RGBA{R: 255, G: 255, B: 255, A: 255}
if x2 >= 8 {
// effectively erases the box after it showed up
x2 = x2 - 8
c = color.RGBA{R: 0, G: 0, B: 0, A: 255}
}
for x := int16(0); x < x2; x++ {
for y := int16(0); y < 8; y++ {
display.SetPixel(x0+x, y0+y, c)
}
}
err = display.Display()
if err != nil {
println("display.Display() failed, error:", err.Error())
continue
}
time.Sleep(33 * time.Millisecond)
}
}
}
-51
View File
@@ -1,51 +0,0 @@
package main
import (
"machine"
"image/color"
"time"
"tinygo.org/x/drivers/ssd1306"
)
func main() {
machine.I2C0.Configure(machine.I2CConfig{
Frequency: machine.TWI_FREQ_400KHZ,
})
display := ssd1306.NewI2C(machine.I2C0)
display.Configure(ssd1306.Config{
Address: ssd1306.Address_128_32,
Width: 128,
Height: 32,
})
display.ClearDisplay()
x := int16(0)
y := int16(0)
deltaX := int16(1)
deltaY := int16(1)
for {
pixel := display.GetPixel(x, y)
c := color.RGBA{255, 255, 255, 255}
if pixel {
c = color.RGBA{0, 0, 0, 255}
}
display.SetPixel(x, y, c)
display.Display()
x += deltaX
y += deltaY
if x == 0 || x == 127 {
deltaX = -deltaX
}
if y == 0 || y == 31 {
deltaY = -deltaY
}
time.Sleep(1 * time.Millisecond)
}
}
-60
View File
@@ -1,60 +0,0 @@
// This example shows how to use 128x64 display over I2C
// Tested on Seeeduino XIAO Expansion Board https://wiki.seeedstudio.com/Seeeduino-XIAO-Expansion-Board/
//
// According to manual, I2C address of the display is 0x78, but that's 8-bit address.
// TinyGo operates on 7-bit addresses and respective 7-bit address would be 0x3C, which we use below.
//
// To learn more about different types of I2C addresses, please see following page
// https://www.totalphase.com/support/articles/200349176-7-bit-8-bit-and-10-bit-I2C-Slave-Addressing
package main
import (
"machine"
"image/color"
"time"
"tinygo.org/x/drivers/ssd1306"
)
func main() {
machine.I2C0.Configure(machine.I2CConfig{
Frequency: machine.TWI_FREQ_400KHZ,
})
display := ssd1306.NewI2C(machine.I2C0)
display.Configure(ssd1306.Config{
Address: 0x3C,
Width: 128,
Height: 64,
})
display.ClearDisplay()
x := int16(0)
y := int16(0)
deltaX := int16(1)
deltaY := int16(1)
for {
pixel := display.GetPixel(x, y)
c := color.RGBA{255, 255, 255, 255}
if pixel {
c = color.RGBA{0, 0, 0, 255}
}
display.SetPixel(x, y, c)
display.Display()
x += deltaX
y += deltaY
if x == 0 || x == 127 {
deltaX = -deltaX
}
if y == 0 || y == 63 {
deltaY = -deltaY
}
time.Sleep(1 * time.Millisecond)
}
}
+59
View File
@@ -0,0 +1,59 @@
package main
// This example shows how to use SSD1306 OLED display driver over I2C and SPI.
//
// Check the `newSSD1306Display()` functions for I2C and SPI initializations.
import (
"runtime"
"image/color"
"time"
)
func main() {
display := newSSD1306Display()
display.ClearDisplay()
w, h := display.Size()
x := int16(0)
y := int16(0)
deltaX := int16(1)
deltaY := int16(1)
traceTime := time.Now().UnixMilli() + 1000
frames := 0
ms := runtime.MemStats{}
for {
pixel := display.GetPixel(x, y)
c := color.RGBA{255, 255, 255, 255}
if pixel {
c = color.RGBA{0, 0, 0, 255}
}
display.SetPixel(x, y, c)
display.Display()
x += deltaX
y += deltaY
if x == 0 || x == w-1 {
deltaX = -deltaX
}
if y == 0 || y == h-1 {
deltaY = -deltaY
}
frames++
now := time.Now().UnixMilli()
if now >= traceTime {
runtime.ReadMemStats(&ms)
println("TS", now, "| FPS", frames, "| HeapInuse", ms.HeapInuse)
traceTime = now + 1000
frames = 0
}
}
}
+38
View File
@@ -0,0 +1,38 @@
//go:build xiao_ble
// This initializes SSD1306 OLED display driver over I2C.
//
// Seeed XIAO BLE board + SSD1306 128x32 I2C OLED display.
//
// Wiring:
// - XIAO GND -> OLED GND
// - XIAO 3v3 -> OLED VCC
// - XIAO D4 (SDA) -> OLED SDA
// - XIAO D5 (SCL) -> OLED SCK
//
// For your case:
// - Connect the display to I2C pins on your board.
// - Adjust I2C address and display size as needed.
package main
import (
"machine"
"tinygo.org/x/drivers/ssd1306"
)
func newSSD1306Display() *ssd1306.Device {
machine.I2C0.Configure(machine.I2CConfig{
Frequency: 400 * machine.KHz,
SDA: machine.SDA0_PIN,
SCL: machine.SCL0_PIN,
})
display := ssd1306.NewI2C(machine.I2C0)
display.Configure(ssd1306.Config{
Address: ssd1306.Address_128_32, // or ssd1306.Address
Width: 128,
Height: 32, // or 64
})
return display
}
+27
View File
@@ -0,0 +1,27 @@
//go:build thumby
// This initializes SSD1306 OLED display driver over SPI.
//
// Thumby board has a tiny built-in 72x40 display.
//
// As the display is built-in, no wiring is needed.
package main
import (
"machine"
"tinygo.org/x/drivers/ssd1306"
)
func newSSD1306Display() *ssd1306.Device {
machine.SPI0.Configure(machine.SPIConfig{})
display := ssd1306.NewSPI(machine.SPI0, machine.THUMBY_DC_PIN, machine.THUMBY_RESET_PIN, machine.THUMBY_CS_PIN)
display.Configure(ssd1306.Config{
Width: 72,
Height: 40,
ResetCol: ssd1306.ResetValue{28, 99},
ResetPage: ssd1306.ResetValue{0, 5},
})
return display
}
+40
View File
@@ -0,0 +1,40 @@
//go:build xiao_rp2040
// This initializes SSD1306 OLED display driver over SPI.
//
// Seeed XIAO RP2040 board + SSD1306 128x64 SPI OLED display.
//
// Wiring:
// - XIAO GND -> OLED GND
// - XIAO 3v3 -> OLED VCC
// - XIAO D8 (SCK) -> OLED D0
// - XIAO D10 (SDO) -> OLED D1
// - XIAO D4 -> OLED RES
// - XIAO D5 -> OLED DC
// - XIAO D6 -> OLED CS
//
// For your case:
// - Connect the display to SPI pins on your board.
// - Adjust RES, DC and CS pins as needed.
// - Adjust SPI frequency as needed.
// - Adjust display size as needed.
package main
import (
"machine"
"tinygo.org/x/drivers/ssd1306"
)
func newSSD1306Display() *ssd1306.Device {
machine.SPI0.Configure(machine.SPIConfig{
Frequency: 50 * machine.MHz,
})
display := ssd1306.NewSPI(machine.SPI0, machine.D5, machine.D4, machine.D6)
display.Configure(ssd1306.Config{
Width: 128,
Height: 64,
})
return display
}
-48
View File
@@ -1,48 +0,0 @@
package main
import (
"image/color"
"machine"
"time"
"tinygo.org/x/drivers/ssd1306"
)
func main() {
machine.SPI0.Configure(machine.SPIConfig{
Frequency: 8000000,
})
display := ssd1306.NewSPI(machine.SPI0, machine.P8, machine.P7, machine.P9)
display.Configure(ssd1306.Config{
Width: 128,
Height: 64,
})
display.ClearDisplay()
x := int16(64)
y := int16(32)
deltaX := int16(1)
deltaY := int16(1)
for {
pixel := display.GetPixel(x, y)
c := color.RGBA{255, 255, 255, 255}
if pixel {
c = color.RGBA{0, 0, 0, 255}
}
display.SetPixel(x, y, c)
display.Display()
x += deltaX
y += deltaY
if x == 0 || x == 127 {
deltaX = -deltaX
}
if y == 0 || y == 63 {
deltaY = -deltaY
}
time.Sleep(1 * time.Millisecond)
}
}
-50
View File
@@ -1,50 +0,0 @@
// This example using the SSD1306 OLED display over SPI on the Thumby board
// A very tiny 72x40 display.
package main
import (
"image/color"
"machine"
"time"
"tinygo.org/x/drivers/ssd1306"
)
func main() {
machine.SPI0.Configure(machine.SPIConfig{})
display := ssd1306.NewSPI(machine.SPI0, machine.THUMBY_DC_PIN, machine.THUMBY_RESET_PIN, machine.THUMBY_CS_PIN)
display.Configure(ssd1306.Config{
Width: 72,
Height: 40,
ResetCol: ssd1306.ResetValue{28, 99},
ResetPage: ssd1306.ResetValue{0, 5},
})
display.ClearDisplay()
x := int16(36)
y := int16(20)
deltaX := int16(1)
deltaY := int16(1)
for {
pixel := display.GetPixel(x, y)
c := color.RGBA{255, 255, 255, 255}
if pixel {
c = color.RGBA{0, 0, 0, 255}
}
display.SetPixel(x, y, c)
display.Display()
x += deltaX
y += deltaY
if x == 0 || x == 71 {
deltaX = -deltaX
}
if y == 0 || y == 39 {
deltaY = -deltaY
}
time.Sleep(1 * time.Millisecond)
}
}
+35
View File
@@ -0,0 +1,35 @@
package main
import (
"machine"
"tinygo.org/x/drivers/tmc2209"
)
func main() {
uart := machine.UART0
comm := tmc2209.NewUARTComm(*uart, 0)
// Create an instance of the TMC2209 with UART communication
tmc := tmc2209.NewTMC2209(comm, 0x00) // Replace 0x00 with the appropriate address
// Set up the TMC2209 driver
err := tmc.Setup()
if err != nil {
println("Failed to set up TMC2209: ", err)
}
// Write to a register (example: setting a register value)
err = tmc.WriteRegister(0x10, 0x12345678) // Replace 0x10 with the register address and 0x12345678 with the value
if err != nil {
println("Failed to write register:", err)
}
// Read from a register (example: reading a register value)
value, err := tmc.ReadRegister(0x10)
if err != nil {
println("Failed to read register: ", err)
}
// Output the read value
println("Register value: ", value)
}
+61
View File
@@ -0,0 +1,61 @@
// Connects to SPI1 on a RP2040 (Pico)
package main
import (
"machine"
"tinygo.org/x/drivers/tmc5160"
)
func main() {
// Step 1. Setup your protocol. SPI setup shown below
spi := machine.SPI1
spi.Configure(machine.SPIConfig{
Frequency: 12000000, // Upto 12 MHZ is pretty stable. Reduce to 5 or 6 Mhz if you are experiencing issues
Mode: 3,
LSBFirst: false,
})
// Step 2. Set up all associated Pins
csPin0 := machine.GPIO13
csPin0.Configure(machine.PinConfig{Mode: machine.PinOutput})
enn0 := machine.GPIO18
enn0.Configure(machine.PinConfig{Mode: machine.PinOutput})
// csPins is a map of all chip select pins in a multi driver setup.
//Only one pin csPin0 mapped to "0"is shown in this example, but add more mappings as required
csPins := map[uint8]machine.Pin{0: csPin0}
//bind csPin to driverAdddress
driverAddress := uint8(0) // Let's assume we are working with driver at address 0x01
// Step 3. Bind the communication interface to the protocol
comm := tmc5160.NewSPIComm(spi, csPins)
// Step 4. Define your stepper like this below
//stepper := tmc5160.NewStepper(angle , gearRatio vSupply rCoil , lCoil , iPeak , rSense , mSteps, fclk )
stepper := tmc5160.NewDefaultStepper() // Default Stepper should be used only for testing.
// Step 5. Instantiate your driver
driver := tmc5160.NewDriver(
comm,
driverAddress,
enn0,
stepper)
// Setting and getting mode
rampMode := tmc5160.NewRAMPMODE(comm, driverAddress)
err := rampMode.SetMode(tmc5160.PositioningMode)
if err != nil {
return
}
mode, err := rampMode.GetMode()
if err != nil {
println("Error getting mode:", err)
} else {
println("Current Mode:", mode)
}
// Read GCONF register
GCONF := tmc5160.NewGCONF()
gconfVal, err := driver.ReadRegister(tmc5160.GCONF)
// Uppack the register to get all the bits and bytes of the register
GCONF.Unpack(gconfVal)
//E.g. MultiStepFlit is retrieved from the GCONF register
println("GCONF:MultiStepFlit:", GCONF.MultistepFilt)
}
+69
View File
@@ -0,0 +1,69 @@
// Capacitive touch sensing example.
//
// This capacitive touch sensor works by charging a normal GPIO pin, then slowly
// discharging it through a 1MΩ resistor and seeing how long it takes to go from
// high to low.
//
// Use as follows:
// - Change touchPin below as needed.
// - Connect this pin to some metal surface, like a piece of aluminimum foil.
// Make sure this surface is covered (using paper, Scotch tape, etc).
// - Also connect this same pin to ground through a 1MΩ resistor.
//
// This sensor is very sensitive to noise on the power source, so you should
// probably try to limit it by running from a battery for example. Especially
// phone chargers can produce a lot of noise.
package main
import (
"machine"
"time"
"tinygo.org/x/drivers/touch/capacitive"
)
const touchPin = machine.GP16 // Raspberry Pi Pico
func main() {
time.Sleep(time.Second * 2)
println("start")
led := machine.LED
led.Configure(machine.PinConfig{Mode: machine.PinOutput})
led.Low()
// Configure the array of GPIO pins used for capacitive touch sensing.
// We're using only one pin.
array := capacitive.NewArray([]machine.Pin{touchPin})
// Use a dynamic threshold, meaning the GPIO pin is automatically calibrated
// and re-calibrated to adjust for varying environments (e.g. changing
// humidity).
array.SetDynamicThreshold(100)
wasTouching := false
for i := uint32(0); ; i++ {
// Update the GPIO pin. This must be called very often.
array.Update()
touching := array.Touching(0)
// Indicate whether the pin is touched via the LED.
led.Set(touching)
// Print something when the touch state changed.
if wasTouching != touching {
wasTouching = touching
if touching {
println(" touch!")
} else {
println(" release!")
}
}
// Print the current value, as a debugging aid. It's not really meant to
// be used directly.
if i%128 == 32 {
println("touch value:", array.RawValue(0))
}
}
}
+40 -12
View File
@@ -3,7 +3,9 @@ package main
import (
"image/color"
"machine"
"time"
"tinygo.org/x/drivers"
"tinygo.org/x/drivers/uc8151"
)
@@ -14,30 +16,56 @@ func main() {
led = machine.LED
led.Configure(machine.PinConfig{Mode: machine.PinOutput})
machine.SPI0.Configure(machine.SPIConfig{
Frequency: 12000000,
Frequency: 12 * machine.MHz,
SCK: machine.EPD_SCK_PIN,
SDO: machine.EPD_SDO_PIN,
})
display = uc8151.New(machine.SPI0, machine.EPD_CS_PIN, machine.EPD_DC_PIN, machine.EPD_RESET_PIN, machine.EPD_BUSY_PIN)
display.Configure(uc8151.Config{
Rotation: uc8151.ROTATION_270,
Speed: uc8151.MEDIUM,
Blocking: true,
Rotation: drivers.Rotation270,
Speed: uc8151.TURBO,
FlickerFree: true,
Blocking: false,
})
black := color.RGBA{1, 1, 1, 255}
display.ClearBuffer()
display.Display()
for i := int16(0); i < 37; i++ {
for j := int16(0); j < 16; j++ {
if (i+j)%2 == 0 {
showRect(i*8, j*8, 8, 8, black)
display.ClearDisplay()
mod := int16(1)
for {
// checkerboard
for i := int16(0); i < 11; i++ {
if mod == 1 {
mod = 0
} else {
mod = 1
}
display.ClearBuffer()
for i := int16(0); i < 37; i++ {
for j := int16(0); j < 16; j++ {
if (i+j)%2 == mod {
showRect(i*8, j*8, 8, 8, black)
}
}
}
display.Display()
time.Sleep(500 * time.Millisecond)
}
// moving line
for i := int16(16); i < 21; i++ {
display.ClearBuffer()
for j := int16(0); j < 16; j++ {
if (i+j)%2 == 0 {
showRect(i*8, j*8, 8, 8, black)
}
display.Display()
time.Sleep(250 * time.Millisecond)
}
}
}
display.Display()
}
func showRect(x int16, y int16, w int16, h int16, c color.RGBA) {
+36
View File
@@ -0,0 +1,36 @@
package main
import (
"image/color"
"machine"
"tinygo.org/x/drivers/waveshare-epd/epd1in54"
"tinygo.org/x/tinyfont"
"tinygo.org/x/tinyfont/gophers"
)
var (
spi0 = machine.SPI0
cs = machine.D10
dc = machine.D9
rst = machine.D6
busy = machine.D5
black = color.RGBA{R: 1, G: 1, B: 1, A: 255}
)
func main() {
display := epd1in54.New(spi0, cs, dc, rst, busy)
display.LDirInit(epd1in54.Config{})
display.Clear()
display.ClearBuffer()
tinyfont.WriteLineRotated(&display, &gophers.Regular58pt, 150, 0, "A B C", black, tinyfont.ROTATION_90)
tinyfont.WriteLineRotated(&display, &gophers.Regular58pt, 100, 0, "D E F", black, tinyfont.ROTATION_90)
tinyfont.WriteLineRotated(&display, &gophers.Regular58pt, 50, 0, "G H I", black, tinyfont.ROTATION_90)
tinyfont.WriteLineRotated(&display, &gophers.Regular58pt, 0, 0, "J K L", black, tinyfont.ROTATION_90)
display.Display()
display.Sleep()
}
+84
View File
@@ -0,0 +1,84 @@
package main
import (
"image/color"
"machine"
"time"
"tinygo.org/x/drivers"
"tinygo.org/x/drivers/waveshare-epd/epd2in66b"
"tinygo.org/x/tinyfont"
"tinygo.org/x/tinyfont/freemono"
)
var (
black = color.RGBA{0, 0, 0, 0xff}
white = color.RGBA{0xff, 0xff, 0xff, 0xff}
red = color.RGBA{0xff, 0, 0, 0xff}
)
func main() {
machine.Serial.Configure(machine.UARTConfig{})
time.Sleep(2 * time.Second)
machine.SPI1.Configure(machine.SPIConfig{
Frequency: epd2in66b.Baudrate,
})
println("started")
// in case you have a Pico module, you can directly use
// dev, err := epd2in66b.NewPicoModule()
display := epd2in66b.New(machine.SPI1)
cfg := epd2in66b.Config{
DataPin: machine.GP8,
ChipSelectPin: machine.GP9,
ResetPin: machine.GP12,
BusyPin: machine.GP13,
}
err := display.Configure(cfg)
if err != nil {
panic(err)
}
err = display.Reset()
if err != nil {
panic(err)
}
println("draw checkerboard")
drawCheckerBoard(&display)
println("draw 'hello'")
tinyfont.WriteLineRotated(&display, &freemono.Bold24pt7b, 40, 10, "Hello!", white, tinyfont.ROTATION_90)
tinyfont.WriteLineRotated(&display, &freemono.Bold12pt7b, 10, 10, "tinygo rocks", white, tinyfont.ROTATION_90)
err = display.Display()
if err != nil {
panic(err)
}
}
func drawCheckerBoard(display drivers.Displayer) {
s := 8
width, height := display.Size()
for x := 0; x <= int(width)-s; x += s {
for y := 0; y <= int(height)-s; y += s {
c := red
if (x/s)%2 == (y/s)%2 {
c = black
}
showRect(display, x, y, s, s, c)
}
}
}
func showRect(display drivers.Displayer, x int, y int, w int, h int, c color.RGBA) {
for i := x; i < x+w; i++ {
for j := y; j < y+h; j++ {
display.SetPixel(int16(i), int16(j), c)
}
}
}
+5 -4
View File
@@ -4,7 +4,8 @@ package main
import "machine"
// Replace neo and led in the code below to match the pin
// that you are using if different.
var neo = machine.D2
var led = machine.LED
func init() {
// Replace neo in the code below to match the pin
// that you are using if different.
neo = machine.D2
}
+6 -5
View File
@@ -4,8 +4,9 @@ package main
import "machine"
// This is the pin assignment for the Digispark only.
// Replace neo and led in the code below to match the pin
// that you are using if different.
var neo machine.Pin = 0
var led = machine.LED
func init() {
// This is the pin assignment for the Digispark only.
// Replace neo and led in the code below to match the pin
// that you are using if different.
neo = machine.Pin(0)
}
+4 -4
View File
@@ -12,11 +12,12 @@ import (
"tinygo.org/x/drivers/ws2812"
)
var leds [10]color.RGBA
var (
neo machine.Pin
leds [10]color.RGBA
)
func main() {
led.Configure(machine.PinConfig{Mode: machine.PinOutput})
neo.Configure(machine.PinConfig{Mode: machine.PinOutput})
ws := ws2812.NewWS2812(neo)
@@ -35,7 +36,6 @@ func main() {
}
ws.WriteColors(leds[:])
led.Set(rg)
time.Sleep(100 * time.Millisecond)
}
}
+6 -5
View File
@@ -1,10 +1,11 @@
//go:build !digispark && !arduino && !qtpy && !m5stamp_c3 && !thingplus_rp2040
//go:build !digispark && !arduino
package main
import "machine"
// Replace neo and led in the code below to match the pin
// that you are using if different.
var neo machine.Pin = machine.WS2812
var led = machine.LED
func init() {
// Replace neo in the code below to match the pin
// that you are using if different.
neo = machine.WS2812
}
-10
View File
@@ -1,10 +0,0 @@
//go:build qtpy || m5stamp_c3
package main
import "machine"
// Replace neo and led in the code below to match the pin
// that you are using if different.
var neo machine.Pin = machine.WS2812
var led = machine.NoPin
-12
View File
@@ -1,12 +0,0 @@
//go:build thingplus_rp2040
package main
import "machine"
// This is the pin assignment for the internal neopixel of the
// Sparkfun thingplus rp2040.
// Replace neo and led in the code below to match the pin
// that you are using if different.
var neo machine.Pin = machine.GPIO8
var led = machine.LED
+41
View File
@@ -46,6 +46,10 @@ var DefaultDeviceIdentifier = DeviceIdentifierFunc(func(id JedecID) Attrs {
return GD25Q16C()
case 0xC84017:
return GD25Q64C()
case 0x856015:
return P25Q16H()
case 0xEF4014:
return W25Q80DV()
case 0xEF4015:
return W25Q16JVIQ()
case 0xEF4016:
@@ -239,6 +243,24 @@ func GD25Q64C() Attrs {
}
}
// Settings for the Puya P25Q16H 2MiB SPI flash.
// Datasheet: https://files.seeedstudio.com/wiki/github_weiruanexample/Flash_P25Q16H-UXH-IR_Datasheet.pdf
func P25Q16H() Attrs {
return Attrs{
TotalSize: 1 << 21, // 2 MiB
StartUp: 5000 * time.Microsecond,
JedecID: JedecID{0x85, 0x60, 0x15},
MaxClockSpeedMHz: 55,
QuadEnableBitMask: 0x02,
HasSectorProtection: true,
SupportsFastRead: true,
SupportsQSPI: true,
SupportsQSPIWrites: true,
WriteStatusSplit: true,
SingleStatusByte: false,
}
}
// Settings for the Winbond W25Q16JV-IQ 2MiB SPI flash. Note that JV-IM has a
// different .memory_type (0x70) Datasheet:
// https://www.winbond.com/resource-files/w25q16jv%20spi%20revf%2005092017.pdf
@@ -380,6 +402,25 @@ func W25Q80DL() Attrs {
TotalSize: 1 << 20, // 1 MiB
StartUp: 5000 * time.Microsecond,
JedecID: JedecID{0xEF, 0x60, 0x14},
MaxClockSpeedMHz: 80,
QuadEnableBitMask: 0x02,
HasSectorProtection: false,
SupportsFastRead: true,
SupportsQSPI: true,
SupportsQSPIWrites: false,
WriteStatusSplit: false,
SingleStatusByte: false,
}
}
// Settings for the Winbond W25Q80DV 2MiB SPI flash.
// Datasheet:
// https://www.winbond.com/resource-files/w25q80dv%20dl_revh_10022015.pdf
func W25Q80DV() Attrs {
return Attrs{
TotalSize: 1 << 21, // 2 MiB
StartUp: 5000 * time.Microsecond,
JedecID: JedecID{0xEF, 0x40, 0x14},
MaxClockSpeedMHz: 104,
QuadEnableBitMask: 0x02,
HasSectorProtection: false,
+14 -10
View File
@@ -5,8 +5,6 @@
package ft6336
import (
"machine"
"tinygo.org/x/drivers"
"tinygo.org/x/drivers/internal/legacy"
"tinygo.org/x/drivers/touch"
@@ -14,19 +12,21 @@ import (
// Device wraps FT6336 I2C Self-Capacitive touch
type Device struct {
bus drivers.I2C
buf []byte
Address uint8
intPin machine.Pin
bus drivers.I2C
buf []byte
Address uint8
configurePins func()
}
// New returns FT6336 device for the provided I2C bus using default address.
func New(i2c drivers.I2C, intPin machine.Pin) *Device {
func New(i2c drivers.I2C, intPin drivers.PinInput) *Device {
return &Device{
bus: i2c,
buf: make([]byte, 11),
Address: Address,
intPin: intPin,
configurePins: func() {
legacy.ConfigurePinInputPulldown(intPin)
},
}
}
@@ -36,8 +36,11 @@ type Config struct {
// Configure the FT6336 device.
func (d *Device) Configure(config Config) error {
if d.configurePins == nil {
return legacy.ErrConfigBeforeInstantiated
}
d.write1Byte(0xA4, 0x00)
d.intPin.Configure(machine.PinConfig{Mode: machine.PinInputPulldown})
d.configurePins()
return nil
}
@@ -80,7 +83,8 @@ func (d *Device) Read() []byte {
func (d *Device) ReadTouchPoint() touch.Point {
d.Read()
z := 0xFFFFF
if d.buf[0] == 0 {
switch d.buf[0] {
case 0, 255:
z = 0
}
+15 -15
View File
@@ -5,12 +5,12 @@ package gc9a01 // import "tinygo.org/x/drivers/gc9a01"
import (
"image/color"
"machine"
"time"
"errors"
"tinygo.org/x/drivers"
"tinygo.org/x/drivers/internal/legacy"
)
// Rotation controls the rotation used by the display.
@@ -22,10 +22,10 @@ type FrameRate uint8
// Device wraps an SPI connection.
type Device struct {
bus drivers.SPI
dcPin machine.Pin
resetPin machine.Pin
csPin machine.Pin
blPin machine.Pin
dcPin drivers.PinOutputFunc
resetPin drivers.PinOutputFunc
csPin drivers.PinOutputFunc
blPin drivers.PinOutputFunc
width int16
height int16
columnOffsetCfg int16
@@ -52,17 +52,17 @@ type Config struct {
}
// New creates a new ST7789 connection. The SPI wire must already be configured.
func New(bus drivers.SPI, resetPin, dcPin, csPin, blPin machine.Pin) Device {
resetPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
dcPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
csPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
blPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
func New(bus drivers.SPI, resetPin, dcPin, csPin, blPin drivers.PinOutput) Device {
legacy.ConfigurePinOut(resetPin)
legacy.ConfigurePinOut(dcPin)
legacy.ConfigurePinOut(csPin)
legacy.ConfigurePinOut(blPin)
return Device{
bus: bus,
resetPin: resetPin,
dcPin: dcPin,
csPin: csPin,
blPin: blPin,
resetPin: resetPin.Set,
dcPin: dcPin.Set,
csPin: csPin.Set,
blPin: blPin.Set,
}
}
@@ -226,7 +226,7 @@ func (d *Device) Data(data uint8) {
// Tx sends data to the display
func (d *Device) Tx(data []byte, isCommand bool) {
d.dcPin.Set(!isCommand)
d.dcPin(!isCommand)
d.bus.Tx(data, nil)
}
+9 -4
View File
@@ -1,20 +1,25 @@
module tinygo.org/x/drivers
go 1.18
go 1.22.1
toolchain go1.23.1
require (
github.com/eclipse/paho.mqtt.golang v1.2.0
github.com/frankban/quicktest v1.10.2
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
github.com/orsinium-labs/tinymath v1.1.0
github.com/soypat/natiu-mqtt v0.5.1
golang.org/x/net v0.7.0
golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d
golang.org/x/net v0.33.0
tinygo.org/x/tinyfont v0.3.0
tinygo.org/x/tinyterm v0.1.0
)
require (
github.com/google/go-cmp v0.5.2 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/kr/pretty v0.2.1 // indirect
github.com/kr/text v0.1.0 // indirect
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 // indirect
)
+8 -4
View File
@@ -3,8 +3,9 @@ github.com/eclipse/paho.mqtt.golang v1.2.0 h1:1F8mhG9+aO5/xpdtFkW4SxOJB67ukuDC3t
github.com/eclipse/paho.mqtt.golang v1.2.0/go.mod h1:H9keYFcgq3Qr5OUJm/JZI/i6U7joQ8SYLhZwfeOo6Ts=
github.com/frankban/quicktest v1.10.2 h1:19ARM85nVi4xH7xPXuc5eM/udya5ieh7b/Sv+d844Tk=
github.com/frankban/quicktest v1.10.2/go.mod h1:K+q6oSqb0W0Ininfk863uOk1lMy69l/P6txr3mVT54s=
github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
@@ -12,12 +13,15 @@ github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfn
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/orsinium-labs/tinymath v1.1.0 h1:KomdsyLHB7vE3f1nRAJF2dyf1m/gnM2HxfTeV1vS5UA=
github.com/orsinium-labs/tinymath v1.1.0/go.mod h1:WPXX6ei3KSXG7JfA03a+ekCYaY9SWN4I+JRl2p6ck+A=
github.com/soypat/natiu-mqtt v0.5.1 h1:rwaDmlvjzD2+3MCOjMZc4QEkDkNwDzbct2TJbpz+TPc=
github.com/soypat/natiu-mqtt v0.5.1/go.mod h1:xEta+cwop9izVCW7xOx2W+ct9PRMqr0gNVkvBPnQTc4=
github.com/valyala/fastjson v1.6.3/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY=
golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d h1:0olWaB5pg3+oychR51GUVCEsGkeCU/2JxjBgIo4f3M0=
golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c=
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
tinygo.org/x/drivers v0.14.0/go.mod h1:uT2svMq3EpBZpKkGO+NQHjxjGf1f42ra4OnMMwQL2aI=
tinygo.org/x/drivers v0.15.1/go.mod h1:uT2svMq3EpBZpKkGO+NQHjxjGf1f42ra4OnMMwQL2aI=
+7 -1
View File
@@ -69,10 +69,16 @@ func NewUART(uart drivers.UART) Device {
}
// NewI2C creates a new I2C GPS connection.
// Uses the default i2c address (0x42) for backward compatibility reasons.
func NewI2C(bus drivers.I2C) Device {
return NewI2CWithAddress(bus, I2C_ADDRESS)
}
// NewI2CWithAddress creates a new I2C GPS connection on the provided address
func NewI2CWithAddress(bus drivers.I2C, i2cAddress uint16) Device {
return Device{
bus: bus,
address: I2C_ADDRESS,
address: i2cAddress,
buffer: make([]byte, bufferSize),
bufIdx: bufferSize,
sentence: strings.Builder{},
+4 -1
View File
@@ -96,7 +96,10 @@ func (parser *Parser) Parse(sentence string) (Fix, error) {
fix.Speed = findSpeed(fields[7])
fix.Heading = findHeading(fields[8])
date := findDate(fields[9])
fix.Time = fix.Time.AddDate(date.Year(), int(date.Month()), date.Day())
fix.Time = date.Add(time.Duration(fix.Time.Hour())*time.Hour +
time.Duration(fix.Time.Minute())*time.Minute +
time.Duration(fix.Time.Second())*time.Second +
time.Duration(fix.Time.Nanosecond())*time.Nanosecond)
return fix, nil
}
+3 -3
View File
@@ -70,15 +70,15 @@ func TestParseRMC(t *testing.T) {
t.Error("should have errInvalidRMCSentence error")
}
val = "$GPRMC,203522.00,A,5109.0262308,N,11401.8407342,W,0.004,133.4,130522,0.0,E,D*2B"
val = "$GPRMC,203522.00,A,5109.0262308,N,11401.8407342,W,0.004,133.4,010622,0.0,E,D*2B"
fix, err := p.Parse(val)
if err != nil {
t.Error("should have parsed")
}
c.Assert(fix.Time.Year(), qt.Equals, 2022)
c.Assert(fix.Time.Month(), qt.Equals, time.May)
c.Assert(fix.Time.Day(), qt.Equals, 13)
c.Assert(fix.Time.Month(), qt.Equals, time.June)
c.Assert(fix.Time.Day(), qt.Equals, 1)
c.Assert(fix.Time.Hour(), qt.Equals, 20)
c.Assert(fix.Time.Minute(), qt.Equals, 35)
c.Assert(fix.Time.Second(), qt.Equals, 22)
+5 -1
View File
@@ -4,7 +4,11 @@ package gps
// The I2C address which this device listens to.
const (
I2C_ADDRESS = 0x42
// To ensure backward compatibility
I2C_ADDRESS = UBLOX_I2C_ADDRESS
UBLOX_I2C_ADDRESS = 0x42
PA1010D_I2C_ADDRESS = 0x10
)
const (
+19 -10
View File
@@ -5,30 +5,39 @@
package hcsr04
import (
"machine"
"time"
"tinygo.org/x/drivers"
"tinygo.org/x/drivers/internal/legacy"
)
const TIMEOUT = 23324 // max sensing distance (4m)
// Device holds the pins
type Device struct {
trigger machine.Pin
echo machine.Pin
trigger drivers.PinOutputFunc
echo drivers.PinInputFunc
configurePins func()
}
// New returns a new ultrasonic driver given 2 pins
func New(trigger, echo machine.Pin) Device {
func New(trigger drivers.PinOutput, echo drivers.PinInput) Device {
return Device{
trigger: trigger,
echo: echo,
trigger: trigger.Set,
echo: echo.Get,
configurePins: func() {
legacy.ConfigurePinOut(trigger)
legacy.ConfigurePinInput(echo)
},
}
}
// Configure configures the pins of the Device
func (d *Device) Configure() {
d.trigger.Configure(machine.PinConfig{Mode: machine.PinOutput})
d.echo.Configure(machine.PinConfig{Mode: machine.PinInput})
if d.configurePins == nil {
panic(legacy.ErrConfigBeforeInstantiated)
}
d.configurePins()
}
// ReadDistance returns the distance of the object in mm
@@ -52,7 +61,7 @@ func (d *Device) ReadPulse() int32 {
d.trigger.Low()
i := uint8(0)
for {
if d.echo.Get() {
if d.echo() {
t = time.Now()
break
}
@@ -66,7 +75,7 @@ func (d *Device) ReadPulse() int32 {
}
i = 0
for {
if !d.echo.Get() {
if !d.echo() {
return int32(time.Since(t).Microseconds())
}
i++
+5 -5
View File
@@ -210,7 +210,7 @@ func (d *Device) SendCommand(command byte) {
d.bus.SetCommandMode(true)
d.bus.Write([]byte{command})
for d.busy(command == DISPLAY_CLEAR || command == CURSOR_HOME) {
for d.isBusy(command == DISPLAY_CLEAR || command == CURSOR_HOME) {
}
}
@@ -219,7 +219,7 @@ func (d *Device) sendData(data byte) {
d.bus.SetCommandMode(false)
d.bus.Write([]byte{data})
for d.busy(false) {
for d.isBusy(false) {
}
}
@@ -231,9 +231,9 @@ func (d *Device) CreateCharacter(cgramAddr uint8, data []byte) {
}
}
// busy returns true when hd447890 is busy
// isBusy returns true when hd447890 is isBusy
// or after the timeout specified
func (d *Device) busy(longDelay bool) bool {
func (d *Device) isBusy(longDelay bool) bool {
if d.bus.WriteOnly() {
// Can't read busy flag if write only, so sleep a bit then return
if longDelay {
@@ -261,7 +261,7 @@ func (d *Device) busy(longDelay bool) bool {
// Busy returns true when hd447890 is busy
func (d *Device) Busy() bool {
return d.busy(false)
return d.isBusy(false)
}
// Size returns the current size of the display.
+1 -1
View File
@@ -167,7 +167,7 @@ func (d *Device) fillMatrixBuffer(x int16, y int16, r uint8, g uint8, b uint8) {
if r > colorTresh {
d.buffer[c][offsetR] |= 1 << bitSelect
} else {
d.buffer[c][offsetR] = d.buffer[c][offsetR] &^ 1 << bitSelect
d.buffer[c][offsetR] &^= 1 << bitSelect
}
if g > colorTresh {
d.buffer[(c+d.colorThirdStep)%d.colorDepth][offsetG] |= 1 << bitSelect
+2 -2
View File
@@ -8,10 +8,10 @@ import (
)
type spiDriver struct {
bus machine.SPI
bus *machine.SPI
}
func NewSPI(bus machine.SPI, dc, cs, rst machine.Pin) *Device {
func NewSPI(bus *machine.SPI, dc, cs, rst machine.Pin) *Device {
return &Device{
dc: dc,
cs: cs,
+2 -2
View File
@@ -8,10 +8,10 @@ import (
)
type spiDriver struct {
bus machine.SPI
bus *machine.SPI
}
func NewSPI(bus machine.SPI, dc, cs, rst machine.Pin) *Device {
func NewSPI(bus *machine.SPI, dc, cs, rst machine.Pin) *Device {
return &Device{
dc: dc,
cs: cs,
+183
View File
@@ -0,0 +1,183 @@
package ina219
type Config struct {
// BusVoltageRange sets the bus voltage range.
BusVoltageRange BusVoltageRange
// PGA sets the programmable gain amplifier.
PGA PGA
// BusADC sets the bus ADC resolution.
BusADC BusADC
// ShuntADC sets the shunt ADC resolution.
ShuntADC ShuntADC
// Mode sets the operating mode.
Mode Mode
// Calibration sets the calibration value for the expected
// voltage and current values.
Calibration Calibration
// 1000 / uA per bit
CurrentDivider float32
// 1mW per bit
PowerMultiplier float32
}
// RegisterValue returns the register value of the configuration.
func (c *Config) RegisterValue() uint16 {
return c.BusVoltageRange.RegisterValue() |
c.PGA.RegisterValue() |
c.BusADC.RegisterValue() |
c.ShuntADC.RegisterValue() |
c.Mode.RegisterValue()
}
// Generate a new configuration from a register value.
func NewConfig(config int16, calibration int16) Config {
return Config{
BusVoltageRange: BusVoltageRange(config >> 13 & 0x1),
PGA: PGA(config >> 11 & 0x3),
BusADC: BusADC(config >> 7 & 0xF),
ShuntADC: ShuntADC(config >> 3 & 0xF),
Mode: Mode(config & 0x7),
Calibration: Calibration(calibration),
}
}
// Configurations from
// https://github.com/adafruit/Adafruit_INA219/blob/master/Adafruit_INA219.cpp
var (
// Config32V2A is a configuration for a 32V 2A range.
Config32V2A = Config{
BusVoltageRange: Range32V,
PGA: PGA8,
BusADC: ADC12,
ShuntADC: SADC12,
Mode: ModeContShuntBus,
Calibration: Calibration16V400mA,
CurrentDivider: 10.0,
PowerMultiplier: 2.0,
}
// Config32V1A is a configuration for a 32V 1A range.
Config32V1A = Config{
BusVoltageRange: Range32V,
PGA: PGA8,
BusADC: ADC12,
ShuntADC: SADC12,
Mode: ModeContShuntBus,
Calibration: Calibration32V1A,
CurrentDivider: 25.0,
PowerMultiplier: 0.8,
}
// Config16V400mA is a configuration for a 16V 400mA range.
Config16V400mA = Config{
BusVoltageRange: Range16V,
PGA: PGA1,
BusADC: ADC12,
ShuntADC: SADC12,
Mode: ModeContShuntBus,
Calibration: Calibration16V400mA,
CurrentDivider: 20.0,
PowerMultiplier: 1.0,
}
)
// BusVoltageRange is the bus voltage range.
type BusVoltageRange int8
const (
Range16V BusVoltageRange = 0 // 0-16V
Range32V BusVoltageRange = 1 // 0-32V
)
func (r BusVoltageRange) RegisterValue() uint16 {
return uint16(r) << 13
}
// PGA is the programmable gain amplifier.
type PGA int8
const (
PGA1 PGA = 0 // 40mV
PGA2 PGA = 1 // 80mV
PGA4 PGA = 2 // 160mV
PGA8 PGA = 3 // 320mV
)
func (p PGA) RegisterValue() uint16 {
return uint16(p) << 11
}
// BusADC is the bus ADC resolution.
type BusADC int8
const (
ADC9 BusADC = 0 // 9-bit
ADC10 BusADC = 1 // 10-bit
ADC11 BusADC = 2 // 11-bit
ADC12 BusADC = 3 // 12-bit
)
func (b BusADC) RegisterValue() uint16 {
return uint16(b) << 7
}
// ShuntADC is the shunt ADC resolution.
type ShuntADC int8
const (
SADC9 ShuntADC = 0 // 9-bit
SADC10 ShuntADC = 1 // 10-bit
SADC11 ShuntADC = 2 // 11-bit
SADC12 ShuntADC = 3 // 12-bit
)
func (s ShuntADC) RegisterValue() uint16 {
return uint16(s) << 3
}
// Mode is the operating mode.
type Mode int8
const (
ModePowerDown Mode = 0 // power-down
ModeTrigShunt Mode = 1 // triggered shunt voltage
ModeTrigBus Mode = 2 // triggered bus voltage
ModeTrigShuntBus Mode = 3 // triggered shunt and bus voltage
ModeADCOff Mode = 4 // ADC off
ModeContShunt Mode = 5 // continuous shunt voltage
ModeContBus Mode = 6 // continuous bus voltage
ModeContShuntBus Mode = 7 // continuous shunt and bus voltage
)
// ModeTriggered is a mask for triggered modes.
const ModeTriggeredMask Mode = 0x4
// ModeTriggered returns true if the mode is a triggered mode.
func ModeTriggered(m Mode) bool {
return m != ModePowerDown && m&ModeTriggeredMask == 0
}
func (m Mode) RegisterValue() uint16 {
return uint16(m)
}
// Calibration is the calibration register for the INA219. Values from:
// https://github.com/adafruit/Adafruit_INA219/blob/master/Adafruit_INA219.cpp
type Calibration uint16
const (
Calibration32V2A Calibration = 4096
Calibration32V1A Calibration = 10240
Calibration16V400mA Calibration = 8192
)
func (c Calibration) RegisterValue() uint16 {
return uint16(c)
}
+13
View File
@@ -0,0 +1,13 @@
package ina219
type ErrOverflow struct{}
func (e ErrOverflow) Error() string { return "overflow" }
type ErrNotReady struct{}
func (e ErrNotReady) Error() string { return "not ready" }
type ErrConfigMismatch struct{}
func (e ErrConfigMismatch) Error() string { return "config mismatch" }
+202
View File
@@ -0,0 +1,202 @@
package ina219
import (
"tinygo.org/x/drivers"
"tinygo.org/x/drivers/internal/legacy"
)
// An INA219 device.
type Device struct {
bus drivers.I2C
Address uint16
config Config
}
// Create a new INA219 device with the default configuration
// and the given I2C bus at the default address.
//
// Set Address after New to change the address.
//
// Call Configure after New to write the configuration to the
// device. If you don't call Configure, the device may have a
// different configuration and the power divider and current
// multiplier are probably wrong.
func New(bus drivers.I2C) Device {
return Device{
bus: bus,
Address: Address,
config: Config32V2A,
}
}
// Set the configuration for the device. This only changes the
// configuration in memory, not on the device. Call Configure
// to write the configuration to the device.
func (d *Device) SetConfig(config Config) {
d.config = config
}
// Write the current configuration to the device.
func (d *Device) Configure() (err error) {
if err = d.WriteRegister(
RegConfig,
d.config.RegisterValue(),
); err != nil {
return
}
if err = d.WriteRegister(
RegCalibration,
d.config.Calibration.RegisterValue(),
); err != nil {
return
}
var readConfig Config
// make sure the configuration is read back correctly
if readConfig, err = d.ReadConfig(); err != nil {
return
} else if readConfig.RegisterValue() != d.config.RegisterValue() {
err = ErrConfigMismatch{}
} else if readConfig.Calibration.RegisterValue() != d.config.Calibration.RegisterValue() {
err = ErrConfigMismatch{}
}
return
}
// Trigger a conversion. This is only necessary if the device is in
// trigger mode. In continuous mode (the default), the device will
// automatically trigger conversions and this has no effect. See
// config.go.
//
// Triggering a conversion or reading the "power" register resets
// the conversion ready bit.
func (d *Device) Trigger() (err error) {
// Only trigger if the mode is one of the triggered modes.
if ModeTriggered(d.config.Mode) {
err = d.WriteRegister(RegConfig, d.config.RegisterValue())
}
return
}
// Measurements reads the bus voltage, shunt voltage, current, and power
// from the device.
func (d *Device) Measurements() (
busVoltage int16,
shuntVoltage int16,
current float32,
power float32,
err error,
) {
// Attempt to read bus voltage first, so we can check for overflow
// or conversion not ready.
if busVoltage, err = d.BusVoltage(); err != nil {
return
}
// Read the rest of the values, reading Power last, which resets
// the conversion ready bit (relevant for triggered modes).
if shuntVoltage, err = d.ShuntVoltage(); err != nil {
return
}
if current, err = d.Current(); err != nil {
return
}
if power, err = d.Power(); err != nil {
return
}
return
}
// BusVoltage reads the "bus" voltage in millivolts.
//
// It returns an error if the value is invalid due to overflow
// or if the conversion is not ready yet. In a continuous mode
// there should always be a measurement available after the
// device is ready. See above notes on Trigger.
func (d *Device) BusVoltage() (voltage int16, err error) {
val, err := d.ReadRegister(RegBusVoltage)
if err != nil {
return
}
// The overflow bit is set, so the values are invalid.
if val&(1<<0) != 0 {
err = ErrOverflow{}
return
}
// The conversion is not ready yet.
if ModeTriggered(d.config.Mode) && val&(1<<1) != 0 {
err = ErrNotReady{}
return
}
voltage = (int16(val) >> 3) * 4
return
}
// ShuntVoltage reads the "shunt" voltage in 100ths of a millivolt.
func (d *Device) ShuntVoltage() (voltage int16, err error) {
return d.ReadRegister(RegShuntVoltage)
}
// Current reads the current in milliamps.
func (d *Device) Current() (current float32, err error) {
val, err := d.ReadRegister(RegCurrent)
if err != nil {
return
}
current = float32(val) / d.config.CurrentDivider
return
}
// Power reads the power in milliwatts.
func (d *Device) Power() (power float32, err error) {
val, err := d.ReadRegister(RegPower)
if err != nil {
return
}
power = float32(val) * d.config.PowerMultiplier
return
}
// Read the configuration from the device.
func (d *Device) ReadConfig() (config Config, err error) {
var cfg, cal int16
if cfg, err = d.ReadRegister(RegConfig); err != nil {
return
}
if cal, err = d.ReadRegister(RegCalibration); err != nil {
return
}
config = NewConfig(cfg, cal)
return
}
// Read a register from the device.
func (d *Device) ReadRegister(reg uint8) (val int16, err error) {
buf := make([]byte, 2)
err = legacy.ReadRegister(d.bus, uint8(d.Address), reg, buf)
if err != nil {
return
}
val = int16(buf[0])<<8 | int16(buf[1]&0xff)
return
}
// Write to a register on the device.
func (d *Device) WriteRegister(reg uint8, val uint16) error {
buf := []byte{byte(val >> 8), byte(val & 0xff)}
return legacy.WriteRegister(d.bus, uint8(d.Address), reg, buf)
}
+232
View File
@@ -0,0 +1,232 @@
package ina219
import (
"fmt"
"testing"
qt "github.com/frankban/quicktest"
"tinygo.org/x/drivers/tester"
)
func TestDefaultAddress(t *testing.T) {
c := qt.New(t)
bus := tester.NewI2CBus(c)
dev := New(bus)
c.Assert(dev.Address, qt.Equals, uint16(Address))
}
func TestBusVoltage(t *testing.T) {
t.Run("valid", func(t *testing.T) {
c := qt.New(t)
bus := tester.NewI2CBus(c)
fake := tester.NewI2CDevice16(c, Address)
fake.Registers = map[uint8]uint16{
RegBusVoltage: (4200 << 3) / 4, // 4.2V
}
bus.AddDevice(fake)
dev := New(bus)
voltage, err := dev.BusVoltage()
c.Assert(err, qt.IsNil)
c.Assert(voltage, qt.Equals, int16(4200))
})
t.Run("overflow", func(t *testing.T) {
c := qt.New(t)
bus := tester.NewI2CBus(c)
fake := tester.NewI2CDevice16(c, Address)
fake.Registers = map[uint8]uint16{
RegBusVoltage: (1 >> 0), // overflow
}
bus.AddDevice(fake)
dev := New(bus)
_, err := dev.BusVoltage()
c.Assert(err, qt.Not(qt.IsNil))
c.Assert(err, qt.ErrorMatches, ErrOverflow{}.Error())
})
t.Run("not ready", func(t *testing.T) {
c := qt.New(t)
bus := tester.NewI2CBus(c)
fake := tester.NewI2CDevice16(c, Address)
fake.Registers = map[uint8]uint16{
RegBusVoltage: ((4200 << 3) / 4) | (1 << 1), // not ready
}
bus.AddDevice(fake)
dev := New(bus)
dev.config.Mode = ModeTrigBus
_, err := dev.BusVoltage()
c.Assert(err, qt.Not(qt.IsNil))
c.Assert(err, qt.ErrorMatches, ErrNotReady{}.Error())
})
}
func TestShuntVoltage(t *testing.T) {
c := qt.New(t)
bus := tester.NewI2CBus(c)
fake := tester.NewI2CDevice16(c, Address)
fake.Registers = map[uint8]uint16{
RegShuntVoltage: 0x1234,
}
bus.AddDevice(fake)
dev := New(bus)
voltage, err := dev.ShuntVoltage()
c.Assert(err, qt.IsNil)
c.Assert(voltage, qt.Equals, int16(0x1234))
}
func TestCurrent(t *testing.T) {
c := qt.New(t)
bus := tester.NewI2CBus(c)
fake := tester.NewI2CDevice16(c, Address)
fake.Registers = map[uint8]uint16{
RegCurrent: 420 * 6.9, // 420mA
}
bus.AddDevice(fake)
dev := New(bus)
dev.config.CurrentDivider = 6.9
current, err := dev.Current()
c.Assert(err, qt.IsNil)
c.Assert(current, qt.Equals, float32(420))
}
func TestPower(t *testing.T) {
c := qt.New(t)
bus := tester.NewI2CBus(c)
fake := tester.NewI2CDevice16(c, Address)
fake.Registers = map[uint8]uint16{
RegPower: 420 / 0.8, // 420mW
}
bus.AddDevice(fake)
dev := New(bus)
dev.config.PowerMultiplier = 0.8
power, err := dev.Power()
c.Assert(err, qt.IsNil)
c.Assert(power, qt.Equals, float32(420))
}
func TestReadConfig(t *testing.T) {
// use the default configurations
for _, tc := range []Config{
Config16V400mA,
Config32V2A,
Config32V1A,
} {
n := fmt.Sprintf("%x/%x", tc.RegisterValue(), tc.Calibration.RegisterValue())
t.Run(n, func(t *testing.T) {
c := qt.New(t)
bus := tester.NewI2CBus(c)
fake := tester.NewI2CDevice16(c, Address)
fake.Registers = map[uint8]uint16{
RegConfig: tc.RegisterValue(),
RegCalibration: tc.Calibration.RegisterValue(),
}
bus.AddDevice(fake)
dev := New(bus)
config, err := dev.ReadConfig()
c.Assert(err, qt.IsNil)
c.Assert(config.BusADC, qt.Equals, tc.BusADC)
c.Assert(config.BusVoltageRange, qt.Equals, tc.BusVoltageRange)
c.Assert(config.Calibration, qt.Equals, tc.Calibration)
c.Assert(config.Mode, qt.Equals, tc.Mode)
c.Assert(config.PGA, qt.Equals, tc.PGA)
c.Assert(config.ShuntADC, qt.Equals, tc.ShuntADC)
})
}
}
func TestWriteConfig(t *testing.T) {
// use the default configurations
for _, tc := range []Config{
Config16V400mA,
Config32V2A,
Config32V1A,
} {
n := fmt.Sprintf("%x/%x", tc.RegisterValue(), tc.Calibration.RegisterValue())
t.Run(n, func(t *testing.T) {
c := qt.New(t)
bus := tester.NewI2CBus(c)
fake := tester.NewI2CDevice16(c, Address)
bus.AddDevice(fake)
fake.Registers = map[uint8]uint16{
RegConfig: 0,
RegCalibration: 0,
}
dev := New(bus)
dev.config = tc
err := dev.Configure()
c.Assert(err, qt.IsNil)
c.Assert(fake.Registers[RegConfig], qt.Equals, tc.RegisterValue())
c.Assert(fake.Registers[RegCalibration], qt.Equals, tc.Calibration.RegisterValue())
})
}
}
func TestSetConfig(t *testing.T) {
for _, tc := range []Config{
Config16V400mA,
Config32V2A,
Config32V1A,
} {
n := fmt.Sprintf("%x/%x", tc.RegisterValue(), tc.Calibration.RegisterValue())
t.Run(n, func(t *testing.T) {
c := qt.New(t)
bus := tester.NewI2CBus(c)
dev := New(bus)
dev.SetConfig(tc)
c.Assert(dev.config, qt.Equals, tc)
})
}
}
func TestTrigger(t *testing.T) {
c := qt.New(t)
bus := tester.NewI2CBus(c)
fake := tester.NewI2CDevice16(c, Address)
bus.AddDevice(fake)
fake.Registers = map[uint8]uint16{
RegConfig: Config32V2A.RegisterValue(),
}
dev := New(bus)
dev.config = Config32V2A
dev.config.Mode = ModeTrigBus
err := dev.Trigger()
c.Assert(err, qt.IsNil)
c.Assert(fake.Registers[RegConfig], qt.Equals, dev.config.RegisterValue())
}
func TestMeasurements(t *testing.T) {
bvVal := int16(4200)
svVal := int16(1234)
iVal := float32(420)
pVal := float32(420)
c := qt.New(t)
bus := tester.NewI2CBus(c)
fake := tester.NewI2CDevice16(c, Address)
bus.AddDevice(fake)
fake.Registers = map[uint8]uint16{
RegBusVoltage: uint16(((4200 << 3) / 4) | (1 << 1)),
RegShuntVoltage: uint16(svVal),
RegCurrent: uint16(iVal * Config16V400mA.CurrentDivider),
RegPower: uint16(pVal / Config16V400mA.PowerMultiplier),
}
dev := New(bus)
dev.config = Config16V400mA
bv, sv, i, p, err := dev.Measurements()
c.Assert(err, qt.IsNil)
c.Assert(bv, qt.Equals, bvVal)
c.Assert(sv, qt.Equals, svVal)
c.Assert(i, qt.Equals, iVal)
c.Assert(p, qt.Equals, pVal)
}
+13
View File
@@ -0,0 +1,13 @@
package ina219
// The default I2C address for this device.
const Address = 0x40
const (
RegConfig uint8 = 0x0
RegShuntVoltage uint8 = 0x1
RegBusVoltage uint8 = 0x2
RegPower uint8 = 0x3
RegCurrent uint8 = 0x4
RegCalibration uint8 = 0x5
)
+54
View File
@@ -0,0 +1,54 @@
package legacy
import (
"errors"
"tinygo.org/x/drivers"
)
// ConfigurePinOut is a legacy function used to configure pins as outputs.
//
// Deprecated: Do not configure pins in drivers.
// This is a legacy feature and should only be used by drivers that
// previously configured pins in initialization to avoid breaking users.
func ConfigurePinOut(po drivers.PinOutput) {
configurePinOut(po)
}
// ConfigurePinInput is a legacy function used to configure pins as inputs.
//
// Deprecated: Do not configure pins in drivers.
// This is a legacy feature and should only be used by drivers that
// previously configured pins in initialization to avoid breaking users.
func ConfigurePinInputPulldown(pi drivers.PinInput) {
configurePinInputPulldown(pi)
}
// ConfigurePinInput is a legacy function used to configure pins as inputs.
//
// Deprecated: Do not configure pins in drivers.
// This is a legacy feature and should only be used by drivers that
// previously configured pins in initialization to avoid breaking users.
func ConfigurePinInput(pi drivers.PinInput) {
configurePinInput(pi)
}
// ConfigurePinInput is a legacy function used to configure pins as inputs.
//
// Deprecated: Do not configure pins in drivers.
// This is a legacy feature and should only be used by drivers that
// previously configured pins in initialization to avoid breaking users.
func ConfigurePinInputPullup(pi drivers.PinInput) {
configurePinInputPullup(pi)
}
// PinIsNoPin returns true if the argument is a machine.Pin type and is the machine.NoPin predeclared type.
//
// Deprecated: Drivers do not require pin knowledge from now on.
func PinIsNoPin(pin any) bool {
return pinIsNoPin(pin)
}
var (
ErrConfigBeforeInstantiated = errors.New("device must be instantiated with New before calling Configure method")
)
+13
View File
@@ -0,0 +1,13 @@
//go:build !baremetal
package legacy
import (
"tinygo.org/x/drivers"
)
func configurePinOut(p drivers.PinOutput) {}
func configurePinInput(p drivers.PinInput) {}
func configurePinInputPulldown(p drivers.PinInput) {}
func configurePinInputPullup(p drivers.PinInput) {}
func pinIsNoPin(a any) bool { return false }
+10
View File
@@ -0,0 +1,10 @@
//go:build baremetal && fe310
package legacy
import "machine"
const (
pulldown = machine.PinInput
pullup = machine.PinInput
)
+13
View File
@@ -0,0 +1,13 @@
//go:build baremetal && !fe310
package legacy
import "machine"
// If you are getting a build error here you then we missed adding
// your CPU build tag to the list of CPUs that do not have pulldown/pullups.
// Add it above and in pinhal_nopulls! You should also add a smoketest for it :)
const (
pulldown = machine.PinInputPulldown
pullup = machine.PinInputPullup
)
+37
View File
@@ -0,0 +1,37 @@
//go:build baremetal
package legacy
import (
"machine"
"tinygo.org/x/drivers"
)
func configurePinOut(po drivers.PinOutput) {
configurePin(po, machine.PinOutput)
}
func configurePinInputPulldown(pi drivers.PinInput) {
configurePin(pi, pulldown) // some chips do not have pull down, in which case pulldown==machine.PinInput.
}
func configurePinInput(pi drivers.PinInput) {
configurePin(pi, machine.PinInput)
}
func configurePinInputPullup(pi drivers.PinInput) {
configurePin(pi, pullup) // some chips do not have pull up, in which case pullup==machine.PinInput.
}
func pinIsNoPin(a any) bool {
p, ok := a.(machine.Pin)
return ok && p == machine.NoPin
}
func configurePin(p any, mode machine.PinMode) {
machinePin, ok := p.(machine.Pin)
if ok {
machinePin.Configure(machine.PinConfig{Mode: mode})
}
}

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