mirror of
https://github.com/tinygo-org/drivers.git
synced 2026-07-26 18:48:41 +00:00
Compare commits
81 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5c305f9b6f | |||
| 1e4110b5a2 | |||
| e286861661 | |||
| 589bf19b01 | |||
| ff4d15cea9 | |||
| d02d21ecea | |||
| 805e2a02f8 | |||
| c4ff8242a7 | |||
| 82c41dbf14 | |||
| c4168864fd | |||
| dbc9022f6a | |||
| d41bc0b85f | |||
| e7f90166ad | |||
| 156d6e7c9c | |||
| 6e04decf19 | |||
| afb1ea39c2 | |||
| 382e79a748 | |||
| 06dd60fba2 | |||
| 27a7d5031e | |||
| 6281f36662 | |||
| ccfcfa837e | |||
| 87c205fd65 | |||
| f57b5ecee9 | |||
| 486949686c | |||
| 00578a3a81 | |||
| 17f273243d | |||
| 0623bb425c | |||
| 019bbbe5fb | |||
| d04e68bef8 | |||
| 76a4276b5d | |||
| 7d7efe25e7 | |||
| 0186d0905d | |||
| 2f3b5ca59a | |||
| ecae5e28ad | |||
| bcf3b84654 | |||
| 4fb5d7a0e5 | |||
| f12454d4f7 | |||
| 829ae09651 | |||
| 30f540c29f | |||
| 6d431e0726 | |||
| f308f8fce0 | |||
| 1c2b802f47 | |||
| ce80e7582f | |||
| 109cab4f3b | |||
| 07216d3051 | |||
| d688fa3f3f | |||
| dd44f9220b | |||
| ee3842f639 | |||
| 1bf1a11067 | |||
| 848f5def03 | |||
| 4a0bcba87c | |||
| 7d983647ad | |||
| 831982ad33 | |||
| 9063f46313 | |||
| 2a621dc3b1 | |||
| 7dbca2a543 | |||
| 50b6f18cc2 | |||
| bb0e6b8ba8 | |||
| 5eeba138a4 | |||
| 1095629f62 | |||
| bf0b383176 | |||
| f206f6f56f | |||
| a0293643b3 | |||
| 60801ba852 | |||
| a74770b2e6 | |||
| 3ed09d3fe1 | |||
| 22581dfd58 | |||
| 31f09d93b8 | |||
| 0ea969f25a | |||
| 8665d44e0f | |||
| c344e5d879 | |||
| 308763f500 | |||
| c41f2e472d | |||
| aa7bd47c68 | |||
| e7931c6a22 | |||
| 3c5e17423a | |||
| 0262122ccd | |||
| 7d24c3c285 | |||
| 5c0f0480bc | |||
| 8642886f73 | |||
| 1a96fc4547 |
@@ -0,0 +1,2 @@
|
||||
# These are supported funding model platforms
|
||||
open_collective: tinygo
|
||||
+218
@@ -1,3 +1,221 @@
|
||||
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**
|
||||
- prepare for CGo changes in TinyGo
|
||||
|
||||
- **new devices**
|
||||
- **adafruit4650**
|
||||
- support for Adafruit 4650 feather OLED
|
||||
- **net**
|
||||
- new networking support based on tinygo net package
|
||||
- **pixel**
|
||||
- add package for efficiently working with raw pixel buffers
|
||||
- **rotary**
|
||||
- Adding driver for rotary encoder support
|
||||
- **seesaw**
|
||||
- Adding support for Adafruit Seesaw platform
|
||||
- **sgp30**
|
||||
- add SGP30 air quality sensor
|
||||
- **sk6812**
|
||||
- added support for SK6812 to WS2812 device (#610)
|
||||
|
||||
- **enhancements**
|
||||
- **epd2in13**
|
||||
- add Sleep method like other displays
|
||||
- unify rotation configuration with other displays
|
||||
- use better black/white approximation
|
||||
- **ili9341**
|
||||
- add DrawBitmap method
|
||||
- **lora/lorawan**
|
||||
- LoRa WAN US915 Support
|
||||
- LoRa WAN add setter functions
|
||||
- refactor shared functionality for channels/regions
|
||||
- **mcp2515**
|
||||
- Add more line speeds to mcp2515.go (#626)
|
||||
- **rtl8720dn**
|
||||
- use drivers package version as the driver version
|
||||
- **ssd1306**
|
||||
- improvements needed for Thumby SPI display
|
||||
- **st7735**
|
||||
- make the display generic over RGB565 and RGB444
|
||||
- **st7789**
|
||||
- add DrawBitmap method
|
||||
- make the display generic over RGB565 and RGB444
|
||||
- **wifinina**
|
||||
- add ResetIsHigh cfg switch for MKR 1010 (copied from #561)
|
||||
- maintenence. Also see PR #4085 in the main TinyGo repo
|
||||
- use drivers package version as the driver version
|
||||
|
||||
- **bugfixes**
|
||||
- **adxl345**
|
||||
- Use int16 for ADXL345 readings (#656)
|
||||
- **at24cx**
|
||||
- fixed the description of the device struct
|
||||
- **rtl8720dn**
|
||||
- allow connecting to open wifi access points
|
||||
- fix check for bad Wifi connect
|
||||
- **sh1106**
|
||||
- fix I2C interface and add smoketest
|
||||
- fixed the description of the device struct
|
||||
- **wifinina**
|
||||
- add 'unknown failure' reason code for AP connect
|
||||
- fix concurrency issues with multiple sockets
|
||||
- fix wifinina UDP send
|
||||
|
||||
- **examples**
|
||||
- **ds3231**
|
||||
- fix the description in the example
|
||||
- **lorawan**
|
||||
- add missing functions for simulated interface
|
||||
- modify atcmd and basic demo to support choosing any one of the supported regions at compile time by using ldflags
|
||||
- **net**
|
||||
- all networking examples now using netdev and netlink.
|
||||
|
||||
- **build**
|
||||
- **all**
|
||||
- fix broken testrunner
|
||||
- migrated legacy I2C
|
||||
- add natiu package for tests
|
||||
- **smoketest**
|
||||
- add stack-size param for net tests.
|
||||
- allow stack-size flag as it is needed for net examples
|
||||
|
||||
|
||||
0.26.0
|
||||
---
|
||||
- **core**
|
||||
|
||||
@@ -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,4 +1,4 @@
|
||||
Copyright (c) 2018-2023 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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
[](https://pkg.go.dev/tinygo.org/x/drivers) [](https://github.com/tinygo-org/drivers/actions/workflows/build.yml)
|
||||
|
||||
|
||||
This package provides a collection of 101 different hardware drivers for devices such as sensors and displays that can be used together with [TinyGo](https://tinygo.org).
|
||||
This package provides a collection of 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!
|
||||
|
||||
+7
-7
@@ -95,16 +95,16 @@ func (d *Device) Restart() {
|
||||
func (d *Device) ReadAcceleration() (x int32, y int32, z int32, err error) {
|
||||
rx, ry, rz := d.ReadRawAcceleration()
|
||||
|
||||
x = d.dataFormat.convertToIS(rx)
|
||||
y = d.dataFormat.convertToIS(ry)
|
||||
z = d.dataFormat.convertToIS(rz)
|
||||
x = int32(d.dataFormat.convertToIS(rx))
|
||||
y = int32(d.dataFormat.convertToIS(ry))
|
||||
z = int32(d.dataFormat.convertToIS(rz))
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// ReadRawAcceleration reads the sensor values and returns the raw x, y and z axis
|
||||
// from the adxl345.
|
||||
func (d *Device) ReadRawAcceleration() (x int32, y int32, z int32) {
|
||||
func (d *Device) ReadRawAcceleration() (x int16, y int16, z int16) {
|
||||
data := []byte{0, 0, 0, 0, 0, 0}
|
||||
legacy.ReadRegister(d.bus, uint8(d.Address), REG_DATAX0, data)
|
||||
|
||||
@@ -140,7 +140,7 @@ func (d *Device) SetRange(sensorRange Range) bool {
|
||||
}
|
||||
|
||||
// convertToIS adjusts the raw values from the adxl345 with the range configuration
|
||||
func (d *dataFormat) convertToIS(rawValue int32) int32 {
|
||||
func (d *dataFormat) convertToIS(rawValue int16) int16 {
|
||||
switch d.sensorRange {
|
||||
case RANGE_2G:
|
||||
return rawValue * 4 // rawValue * 2 * 1000 / 512
|
||||
@@ -190,6 +190,6 @@ func (b *bwRate) toByte() (bits uint8) {
|
||||
}
|
||||
|
||||
// readInt converts two bytes to int16
|
||||
func readIntLE(msb byte, lsb byte) int32 {
|
||||
return int32(uint16(msb) | uint16(lsb)<<8)
|
||||
func readIntLE(msb byte, lsb byte) int16 {
|
||||
return int16(uint16(msb) | uint16(lsb)<<8)
|
||||
}
|
||||
|
||||
+2
-2
@@ -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
|
||||
|
||||
+7
-8
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
package encoders
|
||||
|
||||
type QuadratureDevice struct {
|
||||
cfg QuadratureConfig
|
||||
impl quadratureImpl
|
||||
}
|
||||
|
||||
type QuadratureConfig struct {
|
||||
Precision int
|
||||
}
|
||||
|
||||
type quadratureImpl interface {
|
||||
configure(cfg QuadratureConfig) error
|
||||
readValue() int
|
||||
writeValue(int)
|
||||
}
|
||||
|
||||
func (enc *QuadratureDevice) Configure(cfg QuadratureConfig) error {
|
||||
if cfg.Precision < 1 {
|
||||
cfg.Precision = 4
|
||||
}
|
||||
enc.cfg = cfg
|
||||
return enc.impl.configure(cfg)
|
||||
}
|
||||
|
||||
// Position returns the stored int value for the encoder
|
||||
func (enc *QuadratureDevice) Position() int {
|
||||
return enc.impl.readValue() / enc.cfg.Precision
|
||||
}
|
||||
|
||||
// SetPosition overwrites the currently stored value with the specified int value
|
||||
func (enc *QuadratureDevice) SetPosition(v int) {
|
||||
enc.impl.writeValue(v * enc.cfg.Precision)
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
//go:build tinygo && (rp2040 || stm32 || k210 || esp32c3 || nrf || sam || (avr && (atmega328p || atmega328pb)))
|
||||
|
||||
// Implementation based on:
|
||||
// https://gist.github.com/aykevl/3fc1683ed77bb0a9c07559dfe857304a
|
||||
|
||||
// Note: build constraints in this file list targets that define machine.PinToggle.
|
||||
// If this is supported for additional targets in the future, they can be added above.
|
||||
|
||||
package encoders
|
||||
|
||||
import (
|
||||
"machine"
|
||||
"runtime/volatile"
|
||||
)
|
||||
|
||||
var (
|
||||
states = []int8{0, -1, 1, 0, 1, 0, 0, -1, -1, 0, 0, 1, 0, 1, -1, 0}
|
||||
)
|
||||
|
||||
// NewQuadratureViaInterrupt returns a rotary encoder device that uses GPIO
|
||||
// interrupts and a lookup table to keep track of quadrature state changes.
|
||||
//
|
||||
// This constructur is only available for TinyGo targets for which machine.PinToggle
|
||||
// is defined as a valid interrupt type.
|
||||
func NewQuadratureViaInterrupt(pinA, pinB machine.Pin) *QuadratureDevice {
|
||||
return &QuadratureDevice{impl: &quadInterruptImpl{pinA: pinA, pinB: pinB, oldAB: 0b00000011}}
|
||||
}
|
||||
|
||||
type quadInterruptImpl struct {
|
||||
pinA machine.Pin
|
||||
pinB machine.Pin
|
||||
|
||||
// precision int
|
||||
|
||||
oldAB int
|
||||
value volatile.Register32
|
||||
}
|
||||
|
||||
func (enc *quadInterruptImpl) configure(cfg QuadratureConfig) error {
|
||||
enc.pinA.Configure(machine.PinConfig{Mode: machine.PinInputPullup})
|
||||
enc.pinA.SetInterrupt(machine.PinToggle, enc.interrupt)
|
||||
|
||||
enc.pinB.Configure(machine.PinConfig{Mode: machine.PinInputPullup})
|
||||
enc.pinB.SetInterrupt(machine.PinToggle, enc.interrupt)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (enc *quadInterruptImpl) interrupt(pin machine.Pin) {
|
||||
aHigh, bHigh := enc.pinA.Get(), enc.pinB.Get()
|
||||
enc.oldAB <<= 2
|
||||
if aHigh {
|
||||
enc.oldAB |= 1 << 1
|
||||
}
|
||||
if bHigh {
|
||||
enc.oldAB |= 1
|
||||
}
|
||||
enc.writeValue(enc.readValue() + int(states[enc.oldAB&0x0f]))
|
||||
}
|
||||
|
||||
// readValue gets the value using volatile operations and returns it as an int
|
||||
func (enc *quadInterruptImpl) readValue() int {
|
||||
return int(enc.value.Get())
|
||||
}
|
||||
|
||||
// writeValue set the value to the specified int using volatile operations
|
||||
func (enc *quadInterruptImpl) writeValue(v int) {
|
||||
enc.value.Set(uint32(v))
|
||||
}
|
||||
+3
-4
@@ -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) {
|
||||
@@ -218,8 +218,8 @@ func (d *Device) Listen(sockfd int, backlog int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Device) Accept(sockfd int, ip netip.AddrPort) (int, error) {
|
||||
return -1, netdev.ErrNotSupported
|
||||
func (d *Device) Accept(sockfd int) (int, netip.AddrPort, error) {
|
||||
return -1, netip.AddrPort{}, netdev.ErrNotSupported
|
||||
}
|
||||
|
||||
func (d *Device) sendChunk(sockfd int, buf []byte, deadline time.Time) (int, error) {
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
//go:build macropad_rp2040
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
|
||||
"tinygo.org/x/drivers/encoders"
|
||||
)
|
||||
|
||||
var (
|
||||
enc = encoders.NewQuadratureViaInterrupt(machine.ROT_A, machine.ROT_B)
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
enc.Configure(encoders.QuadratureConfig{
|
||||
Precision: 4,
|
||||
})
|
||||
|
||||
for oldValue := 0; ; {
|
||||
if newValue := enc.Position(); newValue != oldValue {
|
||||
println("value: ", newValue)
|
||||
oldValue = newValue
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -9,7 +9,7 @@
|
||||
// examples/net/webclient (for HTTP)
|
||||
// examples/net/tlsclient (for HTTPS)
|
||||
|
||||
//go:build pyportal || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 || wioterminal
|
||||
//go:build ninafw || wioterminal || comboat_fw
|
||||
|
||||
package main
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
// examples/net/webclient (for HTTP)
|
||||
// examples/net/tlsclient (for HTTPS)
|
||||
|
||||
//go:build pyportal || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 || wioterminal
|
||||
//go:build ninafw || wioterminal || comboat_fw
|
||||
|
||||
package main
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
// examples/net/webclient (for HTTP)
|
||||
// examples/net/tlsclient (for HTTPS)
|
||||
|
||||
//go:build pyportal || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 || wioterminal
|
||||
//go:build ninafw || wioterminal || comboat_fw
|
||||
|
||||
package main
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
// examples/net/webclient (for HTTP)
|
||||
// examples/net/tlsclient (for HTTPS)
|
||||
|
||||
//go:build pyportal || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 || wioterminal
|
||||
//go:build ninafw || wioterminal || comboat_fw
|
||||
|
||||
package main
|
||||
|
||||
|
||||
@@ -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 pyportal || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 || wioterminal || challenger_rp2040
|
||||
//go:build ninafw || wioterminal || challenger_rp2040 || comboat_fw
|
||||
|
||||
package main
|
||||
|
||||
|
||||
@@ -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 pyportal || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 || wioterminal || challenger_rp2040
|
||||
//go:build ninafw || wioterminal || challenger_rp2040 || comboat_fw
|
||||
|
||||
package main
|
||||
|
||||
|
||||
@@ -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 pyportal || arduino_nano33 || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 || wioterminal || challenger_rp2040
|
||||
//go:build ninafw || wioterminal || challenger_rp2040 || comboat_fw
|
||||
|
||||
package main
|
||||
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
//go:build ninafw || wioterminal || comboat_fw
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/netlink"
|
||||
"tinygo.org/x/drivers/netlink/probe"
|
||||
)
|
||||
|
||||
var (
|
||||
ssid string
|
||||
pass string
|
||||
)
|
||||
|
||||
func init() {
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
link, _ := probe.Probe()
|
||||
|
||||
err := link.NetConnect(&netlink.ConnectParams{
|
||||
Ssid: ssid,
|
||||
Passphrase: pass,
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
// This example is the classic snake network test. The snake is feed a steady
|
||||
// diet of pkts and the pkts work themselves thru the snake segments and exit
|
||||
// the tail. Each snake segment is a TCP socket connection to a server. The
|
||||
// server echos pkts received back to the snake, and serves each segment on a
|
||||
// different port. (See server/main.go for server).
|
||||
//
|
||||
// snake | server
|
||||
// |
|
||||
// head ----->---|-->--+
|
||||
// seg a | |
|
||||
// +---<-|--<--+
|
||||
// | |
|
||||
// +-->--|-->--+
|
||||
// seg b | |
|
||||
// +---<-|--<--+
|
||||
// | |
|
||||
// +-->--|-->--+
|
||||
// seg c | |
|
||||
// +---<-|--<--+
|
||||
// | |
|
||||
// +-->--|-->--+
|
||||
// ... | |
|
||||
// +---<-|--<--+
|
||||
// | |
|
||||
// +-->--|-->--+
|
||||
// seg n | |
|
||||
// tail -------<-|--<--+
|
||||
// |
|
||||
|
||||
// The snake segments are linked by channels and each segment is run as a go
|
||||
// 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 || comboat_fw
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
//go:embed main.go
|
||||
var code string
|
||||
|
||||
var (
|
||||
server string = "10.0.0.100:8080"
|
||||
)
|
||||
|
||||
func segment(in chan []byte, out chan []byte) {
|
||||
var buf [512]byte
|
||||
for {
|
||||
c, err := net.Dial("tcp", server)
|
||||
for ; err != nil; c, err = net.Dial("tcp", server) {
|
||||
println(err.Error())
|
||||
time.Sleep(5 * time.Second)
|
||||
}
|
||||
for {
|
||||
select {
|
||||
case msg := <-in:
|
||||
_, err := c.Write(msg)
|
||||
if err != nil {
|
||||
log.Fatal(err.Error())
|
||||
}
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
n, err := c.Read(buf[:])
|
||||
if err != nil {
|
||||
log.Fatal(err.Error())
|
||||
}
|
||||
out <- buf[:n]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func feedit(head chan []byte) {
|
||||
for i := 0; i < 100; i++ {
|
||||
head <- []byte(fmt.Sprintf("\n---%d---\n", i))
|
||||
for _, line := range strings.Split(code, "\n") {
|
||||
if len(line) == 0 {
|
||||
line = " "
|
||||
}
|
||||
head <- []byte(line)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var head = make(chan []byte)
|
||||
var a = make(chan []byte)
|
||||
var b = make(chan []byte)
|
||||
var c = make(chan []byte)
|
||||
var d = make(chan []byte)
|
||||
var e = make(chan []byte)
|
||||
var f = make(chan []byte)
|
||||
var tail = make(chan []byte)
|
||||
|
||||
func main() {
|
||||
|
||||
// The snake
|
||||
go segment(head, a)
|
||||
go segment(a, b)
|
||||
go segment(b, c)
|
||||
go segment(c, d)
|
||||
go segment(d, e)
|
||||
go segment(e, f)
|
||||
go segment(f, tail)
|
||||
|
||||
go feedit(head)
|
||||
|
||||
for {
|
||||
select {
|
||||
case msg := <-tail:
|
||||
println(string(msg))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Listen for connections
|
||||
l, err := net.Listen("tcp", ":8080")
|
||||
if err != nil {
|
||||
log.Fatal(err.Error())
|
||||
}
|
||||
defer l.Close()
|
||||
println("Listening on port", ":8080")
|
||||
for {
|
||||
// Wait for a connection
|
||||
conn, err := l.Accept()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
println("Accepted connection from", conn.RemoteAddr().String())
|
||||
// Service the new connection in a goroutine.
|
||||
// The loop then returns to accepting, so that
|
||||
// multiple connections may be served concurrently
|
||||
go func(c net.Conn) {
|
||||
// Echo all incoming data
|
||||
io.Copy(c, c)
|
||||
// Shut down the connection
|
||||
c.Close()
|
||||
}(conn)
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
//
|
||||
// nc -lk 8080
|
||||
|
||||
//go:build pyportal || arduino_nano33 || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 || wioterminal || challenger_rp2040
|
||||
//go:build ninafw || wioterminal || challenger_rp2040 || comboat_fw
|
||||
|
||||
package main
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
//
|
||||
// nc -lk 8080
|
||||
|
||||
//go:build pyportal || arduino_nano33 || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 || wioterminal || challenger_rp2040 || pico
|
||||
//go:build ninafw || wioterminal || challenger_rp2040 || comboat_fw
|
||||
|
||||
package main
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
//
|
||||
// $ nc 10.0.0.2 8080 <file >copy ; cmp file copy
|
||||
|
||||
//go:build pyportal || arduino_nano33 || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 || wioterminal
|
||||
//go:build ninafw || wioterminal
|
||||
|
||||
package main
|
||||
|
||||
@@ -30,16 +30,18 @@ var (
|
||||
var buf [1024]byte
|
||||
|
||||
func echo(conn net.Conn) {
|
||||
println("Client", conn.RemoteAddr(), "connected")
|
||||
defer conn.Close()
|
||||
_, err := io.CopyBuffer(conn, conn, buf[:])
|
||||
if err != nil && err != io.EOF {
|
||||
log.Fatal(err.Error())
|
||||
}
|
||||
println("Client", conn.RemoteAddr(), "closed")
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
time.Sleep(time.Second)
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
link, _ := probe.Probe()
|
||||
|
||||
@@ -51,6 +53,7 @@ func main() {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
println("Starting TCP server listening on", port)
|
||||
l, err := net.Listen("tcp", port)
|
||||
if err != nil {
|
||||
log.Fatal(err.Error())
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
//
|
||||
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security
|
||||
|
||||
//go:build pyportal || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 || wioterminal
|
||||
//go:build ninafw || wioterminal || comboat_fw
|
||||
|
||||
package main
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
// }
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
//go:build pyportal || arduino_nano33 || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 || wioterminal
|
||||
//go:build ninafw || wioterminal || comboat_fw
|
||||
|
||||
package main
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
// Note: It may be necessary to increase the stack size when using "net/http".
|
||||
// Use the -stack-size=4KB command line option.
|
||||
|
||||
//go:build pyportal || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 || wioterminal
|
||||
//go:build ninafw || wioterminal
|
||||
|
||||
package main
|
||||
|
||||
|
||||
@@ -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 pyportal || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 || wioterminal
|
||||
//go:build ninafw || wioterminal || comboat_fw
|
||||
|
||||
package main
|
||||
|
||||
|
||||
@@ -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 pyportal || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 || wioterminal
|
||||
//go:build ninafw || wioterminal
|
||||
|
||||
package main
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// Note: It may be necessary to increase the stack size when using "net/http".
|
||||
// Use the -stack-size=4KB command line option.
|
||||
|
||||
//go:build pyportal || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 || wioterminal
|
||||
//go:build ninafw || wioterminal
|
||||
|
||||
package main
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
)
|
||||
|
||||
func init() {
|
||||
spi = &machine.SPI2
|
||||
spi = machine.SPI2
|
||||
sckPin = machine.SCK2
|
||||
sdoPin = machine.SDO2
|
||||
sdiPin = machine.SDI2
|
||||
|
||||
+19
-13
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,6 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
|
||||
"image/color"
|
||||
"time"
|
||||
|
||||
@@ -10,21 +8,21 @@ import (
|
||||
)
|
||||
|
||||
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,
|
||||
})
|
||||
|
||||
// Thumby will have preset size.
|
||||
// If not compiling for thumby the width and height will be whatever we suggest
|
||||
const suggestHeight = 32
|
||||
const suggestWidth = 128
|
||||
var display *ssd1306.Device
|
||||
var err error
|
||||
display, err = makeSSD1306(suggestWidth, suggestHeight)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
display.ClearDisplay()
|
||||
|
||||
x := int16(0)
|
||||
y := int16(0)
|
||||
width, height := display.Size()
|
||||
x := int16(width)
|
||||
y := int16(height)
|
||||
deltaX := int16(1)
|
||||
deltaY := int16(1)
|
||||
for {
|
||||
@@ -39,11 +37,11 @@ func main() {
|
||||
x += deltaX
|
||||
y += deltaY
|
||||
|
||||
if x == 0 || x == 127 {
|
||||
if x == 0 || x == width-1 {
|
||||
deltaX = -deltaX
|
||||
}
|
||||
|
||||
if y == 0 || y == 31 {
|
||||
if y == 0 || y == height-1 {
|
||||
deltaY = -deltaY
|
||||
}
|
||||
time.Sleep(1 * time.Millisecond)
|
||||
@@ -0,0 +1,29 @@
|
||||
//go:build !thumby
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
|
||||
"tinygo.org/x/drivers/ssd1306"
|
||||
)
|
||||
|
||||
func makeSSD1306(width, height int16) (*ssd1306.Device, error) {
|
||||
err := machine.I2C0.Configure(machine.I2CConfig{
|
||||
Frequency: 400 * machine.KHz,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
address := uint16(ssd1306.Address)
|
||||
if width == 128 && (height == 32 || height == 64) {
|
||||
address = ssd1306.Address_128_32
|
||||
}
|
||||
display := ssd1306.NewI2C(machine.I2C0)
|
||||
display.Configure(ssd1306.Config{
|
||||
Address: address,
|
||||
Width: width,
|
||||
Height: height,
|
||||
})
|
||||
return &display, nil
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
//go:build thumby
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
|
||||
"tinygo.org/x/drivers/ssd1306"
|
||||
)
|
||||
|
||||
func makeSSD1306(_, _ int16) (*ssd1306.Device, error) {
|
||||
// width and height are known for thumby.
|
||||
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, nil
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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
@@ -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) {
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
+2
-1
@@ -80,7 +80,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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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
@@ -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{},
|
||||
|
||||
+5
-1
@@ -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 (
|
||||
|
||||
+1
-1
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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" }
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
}
|
||||
@@ -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
|
||||
)
|
||||
+35
-24
@@ -8,7 +8,6 @@ import (
|
||||
"errors"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
"tinygo.org/x/drivers/internal/legacy"
|
||||
)
|
||||
|
||||
type AccelRange uint8
|
||||
@@ -26,7 +25,7 @@ type Device struct {
|
||||
accelSampleRate AccelSampleRate
|
||||
gyroRange GyroRange
|
||||
gyroSampleRate GyroSampleRate
|
||||
buf [6]uint8
|
||||
buf [7]uint8 // up to 6 bytes for read + 1 byte for the register address
|
||||
}
|
||||
|
||||
// Configuration for LSM6DS3TR device.
|
||||
@@ -84,30 +83,20 @@ func (d *Device) doConfigure(cfg Configuration) (err error) {
|
||||
d.gyroSampleRate = GYRO_SR_104
|
||||
}
|
||||
|
||||
data := d.buf[:1]
|
||||
|
||||
// Configure accelerometer
|
||||
data[0] = uint8(d.accelRange) | uint8(d.accelSampleRate)
|
||||
err = legacy.WriteRegister(d.bus, uint8(d.Address), CTRL1_XL, data)
|
||||
err = d.writeByte(CTRL1_XL, uint8(d.accelRange)|uint8(d.accelSampleRate))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Set ODR bit
|
||||
err = legacy.ReadRegister(d.bus, uint8(d.Address), CTRL4_C, data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
data[0] = data[0] &^ BW_SCAL_ODR_ENABLED
|
||||
data[0] |= BW_SCAL_ODR_ENABLED
|
||||
err = legacy.WriteRegister(d.bus, uint8(d.Address), CTRL4_C, data)
|
||||
// Enable ODR scaling
|
||||
err = d.setBits(CTRL4_C, BW_SCAL_ODR_ENABLED)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Configure gyroscope
|
||||
data[0] = uint8(d.gyroRange) | uint8(d.gyroSampleRate)
|
||||
err = legacy.WriteRegister(d.bus, uint8(d.Address), CTRL2_G, data)
|
||||
err = d.writeByte(CTRL2_G, uint8(d.gyroRange)|uint8(d.gyroSampleRate))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -118,8 +107,10 @@ func (d *Device) doConfigure(cfg Configuration) (err error) {
|
||||
// Connected returns whether a LSM6DS3TR has been found.
|
||||
// It does a "who am I" request and checks the response.
|
||||
func (d *Device) Connected() bool {
|
||||
data := d.buf[:1]
|
||||
legacy.ReadRegister(d.bus, uint8(d.Address), WHO_AM_I, data)
|
||||
data, err := d.readBytes(WHO_AM_I, 1)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return data[0] == 0x6A
|
||||
}
|
||||
|
||||
@@ -128,8 +119,7 @@ func (d *Device) Connected() bool {
|
||||
// and the sensor is not moving the returned value will be around 1000000 or
|
||||
// -1000000.
|
||||
func (d *Device) ReadAcceleration() (x, y, z int32, err error) {
|
||||
data := d.buf[:6]
|
||||
err = legacy.ReadRegister(d.bus, uint8(d.Address), OUTX_L_XL, data)
|
||||
data, err := d.readBytes(OUTX_L_XL, 6)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -153,8 +143,7 @@ func (d *Device) ReadAcceleration() (x, y, z int32, err error) {
|
||||
// rotation along one axis and while doing so integrate all values over time,
|
||||
// you would get a value close to 360000000.
|
||||
func (d *Device) ReadRotation() (x, y, z int32, err error) {
|
||||
data := d.buf[:6]
|
||||
err = legacy.ReadRegister(d.bus, uint8(d.Address), OUTX_L_G, data)
|
||||
data, err := d.readBytes(OUTX_L_G, 6)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -177,8 +166,7 @@ func (d *Device) ReadRotation() (x, y, z int32, err error) {
|
||||
|
||||
// ReadTemperature returns the temperature in celsius milli degrees (°C/1000)
|
||||
func (d *Device) ReadTemperature() (t int32, err error) {
|
||||
data := d.buf[:2]
|
||||
err = legacy.ReadRegister(d.bus, uint8(d.Address), OUT_TEMP_L, data)
|
||||
data, err := d.readBytes(OUT_TEMP_L, 2)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -187,3 +175,26 @@ func (d *Device) ReadTemperature() (t int32, err error) {
|
||||
t = 25000 + (int32(int16((int16(data[1])<<8)|int16(data[0])))*125)/32
|
||||
return
|
||||
}
|
||||
|
||||
func (d *Device) readBytes(reg, size uint8) ([]byte, error) {
|
||||
d.buf[0] = reg
|
||||
err := d.bus.Tx(d.Address, d.buf[0:1], d.buf[1:size+1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return d.buf[1 : size+1], nil
|
||||
}
|
||||
|
||||
func (d *Device) writeByte(reg, value uint8) error {
|
||||
d.buf[0] = reg
|
||||
d.buf[1] = value
|
||||
return d.bus.Tx(d.Address, d.buf[0:2], nil)
|
||||
}
|
||||
|
||||
func (d *Device) setBits(reg, bits uint8) error {
|
||||
data, err := d.readBytes(reg, 1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return d.writeByte(reg, (data[0]&^bits)|bits)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
// Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/max6675.pdf
|
||||
package max6675
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"machine"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
)
|
||||
|
||||
// ErrThermocoupleOpen is returned when the thermocouple input is open.
|
||||
// i.e. not attached or faulty
|
||||
var ErrThermocoupleOpen = errors.New("thermocouple input open")
|
||||
|
||||
type Device struct {
|
||||
bus drivers.SPI
|
||||
cs machine.Pin
|
||||
}
|
||||
|
||||
// Create a new Device to read from a MAX6675 thermocouple.
|
||||
// Pins must be configured before use. Frequency for SPI
|
||||
// should be 4.3MHz maximum.
|
||||
func NewDevice(bus drivers.SPI, cs machine.Pin) *Device {
|
||||
return &Device{
|
||||
bus: bus,
|
||||
cs: cs,
|
||||
}
|
||||
}
|
||||
|
||||
// Read and return the temperature in celsius
|
||||
func (d *Device) Read() (float32, error) {
|
||||
var (
|
||||
read []byte = []byte{0, 0}
|
||||
value uint16
|
||||
)
|
||||
|
||||
d.cs.Low()
|
||||
if err := d.bus.Tx([]byte{0, 0}, read); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
d.cs.High()
|
||||
|
||||
// datasheet: Bit D2 is normally low and goes high if the thermocouple input is open.
|
||||
if read[1]&0x04 == 0x04 {
|
||||
return 0, ErrThermocoupleOpen
|
||||
}
|
||||
|
||||
// data is 12 bits, split across the two bytes
|
||||
// -XXXXXXX XXXXX---
|
||||
value = (uint16(read[0]) << 5) | (uint16(read[1]) >> 3)
|
||||
|
||||
return float32(value) * 0.25, nil
|
||||
}
|
||||
+4
-2
@@ -4,17 +4,19 @@ package max72xx
|
||||
|
||||
import (
|
||||
"machine"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
)
|
||||
|
||||
type Device struct {
|
||||
bus machine.SPI
|
||||
bus drivers.SPI
|
||||
cs machine.Pin
|
||||
}
|
||||
|
||||
// NewDriver creates a new max7219 connection. The SPI wire must already be configured
|
||||
// The SPI frequency must not be higher than 10MHz.
|
||||
// parameter cs: the datasheet also refers to this pin as "load" pin.
|
||||
func NewDevice(bus machine.SPI, cs machine.Pin) *Device {
|
||||
func NewDevice(bus drivers.SPI, cs machine.Pin) *Device {
|
||||
return &Device{
|
||||
bus: bus,
|
||||
cs: cs,
|
||||
|
||||
+112
-1
@@ -236,16 +236,83 @@ func (d *Device) getMode() (byte, error) {
|
||||
}
|
||||
|
||||
func (d *Device) configRate(speed, clock byte) error {
|
||||
// TODO: add another baudrate
|
||||
var cfg1, cfg2, cfg3 byte
|
||||
set := true
|
||||
switch clock {
|
||||
case Clock16MHz:
|
||||
switch speed {
|
||||
case CAN5kBps:
|
||||
cfg1 = mcp16mHz5kBpsCfg1
|
||||
cfg2 = mcp16mHz5kBpsCfg2
|
||||
cfg3 = mcp16mHz5kBpsCfg3
|
||||
case CAN10kBps:
|
||||
cfg1 = mcp16mHz10kBpsCfg1
|
||||
cfg2 = mcp16mHz10kBpsCfg2
|
||||
cfg3 = mcp16mHz10kBpsCfg3
|
||||
case CAN20kBps:
|
||||
cfg1 = mcp16mHz20kBpsCfg1
|
||||
cfg2 = mcp16mHz20kBpsCfg2
|
||||
cfg3 = mcp16mHz20kBpsCfg3
|
||||
case CAN25kBps:
|
||||
cfg1 = mcp16mHz25kBpsCfg1
|
||||
cfg2 = mcp16mHz25kBpsCfg2
|
||||
cfg3 = mcp16mHz25kBpsCfg3
|
||||
case CAN31k25Bps:
|
||||
cfg1 = mcp16mHz31k25BpsCfg1
|
||||
cfg2 = mcp16mHz31k25BpsCfg2
|
||||
cfg3 = mcp16mHz31k25BpsCfg3
|
||||
case CAN33kBps:
|
||||
cfg1 = mcp16mHz33kBpsCfg1
|
||||
cfg2 = mcp16mHz33kBpsCfg2
|
||||
cfg3 = mcp16mHz33kBpsCfg3
|
||||
case CAN40kBps:
|
||||
cfg1 = mcp16mHz40kBpsCfg1
|
||||
cfg2 = mcp16mHz40kBpsCfg2
|
||||
cfg3 = mcp16mHz40kBpsCfg3
|
||||
case CAN47kBps:
|
||||
cfg1 = mcp16mHz47kBpsCfg1
|
||||
cfg2 = mcp16mHz47kBpsCfg2
|
||||
cfg3 = mcp16mHz47kBpsCfg3
|
||||
case CAN50kBps:
|
||||
cfg1 = mcp16mHz50kBpsCfg1
|
||||
cfg2 = mcp16mHz50kBpsCfg2
|
||||
cfg3 = mcp16mHz50kBpsCfg3
|
||||
case CAN80kBps:
|
||||
cfg1 = mcp16mHz80kBpsCfg1
|
||||
cfg2 = mcp16mHz80kBpsCfg2
|
||||
cfg3 = mcp16mHz80kBpsCfg3
|
||||
case CAN83k3Bps:
|
||||
cfg1 = mcp16mHz83k3BpsCfg1
|
||||
cfg2 = mcp16mHz83k3BpsCfg2
|
||||
cfg3 = mcp16mHz83k3BpsCfg3
|
||||
case CAN95kBps:
|
||||
cfg1 = mcp16mHz95kBpsCfg1
|
||||
cfg2 = mcp16mHz95kBpsCfg2
|
||||
cfg3 = mcp16mHz95kBpsCfg3
|
||||
case CAN100kBps:
|
||||
cfg1 = mcp16mHz100kBpsCfg1
|
||||
cfg2 = mcp16mHz100kBpsCfg2
|
||||
cfg3 = mcp16mHz100kBpsCfg3
|
||||
case CAN125kBps:
|
||||
cfg1 = mcp16mHz125kBpsCfg1
|
||||
cfg2 = mcp16mHz125kBpsCfg2
|
||||
cfg3 = mcp16mHz125kBpsCfg3
|
||||
case CAN200kBps:
|
||||
cfg1 = mcp16mHz200kBpsCfg1
|
||||
cfg2 = mcp16mHz200kBpsCfg2
|
||||
cfg3 = mcp16mHz200kBpsCfg3
|
||||
case CAN250kBps:
|
||||
cfg1 = mcp16mHz250kBpsCfg1
|
||||
cfg2 = mcp16mHz250kBpsCfg2
|
||||
cfg3 = mcp16mHz250kBpsCfg3
|
||||
case CAN500kBps:
|
||||
cfg1 = mcp16mHz500kBpsCfg1
|
||||
cfg2 = mcp16mHz500kBpsCfg2
|
||||
cfg3 = mcp16mHz500kBpsCfg3
|
||||
case CAN666kBps:
|
||||
cfg1 = mcp16mHz666kBpsCfg1
|
||||
cfg2 = mcp16mHz666kBpsCfg2
|
||||
cfg3 = mcp16mHz666kBpsCfg3
|
||||
case CAN1000kBps:
|
||||
cfg1 = mcp16mHz1000kBpsCfg1
|
||||
cfg2 = mcp16mHz1000kBpsCfg2
|
||||
@@ -255,6 +322,50 @@ func (d *Device) configRate(speed, clock byte) error {
|
||||
}
|
||||
case Clock8MHz:
|
||||
switch speed {
|
||||
case CAN5kBps:
|
||||
cfg1 = mcp8mHz5kBpsCfg1
|
||||
cfg2 = mcp8mHz5kBpsCfg2
|
||||
cfg3 = mcp8mHz5kBpsCfg3
|
||||
case CAN10kBps:
|
||||
cfg1 = mcp8mHz10kBpsCfg1
|
||||
cfg2 = mcp8mHz10kBpsCfg2
|
||||
cfg3 = mcp8mHz10kBpsCfg3
|
||||
case CAN20kBps:
|
||||
cfg1 = mcp8mHz20kBpsCfg1
|
||||
cfg2 = mcp8mHz20kBpsCfg2
|
||||
cfg3 = mcp8mHz20kBpsCfg3
|
||||
case CAN31k25Bps:
|
||||
cfg1 = mcp8mHz31k25BpsCfg1
|
||||
cfg2 = mcp8mHz31k25BpsCfg2
|
||||
cfg3 = mcp8mHz31k25BpsCfg3
|
||||
case CAN40kBps:
|
||||
cfg1 = mcp8mHz40kBpsCfg1
|
||||
cfg2 = mcp8mHz40kBpsCfg2
|
||||
cfg3 = mcp8mHz40kBpsCfg3
|
||||
case CAN50kBps:
|
||||
cfg1 = mcp8mHz50kBpsCfg1
|
||||
cfg2 = mcp8mHz50kBpsCfg2
|
||||
cfg3 = mcp8mHz50kBpsCfg3
|
||||
case CAN80kBps:
|
||||
cfg1 = mcp8mHz80kBpsCfg1
|
||||
cfg2 = mcp8mHz80kBpsCfg2
|
||||
cfg3 = mcp8mHz80kBpsCfg3
|
||||
case CAN100kBps:
|
||||
cfg1 = mcp8mHz100kBpsCfg1
|
||||
cfg2 = mcp8mHz100kBpsCfg2
|
||||
cfg3 = mcp8mHz100kBpsCfg3
|
||||
case CAN125kBps:
|
||||
cfg1 = mcp8mHz125kBpsCfg1
|
||||
cfg2 = mcp8mHz125kBpsCfg2
|
||||
cfg3 = mcp8mHz125kBpsCfg3
|
||||
case CAN200kBps:
|
||||
cfg1 = mcp8mHz200kBpsCfg1
|
||||
cfg2 = mcp8mHz200kBpsCfg2
|
||||
cfg3 = mcp8mHz200kBpsCfg3
|
||||
case CAN250kBps:
|
||||
cfg1 = mcp8mHz250kBpsCfg1
|
||||
cfg2 = mcp8mHz250kBpsCfg2
|
||||
cfg3 = mcp8mHz250kBpsCfg3
|
||||
case CAN500kBps:
|
||||
cfg1 = mcp8mHz500kBpsCfg1
|
||||
cfg2 = mcp8mHz500kBpsCfg2
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
// Package mcp9808 implements a driver for the MCP9808 High Accuracy I2C Temperature Sensor
|
||||
//
|
||||
// Datasheet: https://cdn-shop.adafruit.com/datasheets/MCP9808.pdf
|
||||
// Module: https://www.adafruit.com/product/1782
|
||||
// Only implemented: temperature reading, resolution read & set
|
||||
package mcp9808
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
)
|
||||
|
||||
type Device struct {
|
||||
bus drivers.I2C
|
||||
Address uint16
|
||||
}
|
||||
|
||||
func New(bus drivers.I2C) Device {
|
||||
return Device{bus, MCP9808_I2CADDR_DEFAULT}
|
||||
}
|
||||
|
||||
func (d *Device) Connected() bool {
|
||||
data := make([]byte, 2)
|
||||
d.Read(MCP9808_REG_DEVICE_ID, &data)
|
||||
return binary.BigEndian.Uint16(data) == MCP9808_DEVICE_ID
|
||||
}
|
||||
|
||||
func (d *Device) ReadTemperature() (float64, error) {
|
||||
data := make([]byte, 2)
|
||||
var temp float64
|
||||
if err := d.Read(MCP9808_REG_AMBIENT_TEMP, &data); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
data[0] = data[0] & 0x1F
|
||||
if data[0]&0x10 == 0x10 {
|
||||
data[0] = data[0] & 0x0F
|
||||
temp = float64(data[0])*16 + float64(data[1])/16.0 - 256
|
||||
}
|
||||
temp = float64(data[0])*16 + float64(data[1])/16.0
|
||||
return temp, nil
|
||||
}
|
||||
|
||||
func (d *Device) ReadResolution() (resolution, error) {
|
||||
data := make([]byte, 2)
|
||||
err := d.Read(MCP9808_REG_RESOLUTION, &data)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
switch data[0] {
|
||||
case 0:
|
||||
return Low, nil
|
||||
case 1:
|
||||
return Medium, nil
|
||||
case 2:
|
||||
return High, nil
|
||||
case 3:
|
||||
return Maximum, nil
|
||||
|
||||
default:
|
||||
return 0, errors.New("unknown resolution")
|
||||
}
|
||||
}
|
||||
|
||||
func (d *Device) SetResolution(r resolution) error {
|
||||
switch r {
|
||||
case Low:
|
||||
if err := d.Write(MCP9808_REG_RESOLUTION, []byte{0x00}); err != nil {
|
||||
return err
|
||||
}
|
||||
case Medium:
|
||||
if err := d.Write(MCP9808_REG_RESOLUTION, []byte{0x01}); err != nil {
|
||||
return err
|
||||
}
|
||||
case High:
|
||||
if err := d.Write(MCP9808_REG_RESOLUTION, []byte{0x02}); err != nil {
|
||||
return err
|
||||
}
|
||||
case Maximum:
|
||||
if err := d.Write(MCP9808_REG_RESOLUTION, []byte{0x03}); err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Device) Write(register byte, data []byte) error {
|
||||
buf := append([]byte{register}, data...)
|
||||
return d.bus.Tx(d.Address, buf, nil)
|
||||
}
|
||||
|
||||
func (d *Device) Read(register byte, data *[]byte) error {
|
||||
return d.bus.Tx(d.Address, []byte{register}, *data)
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
// Package mcp9808 implements a driver for the MCP9808 High Accuracy I2C Temperature Sensor
|
||||
//
|
||||
// Datasheet: https://cdn-shop.adafruit.com/datasheets/MCP9808.pdf
|
||||
// Module: https://www.adafruit.com/product/1782
|
||||
package mcp9808
|
||||
|
||||
// Constants/addresses used for I2C.
|
||||
const (
|
||||
MCP9808_DEVICE_ID = 0x0400
|
||||
MCP9808_MANUF_ID = 0x0054
|
||||
|
||||
MCP9808_I2CADDR_DEFAULT = 0x18 //default I2C address
|
||||
MCP9808_REG_CONFIG = 0x01 //MCP9808 config register
|
||||
|
||||
MCP9808_REG_CONFIG_SHUTDOWN = 0x0100 //shutdown config
|
||||
MCP9808_REG_CONFIG_CRITLOCKED = 0x0080 //critical trip lock
|
||||
MCP9808_REG_CONFIG_WINLOCKED = 0x0040 //alarm window lock
|
||||
MCP9808_REG_CONFIG_INTCLR = 0x0020 //interrupt clear
|
||||
MCP9808_REG_CONFIG_ALERTSTAT = 0x0010 //alert output status
|
||||
MCP9808_REG_CONFIG_ALERTCTRL = 0x0008 //alert output control
|
||||
MCP9808_REG_CONFIG_ALERTSEL = 0x0004 //alert output select
|
||||
MCP9808_REG_CONFIG_ALERTPOL = 0x0002 //alert output polarity
|
||||
MCP9808_REG_CONFIG_ALERTMODE = 0x0001 //alert output mode
|
||||
|
||||
MCP9808_REG_UPPER_TEMP = 0x02 //upper alert boundary
|
||||
MCP9808_REG_LOWER_TEMP = 0x03 //lower alert boundery
|
||||
MCP9808_REG_CRIT_TEMP = 0x04 //critical temperature
|
||||
MCP9808_REG_AMBIENT_TEMP = 0x05 //ambient temperature
|
||||
MCP9808_REG_MANUF_ID = 0x06 //manufacturer ID
|
||||
MCP9808_REG_DEVICE_ID = 0x07 //device ID
|
||||
MCP9808_REG_RESOLUTION = 0x08 //resolution
|
||||
)
|
||||
|
||||
/*
|
||||
======= ============ ==============
|
||||
value resolution reading Time
|
||||
======= ============ ==============
|
||||
|
||||
0 0.5°C 30 ms
|
||||
1 0.25°C 65 ms
|
||||
2 0.125°C 130 ms
|
||||
3 0.0625°C 250 ms
|
||||
|
||||
======= ============ ==============
|
||||
*/
|
||||
type resolution uint8
|
||||
|
||||
const (
|
||||
Low resolution = iota
|
||||
Medium
|
||||
High
|
||||
Maximum
|
||||
)
|
||||
@@ -64,7 +64,7 @@ func (d *Device) Read(r []int32) (int, error) {
|
||||
count := len(r)
|
||||
|
||||
// get the next group of samples
|
||||
machine.I2S0.Read(d.buf)
|
||||
machine.I2S0.ReadStereo(d.buf)
|
||||
|
||||
if len(r) > len(d.buf) {
|
||||
count = len(d.buf)
|
||||
@@ -83,7 +83,7 @@ func (d *Device) ReadWithFilter(r []int32) (int, error) {
|
||||
for i := 0; i < len(r); i++ {
|
||||
|
||||
// get the next group of samples
|
||||
machine.I2S0.Read(d.buf)
|
||||
machine.I2S0.ReadStereo(d.buf)
|
||||
|
||||
// filter
|
||||
sum = applySincFilter(d.buf)
|
||||
|
||||
+4
-2
@@ -35,10 +35,12 @@ var (
|
||||
var (
|
||||
ErrFamilyNotSupported = errors.New("Address family not supported")
|
||||
ErrProtocolNotSupported = errors.New("Socket protocol/type not supported")
|
||||
ErrStartingDHCPClient = errors.New("Error starting DHPC client")
|
||||
ErrStartingDHCPClient = errors.New("Error starting DHCP client")
|
||||
ErrStartingDHCPServer = errors.New("Error starting DHCP server")
|
||||
ErrNoMoreSockets = errors.New("No more sockets")
|
||||
ErrClosingSocket = errors.New("Error closing socket")
|
||||
ErrNotSupported = errors.New("Not supported")
|
||||
ErrInvalidSocketFd = errors.New("Invalid socket fd")
|
||||
)
|
||||
|
||||
// Duplicate of non-exported net.errTimeout
|
||||
@@ -82,7 +84,7 @@ type Netdever interface {
|
||||
Bind(sockfd int, ip netip.AddrPort) error
|
||||
Connect(sockfd int, host string, ip netip.AddrPort) error
|
||||
Listen(sockfd int, backlog int) error
|
||||
Accept(sockfd int, ip netip.AddrPort) (int, error)
|
||||
Accept(sockfd int) (int, netip.AddrPort, error)
|
||||
Send(sockfd int, buf []byte, flags int, deadline time.Time) (int, error)
|
||||
Recv(sockfd int, buf []byte, flags int, deadline time.Time) (int, error)
|
||||
Close(sockfd int) error
|
||||
|
||||
@@ -13,6 +13,7 @@ var (
|
||||
ErrConnectFailed = errors.New("Connect failed")
|
||||
ErrConnectTimeout = errors.New("Connect timed out")
|
||||
ErrMissingSSID = errors.New("Missing WiFi SSID")
|
||||
ErrShortPassphrase = errors.New("Invalid Wifi Passphrase < 8 chars")
|
||||
ErrAuthFailure = errors.New("Wifi authentication failure")
|
||||
ErrAuthTypeNoGood = errors.New("Wifi authorization type not supported")
|
||||
ErrConnectModeNoGood = errors.New("Connect mode not supported")
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
//go:build comboat_fw
|
||||
|
||||
package probe
|
||||
|
||||
import (
|
||||
"machine"
|
||||
|
||||
"tinygo.org/x/drivers/comboat"
|
||||
"tinygo.org/x/drivers/netdev"
|
||||
"tinygo.org/x/drivers/netlink"
|
||||
)
|
||||
|
||||
func Probe() (netlink.Netlinker, netdev.Netdever) {
|
||||
|
||||
cfg := comboat.Config{
|
||||
BaudRate: 115200,
|
||||
Uart: machine.UART1,
|
||||
Tx: machine.UART1_TX_PIN,
|
||||
Rx: machine.UART1_RX_PIN,
|
||||
}
|
||||
|
||||
combo := comboat.NewDevice(&cfg)
|
||||
netdev.UseNetdev(combo)
|
||||
|
||||
return combo, combo
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
//go:build pyportal || arduino_nano33 || nano_rp2040 || metro_m4_airlift || matrixportal_m4
|
||||
//go:build ninafw && !arduino_mkrwifi1010
|
||||
|
||||
package probe
|
||||
|
||||
|
||||
+17
-9
@@ -27,8 +27,9 @@ type Config struct{}
|
||||
|
||||
// Errors list
|
||||
var (
|
||||
errNoPresence = errors.New("Error: OneWire. No devices on the bus.")
|
||||
errReadAddress = errors.New("Error: OneWire. Read address error: CRC mismatch.")
|
||||
errNoPresence = errors.New("Error: OneWire. No devices on the bus.")
|
||||
errTooManyDevices = errors.New("Error: OneWire. Too many devices on the bus.")
|
||||
errReadAddress = errors.New("Error: OneWire. Read address error: CRC mismatch.")
|
||||
)
|
||||
|
||||
// New creates a new GPIO 1-Wire connection.
|
||||
@@ -46,7 +47,7 @@ func (d *Device) Configure(config Config) {}
|
||||
func (d Device) Reset() error {
|
||||
d.p.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
time.Sleep(480 * time.Microsecond)
|
||||
d.p.Configure(machine.PinConfig{Mode: machine.PinInput})
|
||||
d.p.Configure(machine.PinConfig{Mode: machine.PinInputPullup})
|
||||
time.Sleep(70 * time.Microsecond)
|
||||
precence := d.p.Get()
|
||||
time.Sleep(410 * time.Microsecond)
|
||||
@@ -61,11 +62,11 @@ func (d Device) WriteBit(data uint8) {
|
||||
d.p.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
if data&1 == 1 { // Send '1'
|
||||
time.Sleep(5 * time.Microsecond)
|
||||
d.p.Configure(machine.PinConfig{Mode: machine.PinInput})
|
||||
d.p.Configure(machine.PinConfig{Mode: machine.PinInputPullup})
|
||||
time.Sleep(60 * time.Microsecond)
|
||||
} else { // Send '0'
|
||||
time.Sleep(60 * time.Microsecond)
|
||||
d.p.Configure(machine.PinConfig{Mode: machine.PinInput})
|
||||
d.p.Configure(machine.PinConfig{Mode: machine.PinInputPullup})
|
||||
time.Sleep(5 * time.Microsecond)
|
||||
}
|
||||
}
|
||||
@@ -82,7 +83,7 @@ func (d Device) Write(data uint8) {
|
||||
func (d Device) ReadBit() (data uint8) {
|
||||
d.p.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
time.Sleep(3 * time.Microsecond)
|
||||
d.p.Configure(machine.PinConfig{Mode: machine.PinInput})
|
||||
d.p.Configure(machine.PinConfig{Mode: machine.PinInputPullup})
|
||||
time.Sleep(8 * time.Microsecond)
|
||||
if d.p.Get() {
|
||||
data = 1
|
||||
@@ -111,7 +112,7 @@ func (d Device) ReadAddress() ([]uint8, error) {
|
||||
for i := 0; i < 8; i++ {
|
||||
romid[i] = d.Read()
|
||||
}
|
||||
if d.Сrc8(romid, 7) != romid[7] {
|
||||
if d.Сrc8(romid) != 0 {
|
||||
return nil, errReadAddress
|
||||
}
|
||||
return romid, nil
|
||||
@@ -187,15 +188,22 @@ func (d Device) Search(cmd uint8) ([][]uint8, error) {
|
||||
}
|
||||
d.WriteBit(bit)
|
||||
}
|
||||
if d.Сrc8(lastAddress) != 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
lastFork = lastZero
|
||||
copy(romIDs[romIndex], lastAddress)
|
||||
romIndex++
|
||||
if romIndex >= 32 {
|
||||
return romIDs, errTooManyDevices
|
||||
}
|
||||
}
|
||||
return romIDs[:romIndex:romIndex], nil
|
||||
}
|
||||
|
||||
// Crc8 compute a Dallas Semiconductor 8 bit CRC.
|
||||
func (d Device) Сrc8(buffer []uint8, size int) (crc uint8) {
|
||||
func (_ Device) Сrc8(buffer []uint8) (crc uint8) {
|
||||
// Dow-CRC using polynomial X^8 + X^5 + X^4 + X^0
|
||||
// Tiny 2x16 entry CRC table created by Arjen Lentz
|
||||
// See http://lentz.com.au/blog/calculating-crc-with-a-tiny-32-entry-lookup-table
|
||||
@@ -205,7 +213,7 @@ func (d Device) Сrc8(buffer []uint8, size int) (crc uint8) {
|
||||
0x00, 0x9D, 0x23, 0xBE, 0x46, 0xDB, 0x65, 0xF8,
|
||||
0x8C, 0x11, 0xAF, 0x32, 0xCA, 0x57, 0xE9, 0x74,
|
||||
}
|
||||
for i := 0; i < size; i++ {
|
||||
for i := 0; i < len(buffer); i++ {
|
||||
crc = buffer[i] ^ crc // just re-using crc as intermediate
|
||||
crc = crc8_table[crc&0x0f] ^ crc8_table[16+((crc>>4)&0x0f)]
|
||||
}
|
||||
|
||||
+2
-1
@@ -14,7 +14,8 @@ import (
|
||||
)
|
||||
|
||||
type P1AM struct {
|
||||
bus machine.SPI
|
||||
bus *machine.SPI
|
||||
|
||||
slaveSelectPin, slaveAckPin, baseEnablePin machine.Pin
|
||||
|
||||
// SkipAutoConfig will skip loading a default configuration into each module.
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
// Package pcf8591 implements a driver for the PCF8591 Analog to Digital/Digital to Analog Converter.
|
||||
//
|
||||
// Datasheet: https://www.nxp.com/docs/en/data-sheet/PCF8591.pdf
|
||||
package pcf8591 // import "tinygo.org/x/drivers/pcf8591"
|
||||
|
||||
import (
|
||||
"machine"
|
||||
|
||||
"errors"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
)
|
||||
|
||||
// Device wraps PCF8591 ADC functions.
|
||||
type Device struct {
|
||||
bus drivers.I2C
|
||||
Address uint16
|
||||
CH0 ADCPin
|
||||
CH1 ADCPin
|
||||
CH2 ADCPin
|
||||
CH3 ADCPin
|
||||
}
|
||||
|
||||
// ADCPin is the implementation of the ADConverter interface.
|
||||
type ADCPin struct {
|
||||
machine.Pin
|
||||
d *Device
|
||||
}
|
||||
|
||||
// New returns a new PCF8591 driver. Pass in a fully configured I2C bus.
|
||||
func New(b drivers.I2C) *Device {
|
||||
d := &Device{
|
||||
bus: b,
|
||||
Address: defaultAddress,
|
||||
}
|
||||
|
||||
// setup all channels
|
||||
d.CH0 = d.GetADC(0)
|
||||
d.CH1 = d.GetADC(1)
|
||||
d.CH2 = d.GetADC(2)
|
||||
d.CH3 = d.GetADC(3)
|
||||
|
||||
return d
|
||||
}
|
||||
|
||||
// Configure here just for interface compatibility.
|
||||
func (d *Device) Configure() {
|
||||
}
|
||||
|
||||
// Read analog data from channel
|
||||
func (d *Device) Read(ch int) (uint16, error) {
|
||||
if ch < 0 || ch > 3 {
|
||||
return 0, errors.New("invalid channel for pcf8591 Read")
|
||||
}
|
||||
|
||||
return d.GetADC(ch).Get(), nil
|
||||
}
|
||||
|
||||
// GetADC returns an ADC for a specific channel.
|
||||
func (d *Device) GetADC(ch int) ADCPin {
|
||||
return ADCPin{machine.Pin(ch), d}
|
||||
}
|
||||
|
||||
// Get the current reading for a specific ADCPin.
|
||||
func (p ADCPin) Get() uint16 {
|
||||
// TODO: also implement DAC
|
||||
tx := make([]byte, 2)
|
||||
tx[0] = byte(p.Pin)
|
||||
|
||||
rx := make([]byte, 2)
|
||||
|
||||
// The result from the measurement triggered by the first write,
|
||||
// however, the second write is required to get the result.
|
||||
// See section 8.4 "A/D Conversion" in the datasheet for more info
|
||||
p.d.bus.Tx(p.d.Address, tx, rx)
|
||||
p.d.bus.Tx(p.d.Address, tx, rx)
|
||||
|
||||
// scale result to 16bit value like other ADCs
|
||||
return uint16(rx[1]) << 8
|
||||
}
|
||||
|
||||
// Configure here just for interface compatibility.
|
||||
func (p ADCPin) Configure() {
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package pcf8591
|
||||
|
||||
// PCF8591 Default Address
|
||||
const defaultAddress = 0x48
|
||||
|
||||
// control bit for DAC
|
||||
const PCF8591_ENABLE_DAC = 0x40
|
||||
+85
-56
@@ -22,12 +22,13 @@ func NewImage[T Color](width, height int) Image[T] {
|
||||
}
|
||||
var zeroColor T
|
||||
var data unsafe.Pointer
|
||||
if zeroColor.BitsPerPixel()%8 == 0 {
|
||||
switch {
|
||||
case zeroColor.BitsPerPixel()%8 == 0:
|
||||
// Typical formats like RGB888 and RGB565.
|
||||
// Each color starts at a whole byte offset from the start.
|
||||
buf := make([]T, width*height)
|
||||
data = unsafe.Pointer(&buf[0])
|
||||
} else {
|
||||
default:
|
||||
// Formats like RGB444 that have 12 bits per pixel.
|
||||
// We access these as bytes, so allocate the buffer as a byte slice.
|
||||
bufBits := width * height * zeroColor.BitsPerPixel()
|
||||
@@ -42,6 +43,40 @@ func NewImage[T Color](width, height int) Image[T] {
|
||||
}
|
||||
}
|
||||
|
||||
// NewImageFromBytes creates a new image of the given size using an existing data slice of bytes.
|
||||
func NewImageFromBytes[T Color](width, height int, buf []byte) Image[T] {
|
||||
if width < 0 || height < 0 || int(int16(width)) != width || int(int16(height)) != height {
|
||||
// The width/height are stored as 16-bit integers and should never be
|
||||
// negative.
|
||||
panic("NewImageFromBytes: width/height out of bounds")
|
||||
}
|
||||
var zeroColor T
|
||||
var data unsafe.Pointer
|
||||
switch {
|
||||
case zeroColor.BitsPerPixel()%8 == 0:
|
||||
// Typical formats like RGB888 and RGB565.
|
||||
// Each color starts at a whole byte offset from the start.
|
||||
if len(buf) != width*height*int(unsafe.Sizeof(zeroColor)) {
|
||||
panic("NewImageFromBytes: data slice size mismatch")
|
||||
}
|
||||
data = unsafe.Pointer(&buf[0])
|
||||
default:
|
||||
// Formats like RGB444 that have 12 bits per pixel.
|
||||
// We access these as bytes, so allocate the buffer as a byte slice.
|
||||
bufBits := width * height * zeroColor.BitsPerPixel()
|
||||
bufBytes := (bufBits + 7) / 8
|
||||
if len(buf) != bufBytes {
|
||||
panic("NewImageFromBytes: data slice size mismatch")
|
||||
}
|
||||
data = unsafe.Pointer(&buf[0])
|
||||
}
|
||||
return Image[T]{
|
||||
width: int16(width),
|
||||
height: int16(height),
|
||||
data: data,
|
||||
}
|
||||
}
|
||||
|
||||
// Rescale returns a new Image buffer based on the img buffer.
|
||||
// The contents is undefined after the Rescale operation, and any modification
|
||||
// to the returned image will overwrite the underlying image buffer in undefined
|
||||
@@ -70,54 +105,6 @@ func (img Image[T]) LimitHeight(height int) Image[T] {
|
||||
}
|
||||
}
|
||||
|
||||
// Split the buffer into two buffers that can be used independently.
|
||||
// The top half is split just like LimitHeight. The bottom half is made out of
|
||||
// the remaining buffer area and can be zero. The topHeight parameter must not
|
||||
// be larger than the height of the buffer.
|
||||
//
|
||||
// Always check the height of the bottom half: it may be zero due to alignment
|
||||
// issues.
|
||||
func (img Image[T]) Split(topHeight int) (top, bottom Image[T]) {
|
||||
if topHeight < 0 || topHeight > int(img.height) {
|
||||
panic("Image.Split: out of bounds")
|
||||
}
|
||||
|
||||
// The top half of the buffer, the same as LimitHeight.
|
||||
top = Image[T]{
|
||||
width: img.width,
|
||||
height: int16(topHeight),
|
||||
data: img.data,
|
||||
}
|
||||
|
||||
// Calculate the bottom half of the buffer.
|
||||
// This is a bit more complicated since it's possible that the bottom half
|
||||
// can't have all the other bytes: the top half pixels might cross a byte
|
||||
// boundary (for example with RGB444). So instead we calculate the size of
|
||||
// the buffer we have, the size of the buffer that the top half will use
|
||||
// (which is rounded up to a byte boundary), and then calculate the
|
||||
// remaining bytes at the bottom.
|
||||
// In practice, I expect it's unlikely that the top half will cross a byte
|
||||
// boundary since a typical split buffer will have a width that's a nice
|
||||
// round number, but it's possible so we have to avoid this edge case.
|
||||
var zeroColor T
|
||||
dataBytes := (int(img.width)*int(img.height)*zeroColor.BitsPerPixel() + 7) / 8
|
||||
topDataBytes := (int(img.width)*int(topHeight)*zeroColor.BitsPerPixel() + 7) / 8
|
||||
bottomDataBytes := dataBytes - topDataBytes
|
||||
if bottomDataBytes < 0 {
|
||||
// No buffer remaining (not sure whether this is possible in practice
|
||||
// but guarding just in case).
|
||||
bottomDataBytes = 0
|
||||
}
|
||||
bottomHeight := (bottomDataBytes * 8 / zeroColor.BitsPerPixel()) / int(img.width)
|
||||
bottom = Image[T]{
|
||||
width: img.width,
|
||||
height: int16(bottomHeight),
|
||||
data: unsafe.Add(img.data, topDataBytes),
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Len returns the number of pixels in this image buffer.
|
||||
func (img Image[T]) Len() int {
|
||||
return int(img.width) * int(img.height)
|
||||
@@ -128,10 +115,11 @@ func (img Image[T]) Len() int {
|
||||
func (img Image[T]) RawBuffer() []uint8 {
|
||||
var zeroColor T
|
||||
var numBytes int
|
||||
if zeroColor.BitsPerPixel()%8 == 0 {
|
||||
switch {
|
||||
case zeroColor.BitsPerPixel()%8 == 0:
|
||||
// Each color starts at a whole byte offset.
|
||||
numBytes = int(unsafe.Sizeof(zeroColor)) * int(img.width) * int(img.height)
|
||||
} else {
|
||||
default:
|
||||
// Formats like RGB444 that aren't a whole number of bytes.
|
||||
numBits := zeroColor.BitsPerPixel() * int(img.width) * int(img.height)
|
||||
numBytes = (numBits + 7) / 8 // round up (see NewImage)
|
||||
@@ -147,7 +135,21 @@ func (img Image[T]) Size() (int, int) {
|
||||
func (img Image[T]) setPixel(index int, c T) {
|
||||
var zeroColor T
|
||||
|
||||
if zeroColor.BitsPerPixel()%8 == 0 {
|
||||
switch {
|
||||
case zeroColor.BitsPerPixel() == 1:
|
||||
// Monochrome.
|
||||
offset := index / 8
|
||||
bits := index % 8
|
||||
|
||||
ptr := (*byte)(unsafe.Add(img.data, offset))
|
||||
if c != zeroColor {
|
||||
*((*byte)(ptr)) |= (1 << (7 - uint8(bits)))
|
||||
} else {
|
||||
*((*byte)(ptr)) &^= (1 << (7 - uint8(bits)))
|
||||
}
|
||||
|
||||
return
|
||||
case zeroColor.BitsPerPixel()%8 == 0:
|
||||
// Each color starts at a whole byte offset.
|
||||
// This is the easy case.
|
||||
offset := index * int(unsafe.Sizeof(zeroColor))
|
||||
@@ -195,7 +197,16 @@ func (img Image[T]) Get(x, y int) T {
|
||||
var zeroColor T
|
||||
index := y*int(img.width) + x // index into img.data
|
||||
|
||||
if zeroColor.BitsPerPixel()%8 == 0 {
|
||||
switch {
|
||||
case zeroColor.BitsPerPixel() == 1:
|
||||
// Monochrome.
|
||||
var c Monochrome
|
||||
offset := index / 8
|
||||
bits := index % 8
|
||||
ptr := (*byte)(unsafe.Add(img.data, offset))
|
||||
c = ((*ptr >> (7 - uint8(bits))) & 0x1) > 0
|
||||
return any(c).(T)
|
||||
case zeroColor.BitsPerPixel()%8 == 0:
|
||||
// Colors like RGB565, RGB888, etc.
|
||||
offset := index * int(unsafe.Sizeof(zeroColor))
|
||||
ptr := unsafe.Add(img.data, offset)
|
||||
@@ -229,8 +240,26 @@ func (img Image[T]) Get(x, y int) T {
|
||||
func (img Image[T]) FillSolidColor(color T) {
|
||||
var zeroColor T
|
||||
|
||||
// Fast pass for colors of 8, 16, 24, etc bytes in size.
|
||||
if zeroColor.BitsPerPixel()%8 == 0 {
|
||||
switch {
|
||||
case zeroColor.BitsPerPixel() == 1:
|
||||
// Monochrome.
|
||||
var colorByte uint8
|
||||
if color != zeroColor {
|
||||
colorByte = 0xff
|
||||
}
|
||||
numBytes := int(img.width) * int(img.height) / 8
|
||||
for i := 0; i < numBytes; i++ {
|
||||
// TODO: this can be optimized a lot.
|
||||
// - The store can be done as a 32-bit integer, after checking for
|
||||
// alignment.
|
||||
// - Perhaps the loop can be unrolled to improve copy performance.
|
||||
ptr := (*byte)(unsafe.Add(img.data, i))
|
||||
*((*byte)(ptr)) = colorByte
|
||||
}
|
||||
return
|
||||
|
||||
case zeroColor.BitsPerPixel()%8 == 0:
|
||||
// Fast pass for colors of 8, 16, 24, etc bytes in size.
|
||||
ptr := img.data
|
||||
for i := 0; i < img.Len(); i++ {
|
||||
// TODO: this can be optimized a lot.
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package pixel_test
|
||||
|
||||
import (
|
||||
goimage "image"
|
||||
"image/color"
|
||||
"math/rand"
|
||||
"testing"
|
||||
|
||||
"tinygo.org/x/drivers/pixel"
|
||||
@@ -62,3 +64,191 @@ func TestImageRGB444BE(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestImageMonochrome(t *testing.T) {
|
||||
image := pixel.NewImage[pixel.Monochrome](128, 64)
|
||||
if width, height := image.Size(); width != 128 && height != 64 {
|
||||
t.Errorf("image.Size(): expected 128, 64 but got %d, %d", width, height)
|
||||
}
|
||||
for _, expected := range []color.RGBA{
|
||||
{R: 0xff, G: 0xff, B: 0xff},
|
||||
{G: 0xff},
|
||||
{R: 0xff, G: 0xff},
|
||||
{G: 0xff, B: 0xff},
|
||||
{R: 0x00},
|
||||
{G: 0x00, A: 0xff},
|
||||
{B: 0x00, A: 0xff},
|
||||
} {
|
||||
encoded := pixel.NewColor[pixel.Monochrome](expected.R, expected.G, expected.B)
|
||||
image.Set(5, 3, encoded)
|
||||
actual := image.Get(5, 3).RGBA()
|
||||
switch {
|
||||
case int(expected.R)+int(expected.G)+int(expected.B) > 128*3:
|
||||
// should be true eg white
|
||||
if actual.R == 0 || actual.G == 0 || actual.B == 0 {
|
||||
t.Errorf("failed to roundtrip color: expected %v but got %v", expected, actual)
|
||||
}
|
||||
default:
|
||||
// should be false eg black
|
||||
if actual.R != 0 || actual.G != 0 || actual.B != 0 {
|
||||
t.Errorf("failed to roundtrip color: expected %v but got %v", expected, actual)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 128x128
|
||||
var rprofile = []byte{
|
||||
0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
|
||||
0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00,
|
||||
0x00, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x00,
|
||||
0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE8, 0x17, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0,
|
||||
0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE,
|
||||
0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0xFE,
|
||||
0x3F, 0xFF, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xFE, 0x3F, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xFC,
|
||||
0x3F, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xFE, 0x3F, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xFE,
|
||||
0x3F, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0xFE, 0x3F, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xFC,
|
||||
0x3F, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0xFE, 0x3F, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0xBF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xFE,
|
||||
0x3F, 0xFF, 0xFC, 0x01, 0xF8, 0x00, 0x5F, 0xFF, 0xFF, 0xFC, 0x00, 0x02, 0x80, 0x1F, 0xFF, 0xFE, 0x3F, 0xFF, 0xFC, 0x03, 0xFE, 0x03, 0xFF, 0xFD, 0xBF, 0xFF, 0x80, 0x1F, 0xE0, 0x3F, 0xFF, 0xFC,
|
||||
0x3F, 0xFF, 0xFC, 0x03, 0xFE, 0x01, 0xFF, 0xF7, 0x6B, 0xFF, 0x80, 0x1F, 0xC0, 0x1F, 0xFF, 0xFE, 0x02, 0xFF, 0xFC, 0x03, 0xDF, 0x17, 0xFA, 0x00, 0x00, 0x37, 0xF0, 0x3F, 0xE0, 0x1F, 0xFF, 0xD0,
|
||||
0x00, 0x07, 0xFC, 0x07, 0x07, 0xBF, 0x00, 0x00, 0x00, 0x01, 0xFE, 0xF8, 0x78, 0x3F, 0xF8, 0x00, 0x00, 0x01, 0xFC, 0x06, 0x1B, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xEA, 0x78, 0x1F, 0x80, 0x00,
|
||||
0x00, 0x01, 0xFC, 0x03, 0x1B, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xE2, 0x68, 0x1F, 0xC0, 0x00, 0x00, 0x01, 0xFC, 0x07, 0x5B, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x07, 0xC4, 0x38, 0x1F, 0x80, 0x00,
|
||||
0x00, 0x01, 0xF8, 0x03, 0x3F, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF7, 0x78, 0x3F, 0xC0, 0x00, 0x00, 0x01, 0xFC, 0x03, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0x68, 0x1F, 0x80, 0x00,
|
||||
0x00, 0x01, 0xFC, 0x07, 0x9F, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x70, 0x1F, 0x80, 0x00, 0x00, 0x01, 0xFC, 0x03, 0x9E, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x3E, 0xE8, 0x1F, 0xC0, 0x00,
|
||||
0x00, 0x01, 0xFC, 0x01, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xE0, 0x1F, 0x80, 0x00, 0x00, 0x01, 0xF8, 0x01, 0xFF, 0x55, 0xF8, 0x00, 0x00, 0x03, 0xEA, 0xFF, 0xC0, 0x1F, 0xC0, 0x00,
|
||||
0x00, 0x01, 0xFC, 0x01, 0xFE, 0xAF, 0xF0, 0x00, 0x00, 0x03, 0xFD, 0xBF, 0xE0, 0x1F, 0x80, 0x00, 0x00, 0x01, 0xFC, 0x01, 0xF8, 0x00, 0x7C, 0x00, 0x00, 0x07, 0x80, 0x03, 0xE0, 0x1F, 0xC0, 0x00,
|
||||
0x00, 0x01, 0xFC, 0x03, 0xC1, 0xE0, 0x3E, 0x00, 0x00, 0x1F, 0x03, 0xC0, 0xF0, 0x1F, 0x80, 0x00, 0x00, 0x00, 0xF8, 0x07, 0x82, 0xF8, 0x0F, 0x00, 0x00, 0x3E, 0x05, 0xE0, 0x7C, 0x1F, 0x80, 0x00,
|
||||
0x00, 0x01, 0xFC, 0x07, 0x01, 0xF8, 0x17, 0x00, 0x00, 0x3C, 0x09, 0xE0, 0x78, 0x1F, 0xC0, 0x00, 0x00, 0x03, 0xFC, 0x0F, 0x06, 0xFC, 0x07, 0x80, 0x00, 0x7C, 0x1D, 0xE0, 0x3C, 0x1F, 0xC0, 0x00,
|
||||
0x00, 0xFF, 0xFC, 0x1E, 0x03, 0xFC, 0x03, 0x80, 0x00, 0x78, 0x1F, 0xE0, 0x1E, 0x1F, 0xFE, 0x80, 0x2F, 0xFF, 0xFC, 0x1C, 0x07, 0xF8, 0x01, 0x80, 0x00, 0x60, 0x1F, 0xE0, 0x16, 0x1F, 0xFF, 0xF8,
|
||||
0x1F, 0xFF, 0xF8, 0x3E, 0x07, 0xFC, 0x01, 0xC0, 0x00, 0xE8, 0x1F, 0xE0, 0x1E, 0x1F, 0xFF, 0xEC, 0x3F, 0xFF, 0xFC, 0x3C, 0x03, 0xF8, 0x01, 0xC0, 0x00, 0xE0, 0x17, 0xE0, 0x07, 0x1F, 0xFF, 0xFE,
|
||||
0x3F, 0xFF, 0xFC, 0x38, 0x03, 0xE8, 0x00, 0xC0, 0x00, 0xC0, 0x07, 0xC0, 0x03, 0x1F, 0xFF, 0xFC, 0x3F, 0xFF, 0xFC, 0x38, 0x00, 0xC0, 0x00, 0xE0, 0x00, 0xC0, 0x00, 0x00, 0x03, 0x9F, 0xFF, 0xFE,
|
||||
0x3F, 0xFF, 0xFC, 0x78, 0x01, 0xE0, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0x00, 0x03, 0x1F, 0xFF, 0xFC, 0x3F, 0xFF, 0xFC, 0x38, 0x00, 0x00, 0x00, 0xE0, 0x01, 0xC0, 0x00, 0x00, 0x03, 0x9F, 0xFF, 0xFE,
|
||||
0x3F, 0xFF, 0xF8, 0x78, 0x00, 0x00, 0x00, 0xE0, 0x01, 0xC0, 0x00, 0x00, 0x03, 0x9F, 0xFF, 0xFE, 0x3F, 0xFF, 0xFC, 0x68, 0x00, 0x00, 0x00, 0xE0, 0x01, 0xC0, 0x00, 0x00, 0x03, 0x9F, 0xFF, 0xFC,
|
||||
0x3F, 0xFF, 0xFC, 0x70, 0x00, 0x00, 0x00, 0xE0, 0x01, 0xC0, 0x00, 0x00, 0x01, 0x9F, 0xFF, 0xFE, 0x3F, 0xFF, 0xFC, 0x78, 0x00, 0x00, 0x00, 0xE0, 0x01, 0xC0, 0x00, 0x00, 0x03, 0x9F, 0xFF, 0xFE,
|
||||
0x3F, 0xFF, 0xFC, 0x68, 0x00, 0x00, 0x00, 0xE0, 0x01, 0xC0, 0x00, 0x00, 0x03, 0x9F, 0xFF, 0xFE, 0x3F, 0xFF, 0xF8, 0x70, 0x00, 0x00, 0x00, 0xC0, 0x01, 0xC0, 0x00, 0x00, 0x01, 0x9F, 0xFF, 0xFC,
|
||||
0x3F, 0xFF, 0xFC, 0x68, 0x00, 0x00, 0x00, 0xE0, 0x01, 0xC0, 0x00, 0x00, 0x03, 0x9F, 0xFF, 0xFE, 0x3F, 0xFF, 0xFC, 0x38, 0x00, 0x00, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0x00, 0x03, 0x9F, 0xFF, 0xFE,
|
||||
0x07, 0xFF, 0xFC, 0x78, 0x00, 0x00, 0x00, 0xC0, 0x80, 0xC0, 0x00, 0x00, 0x03, 0x1F, 0xFF, 0xF8, 0x00, 0x37, 0xF8, 0x38, 0x00, 0x00, 0x01, 0xC7, 0xF0, 0xE0, 0x00, 0x00, 0x03, 0x9F, 0xFE, 0x00,
|
||||
0x00, 0x5F, 0xFC, 0x38, 0x00, 0x00, 0x01, 0xC3, 0xE8, 0xE0, 0x00, 0x00, 0x03, 0x1F, 0xFB, 0x00, 0x00, 0x01, 0xFC, 0x3C, 0x00, 0x00, 0x01, 0xC7, 0xF8, 0xE0, 0x00, 0x00, 0x07, 0x1F, 0xC0, 0x00,
|
||||
0x00, 0x01, 0xFC, 0x3C, 0x00, 0x00, 0x03, 0x9F, 0xFC, 0x70, 0x00, 0x00, 0x07, 0x1F, 0x80, 0x00, 0x00, 0x01, 0xF8, 0x1E, 0x00, 0x00, 0x03, 0xBF, 0xFE, 0x78, 0x00, 0x00, 0x1E, 0x1F, 0xC0, 0x00,
|
||||
0x00, 0x01, 0xFC, 0x1E, 0x00, 0x00, 0x07, 0x9F, 0xFF, 0x78, 0x00, 0x00, 0x16, 0x1F, 0x80, 0x00, 0x00, 0x01, 0xFC, 0x1E, 0x00, 0x00, 0x07, 0x73, 0xC3, 0x3C, 0x00, 0x00, 0x1E, 0x1F, 0xC0, 0x00,
|
||||
0x00, 0x01, 0xFC, 0x1F, 0x00, 0x00, 0x1E, 0x60, 0x01, 0x9E, 0x00, 0x00, 0x3C, 0x1F, 0x80, 0x00, 0x00, 0x01, 0xF8, 0x1F, 0x80, 0x00, 0x7E, 0x40, 0x01, 0x17, 0x80, 0x00, 0x7E, 0x1F, 0xC0, 0x00,
|
||||
0x00, 0x01, 0xFC, 0x1F, 0x80, 0x00, 0x3C, 0x40, 0x01, 0x9F, 0x00, 0x00, 0x7C, 0x1F, 0x80, 0x00, 0x00, 0x01, 0xFC, 0x1F, 0xC0, 0x00, 0xFC, 0x40, 0x01, 0x07, 0xC0, 0x00, 0xFC, 0x1F, 0xC0, 0x00,
|
||||
0x00, 0x01, 0xFC, 0x1F, 0xF8, 0x0B, 0xE8, 0x7B, 0xFF, 0x81, 0xF8, 0x03, 0xFC, 0x1F, 0x80, 0x00, 0x00, 0x03, 0xF8, 0x1C, 0xFF, 0xFF, 0xC0, 0x3F, 0xFE, 0x00, 0xFF, 0xFF, 0xDE, 0x1F, 0xC0, 0x00,
|
||||
0x00, 0x05, 0xFC, 0x1C, 0xFF, 0xFF, 0x80, 0x7F, 0xDE, 0x00, 0xFF, 0x7F, 0xDC, 0x1F, 0x80, 0x00, 0x00, 0xFF, 0xFC, 0x1C, 0x3F, 0xFE, 0x00, 0x0E, 0xB8, 0x00, 0x3F, 0xFF, 0x1C, 0x1F, 0xFC, 0x00,
|
||||
0x2F, 0xFF, 0xF8, 0x1C, 0x00, 0x00, 0x00, 0x04, 0x98, 0x00, 0x01, 0x00, 0x1E, 0x1F, 0xFF, 0xF4, 0x3F, 0xFF, 0xFC, 0x3C, 0x00, 0x00, 0x00, 0x06, 0x98, 0x00, 0x00, 0x00, 0x1C, 0x1F, 0xFF, 0xFE,
|
||||
0x3F, 0xFF, 0xFC, 0x1C, 0x00, 0x00, 0x00, 0x06, 0x98, 0x00, 0x00, 0x00, 0x1E, 0x1F, 0xFF, 0xFE, 0x7F, 0xFF, 0xF8, 0x3C, 0x00, 0x00, 0x00, 0x02, 0xB8, 0x00, 0x00, 0x00, 0x1C, 0x1F, 0xFF, 0xFE,
|
||||
0x3F, 0xFF, 0xFC, 0x1C, 0x00, 0x00, 0x00, 0x03, 0xE8, 0x00, 0x00, 0x00, 0x1C, 0x1F, 0xFF, 0xFE, 0x7F, 0xFF, 0xFC, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x1F, 0xFF, 0xFE,
|
||||
0x3F, 0xFF, 0xFC, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x1C, 0x1F, 0xFF, 0xFE, 0x7F, 0xFF, 0xF8, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x1F, 0xFF, 0xFE,
|
||||
0x3F, 0xFF, 0xFC, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x1F, 0xFF, 0xFE, 0x7F, 0xFF, 0xFC, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x1F, 0xFF, 0xFE,
|
||||
0x3F, 0xFF, 0xFC, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x1F, 0xFF, 0xFE, 0x7F, 0xFF, 0xF8, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x1F, 0xFF, 0xFE,
|
||||
0x7F, 0xFF, 0xFC, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x1F, 0xFF, 0xFE, 0x3F, 0xFF, 0xFC, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x1F, 0xFF, 0xFE,
|
||||
0x7F, 0xFF, 0xFC, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x1F, 0xFF, 0xFE, 0x0B, 0xFF, 0xF8, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x1F, 0xFF, 0xF8,
|
||||
0x00, 0x7F, 0xFC, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x1F, 0xFE, 0x00, 0x00, 0x01, 0xFC, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x1F, 0xC0, 0x00,
|
||||
0x00, 0x03, 0xFC, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x1F, 0x80, 0x00, 0x00, 0x01, 0xF8, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x1F, 0xC0, 0x00,
|
||||
0x00, 0x01, 0xFC, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x1F, 0x80, 0x00, 0x00, 0x01, 0xFC, 0x38, 0x15, 0xB7, 0x80, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x1C, 0x1F, 0xC0, 0x00,
|
||||
0x00, 0x01, 0xF8, 0x3C, 0x16, 0xAB, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x16, 0x1F, 0x80, 0x00, 0x00, 0x01, 0xFC, 0x3C, 0x3F, 0xFB, 0x80, 0x00, 0x00, 0xFF, 0x80, 0x00, 0x1C, 0x1F, 0xC0, 0x00,
|
||||
0x00, 0x01, 0xFC, 0x3C, 0x1F, 0xE9, 0x00, 0x00, 0x01, 0xF7, 0x80, 0x00, 0x1E, 0x1F, 0x80, 0x00, 0x00, 0x01, 0xF8, 0x3C, 0x03, 0x82, 0x1D, 0xC6, 0x39, 0xC0, 0x07, 0x00, 0x14, 0x1F, 0xC0, 0x00,
|
||||
0x00, 0x01, 0xFC, 0x3C, 0x03, 0x87, 0x14, 0x85, 0x15, 0xC1, 0x03, 0x80, 0x1E, 0x1F, 0x80, 0x00, 0x00, 0x01, 0xFC, 0x3C, 0x03, 0x87, 0x9F, 0xE6, 0x3D, 0xC0, 0x1F, 0xC0, 0x1C, 0x1F, 0xC0, 0x00,
|
||||
0x00, 0xBF, 0xF8, 0x3C, 0x03, 0x83, 0x9F, 0xE7, 0x3F, 0x8D, 0x1E, 0xC0, 0x16, 0x1F, 0xD4, 0x00, 0x17, 0xFF, 0xFC, 0x3C, 0x03, 0x83, 0x9E, 0xE7, 0x79, 0xD7, 0xBC, 0xE0, 0x1C, 0x1F, 0xFF, 0xE8,
|
||||
0x1F, 0xFF, 0xFC, 0x3C, 0x03, 0x83, 0x9C, 0xE3, 0x3F, 0x9F, 0xBC, 0xE0, 0x1E, 0x1F, 0xFF, 0xD0, 0x3F, 0xFF, 0xF8, 0x3C, 0x03, 0x83, 0x9E, 0xE7, 0x79, 0xC7, 0xBC, 0xE0, 0x16, 0x1F, 0xFF, 0xFE,
|
||||
0x3F, 0xFF, 0xFC, 0x3C, 0x03, 0x83, 0xBC, 0xE3, 0xF9, 0xC3, 0xBC, 0xE0, 0x1C, 0x1F, 0xFF, 0xFC, 0x3F, 0xFF, 0xFC, 0x3C, 0x03, 0x83, 0x9E, 0xEB, 0xE9, 0xE3, 0xBD, 0xE0, 0x1E, 0x1F, 0xFF, 0xFE,
|
||||
0x3F, 0xFF, 0xF8, 0x3C, 0x03, 0x83, 0x9C, 0xE3, 0xF1, 0xC3, 0x9C, 0xC0, 0x1C, 0x1F, 0xFF, 0xFC, 0x3F, 0xFF, 0xFC, 0x3C, 0x03, 0x83, 0x9E, 0xE9, 0xE0, 0xFF, 0x9F, 0xC0, 0x16, 0x1F, 0xFF, 0xFE,
|
||||
0x3F, 0xFF, 0xFC, 0x3C, 0x03, 0x83, 0xBC, 0x61, 0xE0, 0xFF, 0x07, 0xC0, 0x1E, 0x1F, 0xFF, 0xFE, 0x3F, 0xFF, 0xFC, 0x3C, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x1C, 0x3F, 0xFF, 0xFC,
|
||||
0x3F, 0xFF, 0xF8, 0x3C, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x00, 0x1E, 0x1F, 0xFF, 0xFE, 0x3F, 0xFF, 0xFC, 0x3C, 0x00, 0x00, 0x00, 0x07, 0xC0, 0x00, 0x00, 0x00, 0x1C, 0x1F, 0xFF, 0xFC,
|
||||
0x3F, 0xFF, 0xFC, 0x3C, 0x00, 0x00, 0x00, 0x17, 0xC0, 0x00, 0x00, 0x00, 0x16, 0x3F, 0xFF, 0xFE, 0x3F, 0xFF, 0xFC, 0x3C, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x3F, 0xFF, 0xFE,
|
||||
0x3F, 0xFF, 0xFC, 0x3C, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x3F, 0xFF, 0xFC, 0x3F, 0xFF, 0xFE, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x7F, 0xFF, 0xFE,
|
||||
0x2F, 0xFF, 0xFF, 0xBC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x80, 0x5F, 0xFF, 0xFF, 0xF8, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00,
|
||||
0x01, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xA0, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00,
|
||||
0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00,
|
||||
0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
}
|
||||
|
||||
func TestImageFromBytesMonochrome(t *testing.T) {
|
||||
image := pixel.NewImageFromBytes[pixel.Monochrome](128, 128, rprofile)
|
||||
if width, height := image.Size(); width != 128 && height != 128 {
|
||||
t.Errorf("image.Size(): expected 128, 128 but got %d, %d", width, height)
|
||||
}
|
||||
|
||||
raw := image.RawBuffer()
|
||||
for i, b := range raw {
|
||||
if b != rprofile[i] {
|
||||
t.Fatalf("failed to roundtrip image. expected %v but got %v", rprofile[i], b)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Test pixel formats by filling them with noise and checking whether they
|
||||
// contain the same data afterwards.
|
||||
func TestImageNoise(t *testing.T) {
|
||||
t.Run("RGB888", func(t *testing.T) {
|
||||
testImageNoiseN[pixel.RGB888](t)
|
||||
})
|
||||
t.Run("RGB565BE", func(t *testing.T) {
|
||||
testImageNoiseN[pixel.RGB565BE](t)
|
||||
})
|
||||
t.Run("RGB555", func(t *testing.T) {
|
||||
testImageNoiseN[pixel.RGB555](t)
|
||||
})
|
||||
t.Run("RGB444BE", func(t *testing.T) {
|
||||
testImageNoiseN[pixel.RGB444BE](t)
|
||||
})
|
||||
t.Run("Monochrome", func(t *testing.T) {
|
||||
testImageNoiseN[pixel.Monochrome](t)
|
||||
})
|
||||
}
|
||||
|
||||
// Run the testImageNoise multiple times, because a single test might not catch
|
||||
// all bugs (since the test uses random data).
|
||||
func testImageNoiseN[T pixel.Color](t *testing.T) {
|
||||
for i := 0; i < 10; i++ {
|
||||
testImageNoise[T](t)
|
||||
}
|
||||
}
|
||||
|
||||
func testImageNoise[T pixel.Color](t *testing.T) {
|
||||
// Create an image of a random width/height for extra testing.
|
||||
width := rand.Int()%500 + 10
|
||||
height := rand.Int()%500 + 10
|
||||
t.Log("image size:", width, height)
|
||||
|
||||
// Create two images: the to-be-tested image object and a reference image.
|
||||
img := pixel.NewImage[T](width, height)
|
||||
ref := goimage.NewRGBA(goimage.Rect(0, 0, width, height))
|
||||
|
||||
// Fill the two images with noise.
|
||||
for y := 0; y < height; y++ {
|
||||
for x := 0; x < width; x++ {
|
||||
// Set a random color in both images.
|
||||
c := pixel.NewColor[T](uint8(rand.Uint32()), uint8(rand.Uint32()), uint8(rand.Uint32()))
|
||||
img.Set(x, y, c)
|
||||
ref.Set(x, y, c.RGBA())
|
||||
}
|
||||
}
|
||||
|
||||
// Compare the two images. They should match.
|
||||
mismatch := 0
|
||||
firstX := 0
|
||||
firstY := 0
|
||||
var firstExpected, firstActual color.RGBA
|
||||
for y := 0; y < height; y++ {
|
||||
for x := 0; x < width; x++ {
|
||||
c := img.Get(x, y).RGBA()
|
||||
r2, g2, b2, _ := ref.At(x, y).RGBA()
|
||||
c2 := color.RGBA{R: uint8(r2 >> 8), G: uint8(g2 >> 8), B: uint8(b2 >> 8), A: 255}
|
||||
if c != c2 {
|
||||
mismatch++
|
||||
if mismatch == 1 {
|
||||
firstX = x
|
||||
firstY = y
|
||||
firstExpected = c
|
||||
firstActual = c2
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if mismatch != 0 {
|
||||
t.Errorf("mismatch found: %d pixels are different (first diff at (%d, %d), expected %v, actual %v)", mismatch, firstX, firstY, firstExpected, firstActual)
|
||||
}
|
||||
}
|
||||
|
||||
+33
-2
@@ -14,9 +14,9 @@ import (
|
||||
|
||||
// Pixel with a particular color, matching the underlying hardware of a
|
||||
// particular display. Each pixel is at least 1 byte in size.
|
||||
// The color format is sRGB (or close to it) in all cases.
|
||||
// The color format is sRGB (or close to it) in all cases except for 1-bit.
|
||||
type Color interface {
|
||||
RGB888 | RGB565BE | RGB555 | RGB444BE
|
||||
RGB888 | RGB565BE | RGB555 | RGB444BE | Monochrome
|
||||
|
||||
BaseColor
|
||||
}
|
||||
@@ -50,6 +50,8 @@ func NewColor[T Color](r, g, b uint8) T {
|
||||
return any(NewRGB555(r, g, b)).(T)
|
||||
case RGB444BE:
|
||||
return any(NewRGB444BE(r, g, b)).(T)
|
||||
case Monochrome:
|
||||
return any(NewMonochrome(r, g, b)).(T)
|
||||
default:
|
||||
panic("unknown color format")
|
||||
}
|
||||
@@ -202,6 +204,35 @@ func (c RGB444BE) RGBA() color.RGBA {
|
||||
return color
|
||||
}
|
||||
|
||||
type Monochrome bool
|
||||
|
||||
func NewMonochrome(r, g, b uint8) Monochrome {
|
||||
// Very simple black/white split.
|
||||
// This isn't very accurate (especially for sRGB colors) but is close enough.
|
||||
if int(r)+int(g)+int(b) > 128*3 { // light, convert to white
|
||||
return Monochrome(true)
|
||||
}
|
||||
// dark, convert to black
|
||||
return Monochrome(false)
|
||||
}
|
||||
|
||||
func (c Monochrome) BitsPerPixel() int {
|
||||
return 1
|
||||
}
|
||||
|
||||
func (c Monochrome) RGBA() color.RGBA {
|
||||
value := uint8(0)
|
||||
if c {
|
||||
value = 255
|
||||
}
|
||||
return color.RGBA{
|
||||
R: value,
|
||||
G: value,
|
||||
B: value,
|
||||
A: 255,
|
||||
}
|
||||
}
|
||||
|
||||
// Gamma brightness lookup table:
|
||||
// https://victornpb.github.io/gamma-table-generator
|
||||
// gamma = 0.45 steps = 256 range = 0-255
|
||||
|
||||
+98
-17
@@ -17,6 +17,7 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
"tinygo.org/x/drivers/netdev"
|
||||
"tinygo.org/x/drivers/netlink"
|
||||
)
|
||||
@@ -27,13 +28,13 @@ var _debug debug = debugBasic
|
||||
//var _debug debug = debugBasic | debugNetdev | debugRpc
|
||||
|
||||
var (
|
||||
version = "0.0.1"
|
||||
driverName = "Realtek rtl8720dn Wifi network device driver (rtl8720dn)"
|
||||
)
|
||||
|
||||
const (
|
||||
O_NONBLOCK = 1 // note: different value than syscall.O_NONBLOCK (0x800)
|
||||
RTW_MODE_STA = 0x00000001
|
||||
O_NONBLOCK = 1 // note: different value than syscall.O_NONBLOCK (0x800)
|
||||
RTW_MODE_STA = 0x00000001
|
||||
defaultChannel = 6
|
||||
)
|
||||
|
||||
type sock int32
|
||||
@@ -102,14 +103,22 @@ func (r *rtl8720dn) connectToAP() error {
|
||||
return netlink.ErrMissingSSID
|
||||
}
|
||||
|
||||
if len(r.params.Passphrase) != 0 && len(r.params.Passphrase) < 8 {
|
||||
return netlink.ErrShortPassphrase
|
||||
}
|
||||
|
||||
if debugging(debugBasic) {
|
||||
fmt.Printf("Connecting to Wifi SSID '%s'...", r.params.Ssid)
|
||||
}
|
||||
|
||||
// Start the connection process
|
||||
securityType := uint32(0x00400004)
|
||||
securityType := uint32(0) // RTW_SECURITY_OPEN
|
||||
if len(r.params.Passphrase) != 0 {
|
||||
securityType = 0x00400004 // RTW_SECURITY_WPA2_AES_PSK
|
||||
}
|
||||
|
||||
result := r.rpc_wifi_connect(r.params.Ssid, r.params.Passphrase, securityType, -1, 0)
|
||||
if result == -1 {
|
||||
if result != 0 {
|
||||
if debugging(debugBasic) {
|
||||
fmt.Printf("FAILED\r\n")
|
||||
}
|
||||
@@ -127,6 +136,51 @@ func (r *rtl8720dn) connectToAP() error {
|
||||
return r.startDhcpc()
|
||||
}
|
||||
|
||||
func (r *rtl8720dn) startDhcps() error {
|
||||
if result := r.rpc_tcpip_adapter_dhcps_start(0); result == -1 {
|
||||
return netdev.ErrStartingDHCPServer
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *rtl8720dn) startAP() error {
|
||||
if len(r.params.Ssid) == 0 {
|
||||
return netlink.ErrMissingSSID
|
||||
}
|
||||
|
||||
if len(r.params.Passphrase) != 0 && len(r.params.Passphrase) < 8 {
|
||||
return netlink.ErrShortPassphrase
|
||||
}
|
||||
|
||||
if debugging(debugBasic) {
|
||||
fmt.Printf("Starting Wifi AP as SSID '%s'...", r.params.Ssid)
|
||||
}
|
||||
|
||||
// Start the connection process
|
||||
securityType := uint32(0) // RTW_SECURITY_OPEN
|
||||
if len(r.params.Passphrase) != 0 {
|
||||
securityType = 0x00400004 // RTW_SECURITY_WPA2_AES_PSK
|
||||
}
|
||||
|
||||
result := r.rpc_wifi_start_ap(r.params.Ssid, r.params.Passphrase, securityType, defaultChannel)
|
||||
if result != 0 {
|
||||
if debugging(debugBasic) {
|
||||
fmt.Printf("FAILED\r\n")
|
||||
}
|
||||
return netlink.ErrConnectFailed
|
||||
}
|
||||
|
||||
if debugging(debugBasic) {
|
||||
fmt.Printf("LISTENING\r\n")
|
||||
}
|
||||
|
||||
if r.notifyCb != nil {
|
||||
r.notifyCb(netlink.EventNetUp)
|
||||
}
|
||||
|
||||
return r.startDhcps()
|
||||
}
|
||||
|
||||
func (r *rtl8720dn) showDriver() {
|
||||
if r.driverShown {
|
||||
return
|
||||
@@ -134,7 +188,7 @@ func (r *rtl8720dn) showDriver() {
|
||||
if debugging(debugBasic) {
|
||||
fmt.Printf("\r\n")
|
||||
fmt.Printf("%s\r\n\r\n", driverName)
|
||||
fmt.Printf("Driver version : %s\r\n", version)
|
||||
fmt.Printf("Driver version : %s\r\n", drivers.Version)
|
||||
}
|
||||
r.driverShown = true
|
||||
}
|
||||
@@ -238,14 +292,27 @@ func (r *rtl8720dn) netConnect(reset bool) error {
|
||||
}
|
||||
r.showDevice()
|
||||
|
||||
retry:
|
||||
for i := 0; r.params.Retries == 0 || i < r.params.Retries; i++ {
|
||||
if err := r.connectToAP(); err != nil {
|
||||
if err == netlink.ErrConnectFailed {
|
||||
continue
|
||||
switch r.params.ConnectMode {
|
||||
case netlink.ConnectModeAP:
|
||||
if err := r.startAP(); err != nil {
|
||||
if err == netlink.ErrConnectFailed {
|
||||
continue
|
||||
}
|
||||
return err
|
||||
}
|
||||
return err
|
||||
break retry
|
||||
|
||||
default:
|
||||
if err := r.connectToAP(); err != nil {
|
||||
if err == netlink.ErrConnectFailed {
|
||||
continue
|
||||
}
|
||||
return err
|
||||
}
|
||||
break retry
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
if r.networkDown() {
|
||||
@@ -437,6 +504,12 @@ func ipToName(ip netip.AddrPort) []byte {
|
||||
return name
|
||||
}
|
||||
|
||||
func nameToIp(name []byte) netip.AddrPort {
|
||||
port := uint16(name[2])<<8 | uint16(name[3])
|
||||
addr, _ := netip.AddrFromSlice(name[4:8])
|
||||
return netip.AddrPortFrom(addr, port)
|
||||
}
|
||||
|
||||
func (r *rtl8720dn) Bind(sockfd int, ip netip.AddrPort) error {
|
||||
|
||||
if debugging(debugNetdev) {
|
||||
@@ -534,10 +607,10 @@ func (r *rtl8720dn) Listen(sockfd int, backlog int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *rtl8720dn) Accept(sockfd int, ip netip.AddrPort) (int, error) {
|
||||
func (r *rtl8720dn) Accept(sockfd int) (int, netip.AddrPort, error) {
|
||||
|
||||
if debugging(debugNetdev) {
|
||||
fmt.Printf("[Accept] sockfd: %d, peer: %s\r\n", sockfd, ip)
|
||||
fmt.Printf("[Accept] sockfd: %d\r\n", sockfd)
|
||||
}
|
||||
|
||||
r.mu.Lock()
|
||||
@@ -546,12 +619,12 @@ func (r *rtl8720dn) Accept(sockfd int, ip netip.AddrPort) (int, error) {
|
||||
var newSock int32
|
||||
var lsock = sock(sockfd)
|
||||
var socket = r.sockets[lsock]
|
||||
var name = ipToName(ip)
|
||||
var name = ipToName(netip.AddrPort{})
|
||||
|
||||
switch socket.protocol {
|
||||
case netdev.IPPROTO_TCP:
|
||||
default:
|
||||
return -1, netdev.ErrProtocolNotSupported
|
||||
return -1, netip.AddrPort{}, netdev.ErrProtocolNotSupported
|
||||
}
|
||||
|
||||
for {
|
||||
@@ -570,6 +643,14 @@ func (r *rtl8720dn) Accept(sockfd int, ip netip.AddrPort) (int, error) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Get remote peer ip:port
|
||||
namelen = uint32(len(name))
|
||||
result := r.rpc_lwip_getpeername(int32(newSock), name, &namelen)
|
||||
if result == -1 {
|
||||
return -1, netip.AddrPort{}, fmt.Errorf("Getpeername failed")
|
||||
}
|
||||
raddr := nameToIp(name)
|
||||
|
||||
// If we've already seen this socket, we can re-use
|
||||
// the socket and return it. But, only if the socket
|
||||
// is closed. If it's not closed, we'll just come back
|
||||
@@ -582,12 +663,12 @@ func (r *rtl8720dn) Accept(sockfd int, ip netip.AddrPort) (int, error) {
|
||||
continue
|
||||
}
|
||||
// Reuse client socket
|
||||
return int(newSock), nil
|
||||
return int(newSock), raddr, nil
|
||||
}
|
||||
|
||||
// Create new socket for client and return fd
|
||||
r.sockets[sock(newSock)] = newSocket(socket.protocol)
|
||||
return int(newSock), nil
|
||||
return int(newSock), raddr, nil
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+36
-1
@@ -1,6 +1,12 @@
|
||||
package servo
|
||||
|
||||
import "machine"
|
||||
import (
|
||||
"machine"
|
||||
|
||||
"errors"
|
||||
)
|
||||
|
||||
var ErrInvalidAngle = errors.New("servo: invalid angle")
|
||||
|
||||
// PWM is the interface necessary for controlling typical servo motors.
|
||||
type PWM interface {
|
||||
@@ -80,3 +86,32 @@ func (s Servo) SetMicroseconds(microseconds int16) {
|
||||
value := uint64(s.pwm.Top()) * uint64(microseconds) / (pwmPeriod / 1000)
|
||||
s.pwm.Set(s.channel, uint32(value))
|
||||
}
|
||||
|
||||
// SetAngle sets the angle of the servo in degrees. The angle should be between
|
||||
// 0 and 180, where 0 is the minimum angle and 180 is the maximum angle.
|
||||
// This function should work for most servos, but if it doesn't work for yours
|
||||
// you can use SetMicroseconds directly instead.
|
||||
func (s Servo) SetAngle(angle int) error {
|
||||
if angle < 0 || angle > 180 {
|
||||
return ErrInvalidAngle
|
||||
}
|
||||
|
||||
// 0° is 1000µs, 180° is 2000µs. See explanation in SetMicroseconds.
|
||||
microseconds := angle*1000/180 + 1000
|
||||
s.SetMicroseconds(int16(microseconds))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetAngleWithMicroseconds sets the angle of the servo in degrees. The angle should be between
|
||||
// 0 and 180, where 0 is the minimum angle and 180 is the maximum angle.
|
||||
// The high duration can be customized
|
||||
// 0° is lowMicroseconds(us), 180° is highMicroseconds(us)
|
||||
func (s Servo) SetAngleWithMicroseconds(angle int, lowMicroseconds, highMicroseconds int) error {
|
||||
if angle < 0 || angle > 180 {
|
||||
return ErrInvalidAngle
|
||||
}
|
||||
microseconds := lowMicroseconds + (highMicroseconds-lowMicroseconds)*angle/180
|
||||
s.SetMicroseconds(int16(microseconds))
|
||||
return nil
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user