Compare commits

...

20 Commits

Author SHA1 Message Date
deadprogram a15f2167cc release: prepare for v0.19.0
Signed-off-by: deadprogram <ron@hybridgroup.com>
2022-01-26 18:26:16 +01:00
deadprogram 5d5378a47c all: update license year to 2022
Signed-off-by: deadprogram <ron@hybridgroup.com>
2022-01-26 18:25:15 +01:00
deadprogram 3ceb688663 examples/wifi: add unified example for tcpclient that compiles for all supported wifi adaptors
Signed-off-by: deadprogram <ron@hybridgroup.com>
2022-01-24 13:24:39 +01:00
deadprogram 941c1c9057 wifi/espat, rtl8720dn, wifinina, net: modify to use Adapter interface
Signed-off-by: deadprogram <ron@hybridgroup.com>
2022-01-24 13:24:39 +01:00
deadprogram d8c813d515 wifi/espat, rtl8720dn, wifinina: move towards standard common interface for wifi adaptors
Signed-off-by: deadprogram <ron@hybridgroup.com>
2022-01-24 13:24:39 +01:00
Drayton Munster 905fc6fce3 hd44780: fix 4-bit data length flag 2022-01-20 20:01:37 +01:00
Drayton Munster 80913d5fe7 Reset data pins to output mode after reading 2022-01-20 20:01:37 +01:00
Olivier Fauchon b6c750ccd1 sx126x: Driver for Semtech sx126x radio modules and optional RF Switch.
This first version of the driver has been tested with STM32WL SoC,
	which embeddeds SX1262 radio on the same die.
2022-01-15 17:22:36 +01:00
deadprogram 43899e1330 build: remove smoketest for Arduino keyboard4x4 for now
Signed-off-by: deadprogram <ron@hybridgroup.com>
2022-01-12 18:26:10 +01:00
sago35 45c68ef0fc ft6336: add support for ft6336 2022-01-04 15:54:35 +01:00
Olivier Fauchon 025a66655f shtc3: Sensirion SHTC3 Relative Humidity / Temperature i2c sensor 2021-12-28 16:02:10 +01:00
deadprogram 121e8147f7 hd44780i2c: clean up for go fmt
Signed-off-by: deadprogram <ron@hybridgroup.com>
2021-12-24 16:00:39 +01:00
Alan Wang b48bc5ac5d Update hd44780i2c.go 2021-12-24 11:54:00 +01:00
Alan Wang 0d40c99d1f Add files via upload 2021-12-22 16:49:54 +01:00
Alan Wang bee7422c3a Fix Nano 33 BLE drivers (#351)
Fix HTS221, LPS22HB and APDS9960 drivers and their examples for Arduino Nano 33 BLE:

* Update hts221_nano_33_ble.go
* Update lps22hb_nano_33_ble.go
* Update apds9960_nano_33_ble.go
2021-12-20 09:57:37 +01:00
spearson78 43099c5d5f XPT2046 Touch driver (#350)
Co-authored-by: Steven Pearson <steven.pearson.78@gmail.com>
2021-12-14 09:14:10 +09:00
Yurii Soldak 0f9b9d873b wifinina: avoid busy wait 2021-12-10 12:15:56 +01:00
sago35 114e24870e ili9341, ili9342: add support for m5stack 2021-11-27 13:03:07 +01:00
soypat b33c84ff78 add PCA9685 driver 2021-11-21 14:46:15 +01:00
sago35 966210f1b0 ili9341, ili9342: add support for m5stack-core2 2021-11-20 12:20:34 +01:00
90 changed files with 3499 additions and 433 deletions
+26
View File
@@ -1,3 +1,29 @@
0.19.0
---
- **new devices**
- ft6336: add support for ft6336
- pca9685: PCA9685 driver
- shtc3: Sensirion SHTC3 Relative Humidity / Temperature i2c sensor
- sx126x: Driver for Semtech sx126x radio modules
- xpt2046: XPT2046 Touch driver (#350)
- **enhancements**
- **hd44780i2c**
- clean up for go fmt
- Needed fixes and update hd44780i2c.go
- **ili9341, ili9342**
- add support for m5stack
- add support for m5stack-core2
- **wifi**
- modify to use shared net.Adapter interface for all supported wifi devices
- wifinina: remove busy wait
- **bugfixes**
- **hd44780**
- fix 4-bit data length flag
- Reset data pins to output mode after reading
- Nano 33 BLE drivers (#351)
- **docs**
- examples/wifi: add unified example for tcpclient that compiles for all supported wifi adaptors
0.18.0
---
- **new devices**
+1 -1
View File
@@ -1,4 +1,4 @@
Copyright (c) 2018-2021 The TinyGo Authors. All rights reserved.
Copyright (c) 2018-2022 The TinyGo Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
+14 -3
View File
@@ -117,6 +117,8 @@ smoke-test:
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/sht3x/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/shtc3/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/ssd1306/i2c_128x32/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/ssd1306/spi_128x64/main.go
@@ -187,8 +189,8 @@ endif
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=feather-m0 ./examples/dht/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=arduino ./examples/keypad4x4/main.go
@md5sum ./build/test.hex
# tinygo build -size short -o ./build/test.hex -target=arduino ./examples/keypad4x4/main.go
# @md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=xiao ./examples/pcf8563/alarm/
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=xiao ./examples/pcf8563/clkout/
@@ -215,12 +217,21 @@ endif
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.elf -target=wioterminal ./examples/axp192/m5stack-core2-blinky/
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.uf2 -target=pico ./examples/xpt2046/main.go
@md5sum ./build/test.uf2
tinygo build -size short -o ./build/test.elf -target=m5stack-core2 ./examples/ft6336/basic/
@md5sum ./build/test.elf
tinygo build -size short -o ./build/test.elf -target=m5stack-core2 ./examples/ft6336/touchpaint/
@md5sum ./build/test.elf
tinygo build -size short -o ./build/test.hex -target=nucleo-wl55jc ./examples/sx126x/lora_rxtx/
@md5sum ./build/test.hex
DRIVERS = $(wildcard */)
NOTESTS = build examples flash semihosting pcd8544 shiftregister st7789 microphone mcp3008 gps microbitmatrix \
hcsr04 ssd1331 ws2812 thermistor apa102 easystepper ssd1351 ili9341 wifinina shifter hub75 \
hd44780 buzzer ssd1306 espat l9110x st7735 bmi160 l293x dht keypad4x4 max72xx p1am tone tm1637 \
pcf8563 mcp2515 servo sdcard rtl8720dn image cmd i2csoft hts221 lps22hb apds9960 axp192
pcf8563 mcp2515 servo sdcard rtl8720dn image cmd i2csoft hts221 lps22hb apds9960 axp192 xpt2046 \
ft6336 sx126x
TESTS = $(filter-out $(addsuffix /%,$(NOTESTS)),$(DRIVERS))
unit-test:
+5 -1
View File
@@ -52,7 +52,7 @@ func main() {
## Currently supported devices
The following 74 devices are supported.
The following 78 devices are supported.
| Device Name | Interface Type |
|----------|-------------|
@@ -78,6 +78,7 @@ The following 74 devices are supported.
| [DS3231 real time clock](https://datasheets.maximintegrated.com/en/ds/DS3231.pdf) | I2C |
| [ESP32 as WiFi Coprocessor with Arduino nina-fw](https://github.com/arduino/nina-fw) | SPI |
| [ESP8266/ESP32 AT Command set for WiFi/TCP/UDP](https://github.com/espressif/esp32-at) | UART |
| [FT6336 touch controller](https://focuslcds.com/content/FT6236.pdf) | I2C |
| [GPS module](https://www.u-blox.com/en/product/neo-6-series) | I2C/UART |
| [HC-SR04 Ultrasonic distance sensor](https://cdn.sparkfun.com/datasheets/Sensors/Proximity/HCSR04.pdf) | GPIO |
| [HD44780 LCD controller](https://www.sparkfun.com/datasheets/LCD/HD44780.pdf) | GPIO/I2C |
@@ -114,6 +115,7 @@ The following 74 devices are supported.
| [Shift register (PISO)](https://en.wikipedia.org/wiki/Shift_register#Parallel-in_serial-out_\(PISO\)) | GPIO |
| [Shift registers (SIPO)](https://en.wikipedia.org/wiki/Shift_register#Serial-in_parallel-out_(SIPO)) | GPIO |
| [SHT3x Digital Humidity Sensor](https://www.sensirion.com/fileadmin/user_upload/customers/sensirion/Dokumente/2_Humidity_Sensors/Datasheets/Sensirion_Humidity_Sensors_SHT3x_Datasheet_digital.pdf) | I2C |
| [SHTC3 Digital Humidity Sensor (RH/T)](https://www.sensirion.com/fileadmin/user_upload/customers/sensirion/Dokumente/2_Humidity_Sensors/Datasheets/Sensirion_Humidity_Sensors_SHTC3_Datasheet.pdf) | I2C |
| [SPI NOR Flash Memory](https://en.wikipedia.org/wiki/Flash_memory#NOR_flash) | SPI/QSPI |
| [SPI SDCARD/MMC](https://en.wikipedia.org/wiki/SD_card) | SPI |
| [SSD1306 OLED display](https://cdn-shop.adafruit.com/datasheets/SSD1306.pdf) | I2C / SPI |
@@ -131,6 +133,8 @@ The following 74 devices are supported.
| [Waveshare 2.13" e-paper display](https://www.waveshare.com/w/upload/e/e6/2.13inch_e-Paper_Datasheet.pdf) | SPI |
| [Waveshare 4.2" e-paper B/W display](https://www.waveshare.com/w/upload/6/6a/4.2inch-e-paper-specification.pdf) | SPI |
| [WS2812 RGB LED](https://cdn-shop.adafruit.com/datasheets/WS2812.pdf) | GPIO |
| [XPT2046 touch controller](http://grobotronics.com/images/datasheets/xpt2046-datasheet.pdf) | GPIO |
| [Semtech SX126x Lora](https://www.semtech.com/products/wireless-rf/lora-transceiv-ers/sx1261) | SPI |
## Contributing
+53 -46
View File
@@ -57,6 +57,14 @@ type enableConfig struct {
PON bool
}
// New creates a new APDS-9960 connection. The I2C bus must already be
// configured.
//
// This function only creates the Device object, it does not touch the device.
func New(bus drivers.I2C) Device {
return Device{bus: bus, Address: ADPS9960_ADDRESS, mode: MODE_NONE}
}
// Connected returns whether APDS-9960 has been found.
// It does a "who am I" request and checks the response.
func (d *Device) Connected() bool {
@@ -65,52 +73,6 @@ func (d *Device) Connected() bool {
return data[0] == 0xAB
}
// Configure sets up the APDS-9960 device.
func (d *Device) Configure(cfg Configuration) {
d.DisableAll() // turn off everything
// "default" settings
if cfg.ProximityPulseLength == 0 {
cfg.ProximityPulseLength = 16
}
if cfg.ProximityPulseCount == 0 {
cfg.ProximityPulseCount = 64
}
if cfg.GesturePulseLength == 0 {
cfg.GesturePulseLength = 16
}
if cfg.GesturePulseCount == 0 {
cfg.GesturePulseCount = 64
}
if cfg.ProximityGain == 0 {
cfg.ProximityGain = 1
}
if cfg.GestureGain == 0 {
cfg.GestureGain = 1
}
if cfg.ColorGain == 0 {
cfg.ColorGain = 4
}
if cfg.ADCIntegrationCycles == 0 {
cfg.ADCIntegrationCycles = 4
}
if cfg.threshold == 0 {
d.gesture.threshold = 30
}
if cfg.sensitivity == 0 {
d.gesture.sensitivity = 20
}
d.SetProximityPulse(cfg.ProximityPulseLength, cfg.ProximityPulseCount)
d.SetGesturePulse(cfg.GesturePulseLength, cfg.GesturePulseCount)
d.SetGains(cfg.ProximityGain, cfg.GestureGain, cfg.ColorGain)
d.SetADCIntegrationCycles(cfg.ADCIntegrationCycles)
if cfg.LEDBoost > 0 {
d.LEDBoost(cfg.LEDBoost)
}
}
// GetMode returns current engine mode
func (d *Device) GetMode() uint8 {
return d.mode
@@ -353,6 +315,51 @@ func (d *Device) ReadGesture() (gesture int32) {
// private functions
func (d *Device) configureDevice(cfg Configuration) {
d.DisableAll() // turn off everything
// "default" settings
if cfg.ProximityPulseLength == 0 {
cfg.ProximityPulseLength = 16
}
if cfg.ProximityPulseCount == 0 {
cfg.ProximityPulseCount = 64
}
if cfg.GesturePulseLength == 0 {
cfg.GesturePulseLength = 16
}
if cfg.GesturePulseCount == 0 {
cfg.GesturePulseCount = 64
}
if cfg.ProximityGain == 0 {
cfg.ProximityGain = 1
}
if cfg.GestureGain == 0 {
cfg.GestureGain = 1
}
if cfg.ColorGain == 0 {
cfg.ColorGain = 4
}
if cfg.ADCIntegrationCycles == 0 {
cfg.ADCIntegrationCycles = 4
}
if cfg.threshold == 0 {
d.gesture.threshold = 30
}
if cfg.sensitivity == 0 {
d.gesture.sensitivity = 20
}
d.SetProximityPulse(cfg.ProximityPulseLength, cfg.ProximityPulseCount)
d.SetGesturePulse(cfg.GesturePulseLength, cfg.GesturePulseCount)
d.SetGains(cfg.ProximityGain, cfg.GestureGain, cfg.ColorGain)
d.SetADCIntegrationCycles(cfg.ADCIntegrationCycles)
if cfg.LEDBoost > 0 {
d.LEDBoost(cfg.LEDBoost)
}
}
func (d *Device) enable(cfg enableConfig) {
var gen, pien, aien, wen, pen, aen, pon uint8
+4 -6
View File
@@ -5,10 +5,8 @@ package apds9960
import "tinygo.org/x/drivers"
// New creates a new APDS-9960 connection. The I2C bus must already be
// configured.
//
// This function only creates the Device object, it does not touch the device.
func New(bus drivers.I2C) Device {
return Device{bus: bus, Address: ADPS9960_ADDRESS, mode: MODE_NONE}
// Configure sets up the APDS-9960 device.
func (d *Device) Configure(cfg Configuration) {
// configure device
d.configureDevice(cfg)
}
+12 -17
View File
@@ -6,24 +6,19 @@ package apds9960
import (
"machine"
"time"
"tinygo.org/x/drivers"
)
// New creates a new APDS-9960 connection. The I2C bus must already be
// configured.
//
// This function only creates the Device object, it does not touch the device.
func New(bus drivers.I2C) Device {
// turn on internal power pin (machine.P0_22) and I2C1 pullups power pin (machine.P1_00)
// and wait a moment.
ENV := machine.P0_22
ENV.Configure(machine.PinConfig{Mode: machine.PinOutput})
ENV.High()
R := machine.P1_00
R.Configure(machine.PinConfig{Mode: machine.PinOutput})
R.High()
time.Sleep(time.Millisecond * 10)
// Configure sets up the APDS-9960 device.
func (d *Device) Configure(cfg Configuration) {
return Device{bus: bus, Address: ADPS9960_ADDRESS, mode: MODE_NONE}
// Following lines are Nano 33 BLE specific, they have nothing to do with sensor per se
machine.LSM_PWR.Configure(machine.PinConfig{Mode: machine.PinOutput})
machine.LSM_PWR.High()
machine.I2C_PULLUP.Configure(machine.PinConfig{Mode: machine.PinOutput})
machine.I2C_PULLUP.High()
// Wait a moment
time.Sleep(10 * time.Millisecond)
// configure device
d.configureDevice(cfg)
}
+20
View File
@@ -0,0 +1,20 @@
package espat
import (
"time"
"tinygo.org/x/drivers/net"
)
func (d *Device) ConnectToAccessPoint(ssid, pass string, timeout time.Duration) error {
if len(ssid) == 0 {
return net.ErrWiFiMissingSSID
}
d.SetWifiMode(WifiModeClient)
return d.ConnectToAP(ssid, pass, 10)
}
func (d *Device) Disconnect() error {
return d.DisconnectFromAP()
}
+5 -4
View File
@@ -9,21 +9,22 @@ import (
func main() {
// use Nano 33 BLE Sense's internal I2C bus
machine.I2C1.Configure(machine.I2CConfig{
SCL: machine.P0_15, // SCL1 on Nano 33 BLE Sense
SDA: machine.P0_14, // SDA1 on Nano 33 BLE Sense
SCL: machine.SCL1_PIN,
SDA: machine.SDA1_PIN,
Frequency: machine.TWI_FREQ_400KHZ,
})
sensor := apds9960.New(machine.I2C1)
sensor.Configure(apds9960.Configuration{}) // use default settings
if !sensor.Connected() {
println("APDS-9960 not connected!")
return
}
sensor.Configure(apds9960.Configuration{}) // use default settings
sensor.EnableColor() // enable color engine
for {
+6 -5
View File
@@ -9,21 +9,22 @@ import (
func main() {
// use Nano 33 BLE Sense's internal I2C bus
machine.I2C1.Configure(machine.I2CConfig{
SCL: machine.P0_15, // SCL1 on Nano 33 BLE Sense
SDA: machine.P0_14, // SDA1 on Nano 33 BLE Sense
SCL: machine.SCL1_PIN,
SDA: machine.SDA1_PIN,
Frequency: machine.TWI_FREQ_400KHZ,
})
sensor := apds9960.New(machine.I2C1)
sensor.Configure(apds9960.Configuration{}) // use default settings
if !sensor.Connected() {
println("APDS-9960 not connected!")
return
}
sensor.Configure(apds9960.Configuration{}) // use default settings
sensor.EnableGesture() // enable gesture engine
for {
@@ -34,7 +35,7 @@ func main() {
gesture := sensor.ReadGesture()
print("Detected gesture: ")
switch gesture {
case apds9960.GESTURE_UP:
case apds9960.GESTURE_UP: // the nRF52 chip is "up"
println("Up")
case apds9960.GESTURE_DOWN:
println("Down")
+6 -4
View File
@@ -9,21 +9,23 @@ import (
func main() {
// use Nano 33 BLE Sense's internal I2C bus
machine.I2C1.Configure(machine.I2CConfig{
SCL: machine.P0_15, // SCL1 on Nano 33 BLE Sense
SDA: machine.P0_14, // SDA1 on Nano 33 BLE Sense
SCL: machine.SCL1_PIN,
SDA: machine.SDA1_PIN,
Frequency: machine.TWI_FREQ_400KHZ,
})
sensor := apds9960.New(machine.I2C1)
// use default settings
sensor.Configure(apds9960.Configuration{})
if !sensor.Connected() {
println("APDS-9960 not connected!")
return
}
sensor.Configure(apds9960.Configuration{}) // use default settings
sensor.EnableProximity() // enable proximity engine
for {
+3 -2
View File
@@ -113,8 +113,9 @@ func connectToESP() bool {
func connectToAP() {
println("Connecting to wifi network '" + ssid + "'")
adaptor.SetWifiMode(espat.WifiModeClient)
adaptor.ConnectToAP(ssid, pass, 10)
if err := adaptor.ConnectToAccessPoint(ssid, pass, 10*time.Second); err != nil {
failMessage(err.Error())
}
println("Connected.")
ip, err := adaptor.GetClientIP()
+3 -2
View File
@@ -99,8 +99,9 @@ func connectToESP() bool {
func connectToAP() {
println("Connecting to wifi network '" + ssid + "'")
adaptor.SetWifiMode(espat.WifiModeClient)
adaptor.ConnectToAP(ssid, pass, 10)
if err := adaptor.ConnectToAccessPoint(ssid, pass, 10*time.Second); err != nil {
failMessage(err.Error())
}
println("Connected.")
ip, err := adaptor.GetClientIP()
+3 -2
View File
@@ -88,8 +88,9 @@ func connectToESP() bool {
func connectToAP() {
println("Connecting to wifi network '" + ssid + "'")
adaptor.SetWifiMode(espat.WifiModeClient)
adaptor.ConnectToAP(ssid, pass, 10)
if err := adaptor.ConnectToAccessPoint(ssid, pass, 10*time.Second); err != nil {
failMessage(err.Error())
}
println("Connected.")
ip, err := adaptor.GetClientIP()
+3 -2
View File
@@ -108,8 +108,9 @@ func connectToESP() bool {
func connectToAP() {
println("Connecting to wifi network '" + ssid + "'")
adaptor.SetWifiMode(espat.WifiModeClient)
adaptor.ConnectToAP(ssid, pass, 10)
if err := adaptor.ConnectToAccessPoint(ssid, pass, 10*time.Second); err != nil {
failMessage(err.Error())
}
println("Connected.")
ip, err := adaptor.GetClientIP()
+3 -2
View File
@@ -129,8 +129,9 @@ func connectToESP() bool {
func connectToAP() {
println("Connecting to wifi network '" + ssid + "'")
adaptor.SetWifiMode(espat.WifiModeClient)
adaptor.ConnectToAP(ssid, pass, 10)
if err := adaptor.ConnectToAccessPoint(ssid, pass, 10*time.Second); err != nil {
failMessage(err.Error())
}
println("Connected.")
ip, err := adaptor.GetClientIP()
+3 -2
View File
@@ -91,8 +91,9 @@ func connectToESP() bool {
func connectToAP() {
println("Connecting to wifi network '" + ssid + "'")
adaptor.SetWifiMode(espat.WifiModeClient)
adaptor.ConnectToAP(ssid, pass, 10)
if err := adaptor.ConnectToAccessPoint(ssid, pass, 10*time.Second); err != nil {
failMessage(err.Error())
}
println("Connected.")
ip, err := adaptor.GetClientIP()
+24
View File
@@ -0,0 +1,24 @@
//go:build m5stack_core2
// +build m5stack_core2
package main
import (
"machine"
"tinygo.org/x/drivers/ft6336"
"tinygo.org/x/drivers/i2csoft"
"tinygo.org/x/drivers/touch"
)
// InitDisplay initializes the display of each board.
func initDevices() (touch.Pointer, error) {
i2c := i2csoft.New(machine.SCL0_PIN, machine.SDA0_PIN)
i2c.Configure(i2csoft.I2CConfig{Frequency: 100e3})
resistiveTouch := ft6336.New(i2c, machine.Pin(39))
resistiveTouch.Configure(ft6336.Config{})
resistiveTouch.SetPeriodActive(0x00)
return resistiveTouch, nil
}
+16
View File
@@ -0,0 +1,16 @@
package main
func main() {
touchScreen, _ := initDevices()
for {
touch := touchScreen.ReadTouchPoint()
if touch.Z > 0 {
//X and Y are 16 bit with 12 bit resolution and need to be scaled for the display size
//Z is 24 bit and is typically > 2000 for a touch
println("touch:", touch.X, touch.Y, touch.Z)
//Example of scaling for m5stack-core2's 320x240 display with 320x270 touch area
println("screen:", (touch.X*320)>>16, (touch.Y*270)>>16)
}
}
}
@@ -0,0 +1,55 @@
//go:build m5stack_core2
// +build m5stack_core2
package main
import (
"image/color"
"machine"
axp192 "tinygo.org/x/drivers/axp192/m5stack-core2-axp192"
"tinygo.org/x/drivers/ft6336"
"tinygo.org/x/drivers/i2csoft"
"tinygo.org/x/drivers/ili9341"
"tinygo.org/x/drivers/touch"
)
// InitDisplay initializes the display of each board.
func initDevices() (touchPaintDisplay, touch.Pointer, error) {
machine.SPI2.Configure(machine.SPIConfig{
SCK: machine.LCD_SCK_PIN,
SDO: machine.LCD_SDO_PIN,
SDI: machine.LCD_SDI_PIN,
Frequency: 40e6,
})
i2c := i2csoft.New(machine.SCL0_PIN, machine.SDA0_PIN)
i2c.Configure(i2csoft.I2CConfig{Frequency: 100e3})
axp := axp192.New(i2c)
led := axp.LED
led.Low()
display := ili9341.NewSPI(
machine.SPI2,
machine.LCD_DC_PIN,
machine.LCD_SS_PIN,
machine.NoPin,
)
// configure display
display.Configure(ili9341.Config{
Width: 320,
Height: 240,
DisplayInversion: true,
})
display.FillScreen(color.RGBA{255, 255, 255, 255})
display.SetRotation(ili9341.Rotation0Mirror)
resistiveTouch := ft6336.New(i2c, machine.Pin(39))
resistiveTouch.Configure(ft6336.Config{})
resistiveTouch.SetPeriodActive(0x00)
return display, resistiveTouch, nil
}
+163
View File
@@ -0,0 +1,163 @@
package main
import (
"image/color"
"math"
"tinygo.org/x/drivers"
"tinygo.org/x/drivers/touch"
)
type touchPaintDisplay interface {
drivers.Displayer
FillRectangle(x, y, width, height int16, c color.RGBA) error
DrawRectangle(x, y, w, h int16, c color.RGBA) error
}
var (
white = color.RGBA{255, 255, 255, 255}
black = color.RGBA{0, 0, 0, 255}
red = color.RGBA{255, 0, 0, 255}
green = color.RGBA{0, 255, 0, 255}
blue = color.RGBA{0, 0, 255, 255}
magenta = color.RGBA{255, 0, 255, 255}
yellow = color.RGBA{255, 255, 0, 255}
cyan = color.RGBA{0, 255, 255, 255}
oldColor color.RGBA
currentColor color.RGBA
)
const (
penRadius = 3
boxSize = 30
Xmin = 0
Xmax = 0xFFFF
Ymin = 0
Ymax = 0xFFFF
)
func main() {
display, resistiveTouch, _ := initDevices()
// fill the background and activate the backlight
width, height := display.Size()
display.FillRectangle(0, 0, width, height, black)
// make color selection boxes
display.FillRectangle(0, 0, boxSize, boxSize, red)
display.FillRectangle(boxSize, 0, boxSize, boxSize, yellow)
display.FillRectangle(boxSize*2, 0, boxSize, boxSize, green)
display.FillRectangle(boxSize*3, 0, boxSize, boxSize, cyan)
display.FillRectangle(boxSize*4, 0, boxSize, boxSize, blue)
display.FillRectangle(boxSize*5, 0, boxSize, boxSize, magenta)
display.FillRectangle(boxSize*6, 0, boxSize, boxSize, black)
display.FillRectangle(boxSize*7, 0, boxSize, boxSize, white)
// set the initial color to red and draw a box to highlight it
oldColor = red
currentColor = red
display.DrawRectangle(0, 0, boxSize, boxSize, white)
last := touch.Point{}
// loop and poll for touches, including performing debouncing
debounce := 0
for {
point := resistiveTouch.ReadTouchPoint()
touch := touch.Point{}
if point.Z>>6 > 100 {
rawX := mapval(point.X, Xmin, Xmax, 0, int(width))
rawY := mapval(point.Y, Ymin, Ymax, 0, int(height))
touch.X = rawX
touch.Y = rawY
touch.Z = 1
} else {
touch.X = 0
touch.Y = 0
touch.Z = 0
}
if last.Z != touch.Z {
debounce = 0
last = touch
} else if math.Abs(float64(touch.X-last.X)) > 4 ||
math.Abs(float64(touch.Y-last.Y)) > 4 {
debounce = 0
last = touch
} else if debounce > 1 {
debounce = 0
HandleTouch(display, last)
} else if touch.Z > 0 {
debounce++
} else {
last = touch
debounce = 0
}
}
}
// based on Arduino's "map" function
func mapval(x int, inMin int, inMax int, outMin int, outMax int) int {
return (x-inMin)*(outMax-outMin)/(inMax-inMin) + outMin
}
func HandleTouch(display touchPaintDisplay, touch touch.Point) {
if int16(touch.Y) < boxSize {
oldColor = currentColor
x := int16(touch.X)
switch {
case x < boxSize:
currentColor = red
case x < boxSize*2:
currentColor = yellow
case x < boxSize*3:
currentColor = green
case x < boxSize*4:
currentColor = cyan
case x < boxSize*5:
currentColor = blue
case x < boxSize*6:
currentColor = magenta
case x < boxSize*7:
currentColor = black
case x < boxSize*8:
currentColor = white
}
if oldColor == currentColor {
return
}
display.DrawRectangle((x/boxSize)*boxSize, 0, boxSize, boxSize, white)
switch oldColor {
case red:
x = 0
case yellow:
x = boxSize
case green:
x = boxSize * 2
case cyan:
x = boxSize * 3
case blue:
x = boxSize * 4
case magenta:
x = boxSize * 5
case black:
x = boxSize * 6
case white:
x = boxSize * 7
}
display.FillRectangle(int16(x), 0, boxSize, boxSize, oldColor)
}
if (int16(touch.Y) - penRadius) > boxSize {
display.FillRectangle(
int16(touch.X), int16(touch.Y), penRadius*2, penRadius*2, currentColor)
}
}
+5 -6
View File
@@ -9,28 +9,27 @@ import (
func main() {
// use Nano 33 BLE Sense's internal I2C bus
machine.I2C1.Configure(machine.I2CConfig{
SCL: machine.P0_15, // SCL1 on Nano 33 BLE Sense
SDA: machine.P0_14, // SDA1 on Nano 33 BLE Sense
SCL: machine.SCL1_PIN,
SDA: machine.SDA1_PIN,
Frequency: machine.TWI_FREQ_400KHZ,
})
sensor := hts221.New(machine.I2C1)
sensor.Configure() // power on and calibrate
if !sensor.Connected() {
println("HTS221 not connected!")
return
}
sensor.Configure() // power on and calibrate
for {
h, _ := sensor.ReadHumidity()
t, _ := sensor.ReadTemperature()
println("h =", float32(h)/100.0, "% / t =", float32(t)/1000.0, "*C")
time.Sleep(time.Second)
}
}
+43
View File
@@ -0,0 +1,43 @@
//go:build m5stack
// +build m5stack
package initdisplay
import (
"machine"
"tinygo.org/x/drivers/ili9341"
)
func InitDisplay() *ili9341.Device {
machine.SPI2.Configure(machine.SPIConfig{
SCK: machine.SPI0_SCK_PIN,
SDO: machine.SPI0_SDO_PIN,
SDI: machine.SPI0_SDI_PIN,
Frequency: 40e6,
})
// configure backlight
backlight := machine.LCD_BL_PIN
backlight.Configure(machine.PinConfig{machine.PinOutput})
display := ili9341.NewSPI(
machine.SPI2,
machine.LCD_DC_PIN,
machine.LCD_SS_PIN,
machine.LCD_RST_PIN,
)
// configure display
display.Configure(ili9341.Config{
Width: 320,
Height: 240,
DisplayInversion: true,
})
backlight.High()
display.SetRotation(ili9341.Rotation0Mirror)
return display
}
@@ -0,0 +1,49 @@
//go:build m5stack_core2
// +build m5stack_core2
package initdisplay
import (
"image/color"
"machine"
axp192 "tinygo.org/x/drivers/axp192/m5stack-core2-axp192"
"tinygo.org/x/drivers/i2csoft"
"tinygo.org/x/drivers/ili9341"
)
// InitDisplay initializes the display of each board.
func InitDisplay() *ili9341.Device {
machine.SPI2.Configure(machine.SPIConfig{
SCK: machine.LCD_SCK_PIN,
SDO: machine.LCD_SDO_PIN,
SDI: machine.LCD_SDI_PIN,
Frequency: 40e6,
})
i2c := i2csoft.New(machine.SCL0_PIN, machine.SDA0_PIN)
i2c.Configure(i2csoft.I2CConfig{Frequency: 100e3})
axp := axp192.New(i2c)
led := axp.LED
led.Low()
display := ili9341.NewSPI(
machine.SPI2,
machine.LCD_DC_PIN,
machine.LCD_SS_PIN,
machine.NoPin,
)
// configure display
display.Configure(ili9341.Config{
Width: 320,
Height: 240,
DisplayInversion: true,
})
display.FillScreen(color.RGBA{255, 255, 255, 255})
display.SetRotation(ili9341.Rotation0Mirror)
return display
}
+4 -6
View File
@@ -9,29 +9,27 @@ import (
func main() {
// use Nano 33 BLE Sense's internal I2C bus
machine.I2C1.Configure(machine.I2CConfig{
SCL: machine.P0_15, // SCL1 on Nano 33 BLE Sense
SDA: machine.P0_14, // SDA1 on Nano 33 BLE Sense
SCL: machine.SCL1_PIN,
SDA: machine.SDA1_PIN,
Frequency: machine.TWI_FREQ_400KHZ,
})
sensor := lps22hb.New(machine.I2C1)
sensor.Configure()
if !sensor.Connected() {
println("LPS22HB not connected!")
return
}
sensor.Configure()
for {
p, _ := sensor.ReadPressure()
t, _ := sensor.ReadTemperature()
println("p =", float32(p)/1000.0, "hPa / t =", float32(t)/1000.0, "*C")
time.Sleep(time.Second)
// note: the device would power down itself after each query
}
}
+33 -16
View File
@@ -3,37 +3,54 @@ package main
import (
"machine"
"time"
"tinygo.org/x/drivers/lsm303agr"
)
func main() {
// LSM303AGR/MAG is connected to the I2C0 bus on micro:bit v1 (the same as P19/P20) and v2 (internal)
machine.I2C0.Configure(machine.I2CConfig{})
accel_mag := lsm303agr.New(machine.I2C0)
if !accel_mag.Connected() {
sensor := lsm303agr.New(machine.I2C0)
sensor.Configure(lsm303agr.Configuration{}) //default settings
// you can specify the following options to adjust accuracy, sensor range or save power.
// see https://github.com/tinygo-org/drivers/blob/release/lsm303agr/registers.go for details:
/*
sensor.Configure(lsm303agr.Configuration{
AccelPowerMode: lsm303agr.ACCEL_POWER_NORMAL,
AccelRange: lsm303agr.ACCEL_RANGE_2G,
AccelDataRate: lsm303agr.ACCEL_DATARATE_100HZ,
MagPowerMode: lsm303agr.MAG_POWER_NORMAL,
MagSystemMode: lsm303agr.MAG_SYSTEM_CONTINUOUS,
MagDataRate: lsm303agr.MAG_DATARATE_10HZ,
})
*/
if !sensor.Connected() {
println("LSM303AGR/MAG not connected!")
return
}
accel_mag.Configure(lsm303agr.Configuration{}) //default settings
for {
// accel_x, accel_y, accel_z := sensor.ReadAcceleration()
// println("ACCEL_X:", accel_x/100000, " ACCEL_Y:", accel_y/100000, " ACCEL_Z:", accel_z/100000)
accel_x, accel_y, accel_z := accel_mag.ReadAcceleration()
pitch, roll := accel_mag.ReadPitchRoll()
mag_x, mag_y, mag_z := accel_mag.ReadMagneticField()
heading := accel_mag.ReadCompass()
temp, _ := accel_mag.ReadTemperature()
// mag_x, mag_y, mag_z := sensor.ReadMagneticField()
// println("MAG_X:", mag_x/100000, " MAG_Y:", mag_y/100000, " MAG_Z:", mag_z/100000)
pitch, roll := sensor.ReadPitchRoll()
println("Pitch:", float32(pitch)/100000, " Roll:", float32(roll)/100000)
heading := sensor.ReadCompass()
println("Heading:", float32(heading)/100000, "degrees")
temp, _ := sensor.ReadTemperature()
println("Temperature:", float32(temp)/1000, "*C")
println("ACCEL_X:", accel_x, " ACCEL_Y:", accel_y, " ACCEL_Z:", accel_z)
println("MAG_X:", mag_x, " MAG_Y:", mag_y, " MAG_Z:", mag_z)
println("Pitch:", pitch, " Roll:", roll)
println("Heading:", heading)
println("Temperature:", temp/1000)
println("\n")
time.Sleep(time.Millisecond * 100)
time.Sleep(time.Millisecond * 250)
}
}
+68
View File
@@ -0,0 +1,68 @@
package main
import (
"machine"
"time"
"tinygo.org/x/drivers/pca9685"
)
func main() {
const (
// Default address on most breakout boards.
pcaAddr = 0x40
)
err := machine.I2C0.Configure(machine.I2CConfig{})
if err != nil {
panic(err.Error())
}
d := pca9685.New(machine.I2C0, 0x40)
err = d.IsConnected()
if err != nil {
panic(err.Error())
}
err = d.Configure(pca9685.PWMConfig{Period: 1e9 / 200}) // 200Hz PWM
if err != nil {
panic(err.Error())
}
var value uint32
step := d.Top() / 5
for {
for value = 0; value <= d.Top(); value += step {
d.SetAll(value)
dc := 100 * value / d.Top()
println("set dc @", dc, "%")
time.Sleep(800 * time.Millisecond)
}
}
}
// ScanI2CDev finds I2C devices on the bus and rreturns them inside
// a slice. If slice is nil then no devices were found.
func ScanI2CDev(bus machine.I2C) (addrs []uint8) {
var addr, count uint8
var err error
w := []byte{1}
// Count devices in first scan
for addr = 1; addr < 127; addr++ {
err = bus.Tx(uint16(addr), w, nil)
if err == nil {
count++
}
}
if count == 0 {
return nil
}
// Allocate slice and populate slice with addresses
addrs = make([]uint8, count)
count = 0
for addr = 1; addr < 127; addr++ {
err = bus.Tx(uint16(addr), w, nil)
if err == nil && count < uint8(len(addrs)) {
addrs[count] = addr
count++
}
}
return addrs
}
+1 -1
View File
@@ -65,7 +65,7 @@ func run() error {
}
net.UseDriver(rtl)
err = rtl.ConnectToAP(ssid, password)
err = rtl.ConnectToAccessPoint(ssid, password, 10*time.Second)
if err != nil {
return err
}
+1 -1
View File
@@ -74,7 +74,7 @@ func run() error {
}
net.UseDriver(rtl)
err = rtl.ConnectToAP(ssid, password)
err = rtl.ConnectToAccessPoint(ssid, password, 10*time.Second)
if err != nil {
return err
}
+1 -1
View File
@@ -47,7 +47,7 @@ func run() error {
}
net.UseDriver(rtl)
err = rtl.ConnectToAP(ssid, password)
err = rtl.ConnectToAccessPoint(ssid, password, 10*time.Second)
if err != nil {
return err
}
+1 -1
View File
@@ -47,7 +47,7 @@ func run() error {
}
net.UseDriver(rtl)
err = rtl.ConnectToAP(ssid, password)
err = rtl.ConnectToAccessPoint(ssid, password, 10*time.Second)
if err != nil {
return err
}
+1 -1
View File
@@ -76,7 +76,7 @@ func run() error {
net.UseDriver(rtl)
http.SetBuf(buf[:])
err = rtl.ConnectToAP(ssid, password)
err = rtl.ConnectToAccessPoint(ssid, password, 10*time.Second)
if err != nil {
return err
}
+1 -1
View File
@@ -43,7 +43,7 @@ func run() error {
net.UseDriver(rtl)
http.SetBuf(buf[:])
err = rtl.ConnectToAP(ssid, password)
err = rtl.ConnectToAccessPoint(ssid, password, 10*time.Second)
if err != nil {
return err
}
@@ -74,7 +74,7 @@ func run() error {
http.SetBuf(buf[:])
fmt.Fprintf(terminal, "ConnectToAP()\r\n")
err = rtl.ConnectToAP(ssid, password)
err = rtl.ConnectToAccessPoint(ssid, password, 10*time.Second)
if err != nil {
return err
}
+1 -1
View File
@@ -43,7 +43,7 @@ func run() error {
net.UseDriver(rtl)
http.SetBuf(buf[:])
err = rtl.ConnectToAP(ssid, password)
err = rtl.ConnectToAccessPoint(ssid, password, 10*time.Second)
if err != nil {
return err
}
+1 -1
View File
@@ -43,7 +43,7 @@ func run() error {
}
http.UseDriver(rtl)
err = rtl.ConnectToAP(ssid, password)
err = rtl.ConnectToAccessPoint(ssid, password, 10*time.Second)
if err != nil {
return err
}
+30
View File
@@ -0,0 +1,30 @@
package main
import (
"fmt"
"machine"
"time"
"tinygo.org/x/drivers/shtc3"
)
func main() {
machine.I2C0.Configure(machine.I2CConfig{})
sensor := shtc3.New(machine.I2C0)
for {
sensor.WakeUp()
temp, humidity, _ := sensor.ReadTemperatureHumidity()
t := fmt.Sprintf("%.2f", float32(temp)/1000)
h := fmt.Sprintf("%.2f", float32(humidity)/100)
println("Temperature:", t, "°C")
println("Humidity", h, "%")
sensor.Sleep()
time.Sleep(2 * time.Second)
}
}
@@ -0,0 +1,80 @@
package main
// This example will periodically enable Continuous "Preamble" and "Wave" modes on 868.1 Mhz
import (
"machine"
"time"
rfswitch "tinygo.org/x/drivers/examples/sx126x/rfswitch"
"tinygo.org/x/drivers/sx126x"
)
const FREQ = 868100000
var (
loraRadio *sx126x.Device
)
func main() {
println("\n# TinyGo Lora continuous Wave/Preamble test")
println("# -----------------------------------------")
machine.LED.Configure(machine.PinConfig{Mode: machine.PinOutput})
// Create the driver
loraRadio = sx126x.New(machine.SPI3)
loraRadio.SetDeviceType(sx126x.DEVICE_TYPE_SX1262)
// Create RF Switch
var radioSwitch rfswitch.CustomSwitch
loraRadio.SetRfSwitch(radioSwitch)
state := loraRadio.DetectDevice()
if !state {
panic("sx126x not detected. ")
}
// Prepare for Lora operation
loraConf := sx126x.LoraConfig{
Freq: FREQ,
Bw: sx126x.SX126X_LORA_BW_500_0,
Sf: sx126x.SX126X_LORA_SF9,
Cr: sx126x.SX126X_LORA_CR_4_7,
HeaderType: sx126x.SX126X_LORA_HEADER_EXPLICIT,
Preamble: 12,
Ldr: sx126x.SX126X_LORA_LOW_DATA_RATE_OPTIMIZE_OFF,
Iq: sx126x.SX126X_LORA_IQ_STANDARD,
Crc: sx126x.SX126X_LORA_CRC_ON,
SyncWord: sx126x.SX126X_LORA_MAC_PRIVATE_SYNCWORD,
LoraTxPowerDBm: 14,
}
loraRadio.LoraConfig(loraConf)
// Although LoraConfig has already configured most of Lora settings,
// the following lines are still required to enable Continuous Preamble/Wave
loraRadio.SetPacketType(sx126x.SX126X_PACKET_TYPE_LORA)
loraRadio.SetRfFrequency(loraConf.Freq)
loraRadio.SetModulationParams(loraConf.Sf, loraConf.Bw, loraConf.Cr, loraConf.Ldr)
loraRadio.SetTxParams(loraConf.LoraTxPowerDBm, sx126x.SX126X_PA_RAMP_200U)
for {
println("2 seconds in Continuous Preamble")
loraRadio.SetStandby()
loraRadio.SetTxContinuousPreamble()
time.Sleep(2 * time.Second)
println("Continuous Preamble Stopped")
loraRadio.SetStandby()
time.Sleep(10 * time.Second)
println("2 seconds in Continuous Wave")
loraRadio.SetTxContinuousWave()
time.Sleep(2 * time.Second)
println(" Continuous Wave Stopped")
loraRadio.SetStandby()
time.Sleep(60 * time.Second)
}
}
+98
View File
@@ -0,0 +1,98 @@
package main
// In this example, a Lora packet will be sent every 10s
// module will be in RX mode between two transmissions
import (
"device/stm32"
"machine"
"runtime/interrupt"
"time"
rfswitch "tinygo.org/x/drivers/examples/sx126x/rfswitch"
"tinygo.org/x/drivers/sx126x"
)
const FREQ = 868100000
const (
LORA_DEFAULT_RXTIMEOUT_MS = 1000
LORA_DEFAULT_TXTIMEOUT_MS = 5000
)
var (
loraRadio *sx126x.Device
txmsg = []byte("Hello TinyGO")
)
// radioIntHandler will take care of radio interrupts
func radioIntHandler(intr interrupt.Interrupt) {
loraRadio.HandleInterrupt()
}
func main() {
println("\n# TinyGo Lora RX/TX test")
println("# ----------------------")
machine.LED.Configure(machine.PinConfig{Mode: machine.PinOutput})
// Create the driver
loraRadio = sx126x.New(machine.SPI3)
loraRadio.SetDeviceType(sx126x.DEVICE_TYPE_SX1262)
// Create RF Switch
var radioSwitch rfswitch.CustomSwitch
loraRadio.SetRfSwitch(radioSwitch)
// Detect the device
state := loraRadio.DetectDevice()
if !state {
panic("sx126x not detected.")
}
// Add interrupt handler for Radio IRQs
intr := interrupt.New(stm32.IRQ_Radio_IRQ_Busy, radioIntHandler)
intr.Enable()
loraConf := sx126x.LoraConfig{
Freq: FREQ,
Bw: sx126x.SX126X_LORA_BW_500_0,
Sf: sx126x.SX126X_LORA_SF9,
Cr: sx126x.SX126X_LORA_CR_4_7,
HeaderType: sx126x.SX126X_LORA_HEADER_EXPLICIT,
Preamble: 12,
Ldr: sx126x.SX126X_LORA_LOW_DATA_RATE_OPTIMIZE_OFF,
Iq: sx126x.SX126X_LORA_IQ_STANDARD,
Crc: sx126x.SX126X_LORA_CRC_ON,
SyncWord: sx126x.SX126X_LORA_MAC_PRIVATE_SYNCWORD,
LoraTxPowerDBm: 20,
}
loraRadio.LoraConfig(loraConf)
var count uint
for {
tStart := time.Now()
// Blocking RX for LORA_DEFAULT_RXTIMEOUT_MS
println("Start Lora RX for 10 sec")
for int(time.Now().Sub(tStart).Seconds()) < 10 {
buf, err := loraRadio.LoraRx(LORA_DEFAULT_RXTIMEOUT_MS)
if err != nil {
println("RX Error: ", err)
} else if buf != nil {
println("Packet Received: len=", len(buf), string(buf))
}
}
println("END Lora RX")
println("LORA TX size=", len(txmsg))
err := loraRadio.LoraTx(txmsg, LORA_DEFAULT_TXTIMEOUT_MS)
if err != nil {
println("TX Error:", err)
}
count++
}
}
+61
View File
@@ -0,0 +1,61 @@
//go:build gnse
// +build gnse
/*
Generic Node Sensor Edition
RFSwitch
Disable Switch : PB8=OFF PA0=OFF PA1=OFF
Enable RX : PB8=ON PA0=ON PA1=OFF
Enable TX RFO LP : PB8=ON PA0=ON PA1=ON
Enable TX RFO HP : PB8=ON PA0=OFF PA1=ON
*/
package rfswitch
import (
"machine"
"tinygo.org/x/drivers/sx126x"
)
type CustomSwitch struct {
}
var (
rfstate int
)
func (s CustomSwitch) InitRFSwitch() {
machine.RF_FE_CTRL1.Configure(machine.PinConfig{Mode: machine.PinOutput})
machine.RF_FE_CTRL2.Configure(machine.PinConfig{Mode: machine.PinOutput})
machine.RF_FE_CTRL3.Configure(machine.PinConfig{Mode: machine.PinOutput})
rfstate = -1 //Unknown
}
func (s CustomSwitch) SetRfSwitchMode(mode int) error {
if mode == rfstate {
return nil
}
switch mode {
case sx126x.RFSWITCH_TX_HP:
machine.RF_FE_CTRL1.Set(false)
machine.RF_FE_CTRL2.Set(true)
machine.RF_FE_CTRL3.Set(true)
case sx126x.RFSWITCH_TX_LP:
machine.RF_FE_CTRL1.Set(true)
machine.RF_FE_CTRL2.Set(true)
machine.RF_FE_CTRL3.Set(true)
case sx126x.RFSWITCH_RX:
machine.RF_FE_CTRL1.Set(true)
machine.RF_FE_CTRL2.Set(false)
machine.RF_FE_CTRL3.Set(true)
}
rfstate = mode
return nil
}
+43
View File
@@ -0,0 +1,43 @@
//go:build lorae5
// +build lorae5
package radio
/*
/!\ LoRa-E5 module ONLY transmits through RFO_HP:
Receive: PA4=1, PA5=0
Transmit(high output power, SMPS mode): PA4=0, PA5=1
*/
import (
"errors"
"machine"
"tinygo.org/x/drivers/sx126x"
)
type CustomSwitch struct {
}
func (s CustomSwitch) InitRFSwitch() {
machine.PA4.Configure(machine.PinConfig{Mode: machine.PinOutput})
machine.PB5.Configure(machine.PinConfig{Mode: machine.PinOutput})
}
func (s CustomSwitch) SetRfSwitchMode(mode int) error {
switch mode {
case sx126x.RFSWITCH_RX:
machine.PA4.Set(true)
machine.PB5.Set(false)
case sx126x.RFSWITCH_TX_LP:
errors.New("RFSWITCH_TX_LP not supported ")
case sx126x.RFSWITCH_TX_HP:
machine.PA4.Set(false)
machine.PB5.Set(true)
}
return nil
}
+55
View File
@@ -0,0 +1,55 @@
//go:build nucleowl55jc
// +build nucleowl55jc
/*
Nucleo WL55JC1
RFSwitch
+-----------+---------+------------+------------+
| | FE_CTRL1 | FE_CTRL2 | FE_CTRL3 |
| | (PC4) | (PC5) | (PC3) |
+-----------+----------+-----------+------------+
| TX_HP | LOW | HIGH | HIGH |
| TX_LP | HIGH | HIGH | HIGH |
| RX | HIGH | LOW | HIGH |
+-----------+----------+-----------+------------+
*/
package rfswitch
import (
"machine"
"tinygo.org/x/drivers/sx126x"
)
type CustomSwitch struct {
}
func (s CustomSwitch) InitRFSwitch() {
machine.PC4.Configure(machine.PinConfig{Mode: machine.PinOutput})
machine.PC5.Configure(machine.PinConfig{Mode: machine.PinOutput})
machine.PC3.Configure(machine.PinConfig{Mode: machine.PinOutput})
}
func (s CustomSwitch) SetRfSwitchMode(mode int) error {
switch mode {
case sx126x.RFSWITCH_TX_HP:
machine.PC4.Set(false)
machine.PC5.Set(true)
machine.PC3.Set(true)
case sx126x.RFSWITCH_TX_LP:
machine.PC4.Set(true)
machine.PC5.Set(true)
machine.PC3.Set(true)
case sx126x.RFSWITCH_RX:
machine.PC4.Set(true)
machine.PC5.Set(false)
machine.PC3.Set(true)
}
return nil
}
+28
View File
@@ -0,0 +1,28 @@
//go:build espat
// +build espat
package main
import (
"machine"
"tinygo.org/x/drivers/espat"
)
// these are the default pins for the Arduino Nano33 IoT.
// change these to connect to a different UART or pins for the ESP8266/ESP32
var (
uart = machine.UART1
tx = machine.PA22
rx = machine.PA23
adaptor *espat.Device
)
func initAdaptor() *espat.Device {
uart.Configure(machine.UARTConfig{TX: tx, RX: rx})
adaptor = espat.New(uart)
adaptor.Configure()
return adaptor
}
+111
View File
@@ -0,0 +1,111 @@
// This example opens a TCP connection and sends some data,
// for the purpose of testing speed and connectivity.
//
// You can open a server to accept connections from this program using:
//
// nc -w 5 -lk 8080
//
package main
import (
"bytes"
"fmt"
"time"
"tinygo.org/x/drivers/net"
)
// access point info
const ssid = ""
const pass = ""
// IP address of the server aka "hub". Replace with your own info.
const serverIP = ""
var buf = &bytes.Buffer{}
func main() {
initAdaptor()
connectToAP()
for {
sendBatch()
time.Sleep(500 * time.Millisecond)
}
println("Done.")
}
func sendBatch() {
// make TCP connection
ip := net.ParseIP(serverIP)
raddr := &net.TCPAddr{IP: ip, Port: 8080}
laddr := &net.TCPAddr{Port: 8080}
message("---------------\r\nDialing TCP connection")
conn, err := net.DialTCP("tcp", laddr, raddr)
for ; err != nil; conn, err = net.DialTCP("tcp", laddr, raddr) {
message(err.Error())
time.Sleep(5 * time.Second)
}
n := 0
w := 0
start := time.Now()
// send data
message("Sending data")
for i := 0; i < 1000; i++ {
buf.Reset()
fmt.Fprint(buf,
"\r---------------------------- i == ", i, " ----------------------------"+
"\r---------------------------- i == ", i, " ----------------------------")
if w, err = conn.Write(buf.Bytes()); err != nil {
println("error:", err.Error(), "\r")
continue
}
n += w
}
buf.Reset()
ms := time.Now().Sub(start).Milliseconds()
fmt.Fprint(buf, "\nWrote ", n, " bytes in ", ms, " ms\r\n")
message(buf.String())
if _, err := conn.Write(buf.Bytes()); err != nil {
println("error:", err.Error(), "\r")
}
// Right now this code is never reached. Need a way to trigger it...
println("Disconnecting TCP...")
conn.Close()
}
// connect to access point
func connectToAP() {
time.Sleep(2 * time.Second)
println("Connecting to " + ssid)
err := adaptor.ConnectToAccessPoint(ssid, pass, 10*time.Second)
if err != nil { // error connecting to AP
for {
println(err)
time.Sleep(1 * time.Second)
}
}
println("Connected.")
time.Sleep(2 * time.Second)
ip, err := adaptor.GetClientIP()
for ; err != nil; ip, err = adaptor.GetClientIP() {
message(err.Error())
time.Sleep(1 * time.Second)
}
message(ip)
}
func message(msg string) {
println(msg, "\r")
}
+77
View File
@@ -0,0 +1,77 @@
//go:build rtl8720dn
// +build rtl8720dn
package main
import (
"device/sam"
"machine"
"runtime/interrupt"
"time"
"tinygo.org/x/drivers/net"
"tinygo.org/x/drivers/rtl8720dn"
)
var (
adaptor *rtl8720dn.RTL8720DN
uart UARTx
)
func initAdaptor() *rtl8720dn.RTL8720DN {
adaptor, err := setupRTL8720DN()
if err != nil {
return nil
}
net.UseDriver(adaptor)
return adaptor
}
func handleInterrupt(interrupt.Interrupt) {
// should reset IRQ
uart.Receive(byte((uart.Bus.DATA.Get() & 0xFF)))
uart.Bus.INTFLAG.SetBits(sam.SERCOM_USART_INT_INTFLAG_RXC)
}
func setupRTL8720DN() (*rtl8720dn.RTL8720DN, error) {
machine.RTL8720D_CHIP_PU.Configure(machine.PinConfig{Mode: machine.PinOutput})
machine.RTL8720D_CHIP_PU.Low()
time.Sleep(100 * time.Millisecond)
machine.RTL8720D_CHIP_PU.High()
time.Sleep(1000 * time.Millisecond)
uart = UARTx{
UART: &machine.UART{
Buffer: machine.NewRingBuffer(),
Bus: sam.SERCOM0_USART_INT,
SERCOM: 0,
},
}
uart.Interrupt = interrupt.New(sam.IRQ_SERCOM0_2, handleInterrupt)
uart.Configure(machine.UARTConfig{TX: machine.PB24, RX: machine.PC24, BaudRate: 614400})
rtl := rtl8720dn.New(uart)
//rtl.Debug(debug)
_, err := rtl.Rpc_tcpip_adapter_init()
if err != nil {
return nil, err
}
return rtl, nil
}
type UARTx struct {
*machine.UART
}
func (u UARTx) Read(p []byte) (n int, err error) {
if u.Buffered() == 0 {
time.Sleep(1 * time.Millisecond)
return 0, nil
}
return u.UART.Read(p)
}
+36
View File
@@ -0,0 +1,36 @@
//go:build wifinina
// +build wifinina
package main
import (
"machine"
"tinygo.org/x/drivers/wifinina"
)
var (
// default interface for the Arduino Nano33 IoT.
spi = machine.NINA_SPI
// ESP32/ESP8266 chip that has the WIFININA firmware flashed on it
adaptor *wifinina.Device
)
func initAdaptor() *wifinina.Device {
// Configure SPI for 8Mhz, Mode 0, MSB First
spi.Configure(machine.SPIConfig{
Frequency: 8 * 1e6,
SDO: machine.NINA_SDO,
SDI: machine.NINA_SDI,
SCK: machine.NINA_SCK,
})
adaptor = wifinina.New(spi,
machine.NINA_CS,
machine.NINA_ACK,
machine.NINA_GPIO0,
machine.NINA_RESETN)
adaptor.Configure()
return adaptor
}
+7 -11
View File
@@ -128,19 +128,15 @@ func waitSerial() {
// connect to access point
func connectToAP() {
if len(ssid) == 0 || len(pass) == 0 {
for {
println("Connection failed: Either ssid or password not set")
time.Sleep(10 * time.Second)
}
}
time.Sleep(2 * time.Second)
println("Connecting to " + ssid)
adaptor.SetPassphrase(ssid, pass)
for st, _ := adaptor.GetConnectionStatus(); st != wifinina.StatusConnected; {
println("Connection status: " + st.String())
time.Sleep(1 * time.Second)
st, _ = adaptor.GetConnectionStatus()
err := adaptor.ConnectToAccessPoint(ssid, pass, 10*time.Second)
if err != nil { // error connecting to AP
for {
println(err)
time.Sleep(1 * time.Second)
}
}
println("Connected.")
}
+9 -13
View File
@@ -132,22 +132,18 @@ func waitSerial() {
// connect to access point
func connectToAP() {
if len(ssid) == 0 || len(pass) == 0 {
time.Sleep(2 * time.Second)
println("Connecting to " + ssid)
err := adaptor.ConnectToAccessPoint(ssid, pass, 10*time.Second)
if err != nil { // error connecting to AP
for {
println("Connection failed: Either ssid or password not set")
time.Sleep(10 * time.Second)
println(err)
time.Sleep(1 * time.Second)
}
}
time.Sleep(2 * time.Second)
message("Connecting to " + ssid)
adaptor.SetPassphrase(ssid, pass)
for st, _ := adaptor.GetConnectionStatus(); st != wifinina.StatusConnected; {
message("Connection status: " + st.String())
time.Sleep(1 * time.Second)
st, _ = adaptor.GetConnectionStatus()
}
message("Connected.")
time.Sleep(2 * time.Second)
println("Connected.")
ip, _, _, err := adaptor.GetIP()
for ; err != nil; ip, _, _, err = adaptor.GetIP() {
message(err.Error())
+8 -6
View File
@@ -104,14 +104,16 @@ func main() {
func connectToAP() {
time.Sleep(2 * time.Second)
println("Connecting to " + ssid)
adaptor.SetPassphrase(ssid, pass)
for st, _ := adaptor.GetConnectionStatus(); st != wifinina.StatusConnected; {
println("Connection status: " + st.String())
time.Sleep(1 * time.Second)
st, _ = adaptor.GetConnectionStatus()
err := adaptor.ConnectToAccessPoint(ssid, pass, 10*time.Second)
if err != nil { // error connecting to AP
for {
println(err)
time.Sleep(1 * time.Second)
}
}
println("Connected.")
time.Sleep(2 * time.Second)
ip, _, _, err := adaptor.GetIP()
for ; err != nil; ip, _, _, err = adaptor.GetIP() {
println(err.Error())
+8 -5
View File
@@ -116,13 +116,16 @@ func publishing() {
func connectToAP() {
time.Sleep(2 * time.Second)
println("Connecting to " + ssid)
adaptor.SetPassphrase(ssid, pass)
for st, _ := adaptor.GetConnectionStatus(); st != wifinina.StatusConnected; {
println("Connection status: " + st.String())
time.Sleep(1 * time.Second)
st, _ = adaptor.GetConnectionStatus()
err := adaptor.ConnectToAccessPoint(ssid, pass, 10*time.Second)
if err != nil { // error connecting to AP
for {
println(err)
time.Sleep(1 * time.Second)
}
}
println("Connected.")
time.Sleep(2 * time.Second)
ip, _, _, err := adaptor.GetIP()
for ; err != nil; ip, _, _, err = adaptor.GetIP() {
+9 -12
View File
@@ -151,21 +151,18 @@ func clearBuffer() {
// connect to access point
func connectToAP() {
if len(ssid) == 0 || len(pass) == 0 {
time.Sleep(2 * time.Second)
println("Connecting to " + ssid)
err := adaptor.ConnectToAccessPoint(ssid, pass, 10*time.Second)
if err != nil { // error connecting to AP
for {
println("Connection failed: Either ssid or password not set")
time.Sleep(10 * time.Second)
println(err)
time.Sleep(1 * time.Second)
}
}
time.Sleep(2 * time.Second)
message("Connecting to " + ssid)
adaptor.SetPassphrase(ssid, pass)
for st, _ := adaptor.GetConnectionStatus(); st != wifinina.StatusConnected; {
message("Connection status: " + st.String())
time.Sleep(1 * time.Second)
st, _ = adaptor.GetConnectionStatus()
}
message("Connected.")
println("Connected.")
time.Sleep(2 * time.Second)
ip, _, _, err := adaptor.GetIP()
for ; err != nil; ip, _, _, err = adaptor.GetIP() {
+10 -7
View File
@@ -113,14 +113,17 @@ func sendBatch() {
// connect to access point
func connectToAP() {
time.Sleep(2 * time.Second)
message("Connecting to " + ssid)
adaptor.SetPassphrase(ssid, pass)
for st, _ := adaptor.GetConnectionStatus(); st != wifinina.StatusConnected; {
message("Connection status: " + st.String())
time.Sleep(1 * time.Second)
st, _ = adaptor.GetConnectionStatus()
println("Connecting to " + ssid)
err := adaptor.ConnectToAccessPoint(ssid, pass, 10*time.Second)
if err != nil { // error connecting to AP
for {
println(err)
time.Sleep(1 * time.Second)
}
}
message("Connected.")
println("Connected.")
time.Sleep(2 * time.Second)
ip, _, _, err := adaptor.GetIP()
for ; err != nil; ip, _, _, err = adaptor.GetIP() {
+9 -12
View File
@@ -122,21 +122,18 @@ func makeHTTPSRequest() {
// connect to access point
func connectToAP() {
if len(ssid) == 0 || len(pass) == 0 {
time.Sleep(2 * time.Second)
println("Connecting to " + ssid)
err := adaptor.ConnectToAccessPoint(ssid, pass, 10*time.Second)
if err != nil { // error connecting to AP
for {
println("Connection failed: Either ssid or password not set")
time.Sleep(10 * time.Second)
println(err)
time.Sleep(1 * time.Second)
}
}
time.Sleep(2 * time.Second)
message("Connecting to " + ssid)
adaptor.SetPassphrase(ssid, pass)
for st, _ := adaptor.GetConnectionStatus(); st != wifinina.StatusConnected; {
message("Connection status: " + st.String())
time.Sleep(1 * time.Second)
st, _ = adaptor.GetConnectionStatus()
}
message("Connected.")
println("Connected.")
time.Sleep(2 * time.Second)
ip, _, _, err := adaptor.GetIP()
for ; err != nil; ip, _, _, err = adaptor.GetIP() {
+10 -8
View File
@@ -76,15 +76,17 @@ func main() {
// connect to access point
func connectToAP() {
time.Sleep(2 * time.Second)
message("Connecting to " + ssid)
adaptor.SetPassphrase(ssid, pass)
for st, _ := adaptor.GetConnectionStatus(); st != wifinina.StatusConnected; {
message("Connection status: " + st.String())
time.Sleep(1 * time.Second)
st, _ = adaptor.GetConnectionStatus()
println("Connecting to " + ssid)
err := adaptor.ConnectToAccessPoint(ssid, pass, 10*time.Second)
if err != nil { // error connecting to AP
for {
println(err)
time.Sleep(1 * time.Second)
}
}
message("Connected.")
time.Sleep(2 * time.Second)
println("Connected.")
ip, _, _, err := adaptor.GetIP()
for ; err != nil; ip, _, _, err = adaptor.GetIP() {
message(err.Error())
+9 -13
View File
@@ -124,22 +124,18 @@ func makeHTTPRequest() {
// connect to access point
func connectToAP() {
if len(ssid) == 0 || len(pass) == 0 {
time.Sleep(2 * time.Second)
println("Connecting to " + ssid)
err := adaptor.ConnectToAccessPoint(ssid, pass, 10*time.Second)
if err != nil { // error connecting to AP
for {
println("Connection failed: Either ssid or password not set")
time.Sleep(10 * time.Second)
println(err)
time.Sleep(1 * time.Second)
}
}
time.Sleep(2 * time.Second)
message("Connecting to " + ssid)
adaptor.SetPassphrase(ssid, pass)
for st, _ := adaptor.GetConnectionStatus(); st != wifinina.StatusConnected; {
message("Connection status: " + st.String())
time.Sleep(1 * time.Second)
st, _ = adaptor.GetConnectionStatus()
}
message("Connected.")
time.Sleep(2 * time.Second)
println("Connected.")
ip, _, _, err := adaptor.GetIP()
for ; err != nil; ip, _, _, err = adaptor.GetIP() {
message(err.Error())
+44
View File
@@ -0,0 +1,44 @@
package main
import (
"machine"
"time"
"tinygo.org/x/drivers/xpt2046"
)
func main() {
clk := machine.GPIO0
cs := machine.GPIO1
din := machine.GPIO2
dout := machine.GPIO3
irq := machine.GPIO4
touchScreen := xpt2046.New(clk, cs, din, dout, irq)
touchScreen.Configure(&xpt2046.Config{
Precision: 10, //Maximum number of samples for a single ReadTouchPoint to improve accuracy.
})
for {
//Wait for a touch
for !touchScreen.Touched() {
time.Sleep(50 * time.Millisecond)
}
touch := touchScreen.ReadTouchPoint()
//X and Y are 16 bit with 12 bit resolution and need to be scaled for the display size
//Z is 24 bit and is typically > 2000 for a touch
println("touch:", touch.X, touch.Y, touch.Z)
//Example of scaling for a 240x320 display
println("screen:", (touch.X*240)>>16, (touch.Y*320)>>16)
//Wait for touch to end
for touchScreen.Touched() {
time.Sleep(50 * time.Millisecond)
}
}
}
+107
View File
@@ -0,0 +1,107 @@
// Package ft6336 provides a driver for the FT6336 I2C Self-Capacitive touch
// panel controller.
//
// Datasheet: https://focuslcds.com/content/FT6236.pdf
//
package ft6336
import (
"machine"
"tinygo.org/x/drivers"
"tinygo.org/x/drivers/touch"
)
// Device wraps FT6336 I2C Self-Capacitive touch
type Device struct {
bus drivers.I2C
buf []byte
Address uint8
intPin machine.Pin
}
// New returns FT6336 device for the provided I2C bus using default address.
func New(i2c drivers.I2C, intPin machine.Pin) *Device {
return &Device{
bus: i2c,
buf: make([]byte, 11),
Address: Address,
intPin: intPin,
}
}
// Config contains settings for FT6636.
type Config struct {
}
// Configure the FT6336 device.
func (d *Device) Configure(config Config) error {
d.write1Byte(0xA4, 0x00)
d.intPin.Configure(machine.PinConfig{Mode: machine.PinInputPulldown})
return nil
}
// SetGMode sets interrupt mode.
// 0x00 : Interrupt Polling mode
// 0x01 : Interrupt Trigger mode (default)
func (d *Device) SetGMode(v uint8) {
d.write1Byte(RegGMode, v)
}
// GetGMode gets interrupt mode.
func (d *Device) GetGMode() uint8 {
return d.read8bit(RegGMode)
}
// SetPeriodActive sets report rate in Active mode.
func (d *Device) SetPeriodActive(v uint8) {
d.write1Byte(RegPeriodActive, v)
}
// GetPeriodActive gets report rate in Active mode.
func (d *Device) GetPeriodActive() uint8 {
return d.read8bit(RegPeriodActive)
}
// GetFirmwareID gets firmware version.
func (d *Device) GetFirmwareID() uint8 {
return d.read8bit(RegFirmid)
}
// Read reads the registers.
func (d *Device) Read() []byte {
d.bus.Tx(uint16(d.Address), []byte{0x02}, d.buf[:])
return d.buf[:]
}
// ReadTouchPoint reads a single touch.Point from the device. The maximum value
// for each touch.Point is 0xFFFF.
func (d *Device) ReadTouchPoint() touch.Point {
d.Read()
z := 0xFFFFF
if d.buf[0] == 0 {
z = 0
}
//Scale X&Y to 16 bit for consistency across touch drivers
return touch.Point{
X: (int(d.buf[1]&0x0F)<<8 + int(d.buf[2])) * ((1 << 16) / 320),
Y: (int(d.buf[3]&0x0F)<<8 + int(d.buf[4])) * ((1 << 16) / 270),
Z: z,
}
}
// Touched returns if touched or not.
func (d *Device) Touched() bool {
p := d.ReadTouchPoint()
return p.Z > 0
}
func (d *Device) write1Byte(reg, data uint8) {
d.bus.WriteRegister(d.Address, reg, []byte{data})
}
func (d *Device) read8bit(reg uint8) uint8 {
d.bus.ReadRegister(d.Address, reg, d.buf[:1])
return d.buf[0]
}
+49
View File
@@ -0,0 +1,49 @@
package ft6336
// 0x00 DEV_MODE
// 0x01 GEST_ID
// 0x02 TD_STATUS
// 0x03 P1_XH
// 0x04 P1_XL
// 0x05 P1_YH
// 0x06 P1_YL
// 0x07 P1_WEIGHT
// 0x08 P1_MISC
// 0x09 P2_XH
// 0x0A P2_XL
// 0x0B P2_YH
// 0x0C P2_YL
// 0x0D P2_WEIGHT
// 0x0E P2_MISC
// 0x80 TH_GROUP
// 0x85 TH_DIFF
// 0x86 CTRL
// 0x87 TIMEENTERMONITOR
// 0x88 PERIODACTIVE
// 0x89 PERIODMONITOR
// 0x91 RADIAN_VALUE
// 0x92 OFFSET_LEFT_RIGHT
// 0x93 OFFSET_UP_D
// 0x94 DISTANCE_LE
// 0x95 DISTANCE_UP
// 0x96 DISTANCE_ZO
// ...
// 0xA1 LIB_VER_H
// 0xA2 LIB_VER_L
// 0xA3 CIPHER
// 0xA4 G_MODE
// 0xA5 PWR_MODE
// 0xA6 FIRMID
// 0xA8 FOCALTECH_ID
// ...
// 0xAF RELEASE_CODE_ID
// ...
// 0xBC STATE
const (
Address = 0x38
RegPeriodActive = 0x88
RegGMode = 0xA4
RegFirmid = 0xA6
)
+3 -1
View File
@@ -105,7 +105,8 @@ func (g *GPIO) Read(data []byte) (n int, err error) {
data[i] = g.read()
n++
}
g.reconfigureGPIOMode(machine.PinInput)
g.rw.Low()
g.reconfigureGPIOMode(machine.PinOutput)
return n, nil
}
@@ -113,6 +114,7 @@ func (g *GPIO) read4BitMode() byte {
g.en.High()
data := (g.pins() << 4 & 0xF0)
g.en.Low()
g.en.High()
data |= (g.pins() & 0x0F)
g.en.Low()
+1 -1
View File
@@ -126,7 +126,7 @@ func (d *Device) Configure(cfg Config) error {
}
if d.datalength == DATA_LENGTH_4BIT {
d.bus.Write([]byte{DATA_LENGTH_4BIT >> 4})
d.bus.Write([]byte{DATA_LENGTH_4BIT})
}
// Busy flag is now accessible
+1 -1
View File
@@ -26,7 +26,7 @@ const (
FUNCTION_MODE = 0x20
DATA_LENGTH_8BIT = FUNCTION_MODE | 0x10
DATA_LENGTH_4BIT = FUNCTION_MODE | 0x0
DATA_LENGTH_4BIT = FUNCTION_MODE | 0x2
TWO_LINE = FUNCTION_MODE | 0x8
ONE_LINE = FUNCTION_MODE | 0x0
FONT_5X10 = FUNCTION_MODE | 0x4
+4 -3
View File
@@ -101,7 +101,7 @@ func (d *Device) Configure(cfg Config) error {
return nil
}
// ClearDisplay clears all texts on the display.
// ClearDisplay clears all texts on the display and sets the cursor back to position (0, 0).
func (d *Device) ClearDisplay() {
d.sendCommand(DISPLAY_CLEAR)
d.cursor.x = 0
@@ -119,7 +119,8 @@ func (d *Device) Home() {
// SetCursor sets the cursor to a specific position (x, y).
//
// if y (row) is set larger than actual rows, it would be set to 0.
// For example, on 16x2 LCDs the range of x (column) is 0~15 and y (row) is 0~1.
// if y is larger than actual rows, it would be set to 0 (restart from first row).
func (d *Device) SetCursor(x, y uint8) {
rowOffset := []uint8{0x0, 0x40, 0x14, 0x54}
if y > (d.height - 1) {
@@ -139,11 +140,11 @@ func (d *Device) Print(data []byte) {
if chr == '\n' {
d.newLine()
} else {
d.cursor.x++
if d.cursor.x >= d.width {
d.newLine()
}
d.sendData(uint8(rune(chr)))
d.cursor.x++
}
}
}
+8 -8
View File
@@ -21,6 +21,14 @@ type Device struct {
temperatureZero float32
}
// New creates a new HTS221 connection. The I2C bus must already be
// configured.
//
// This function only creates the Device object, it does not touch the device.
func New(bus drivers.I2C) Device {
return Device{bus: bus, Address: HTS221_ADDRESS}
}
// Connected returns whether HTS221 has been found.
// It does a "who am I" request and checks the response.
func (d *Device) Connected() bool {
@@ -29,14 +37,6 @@ func (d *Device) Connected() bool {
return data[0] == 0xBC
}
// Configure sets up the HTS221 device for communication.
func (d *Device) Configure() {
// read calibration data
d.calibration()
// activate device and use block data update mode
d.Power(true)
}
// Power is for turn on/off the HTS221 device
func (d *Device) Power(status bool) {
data := []byte{0}
+6 -6
View File
@@ -5,10 +5,10 @@ package hts221
import "tinygo.org/x/drivers"
// New creates a new HTS221 connection. The I2C bus must already be
// configured.
//
// This function only creates the Device object, it does not touch the device.
func New(bus drivers.I2C) Device {
return Device{bus: bus, Address: HTS221_ADDRESS}
// Configure sets up the HTS221 device for communication.
func (d *Device) Configure() {
// read calibration data
d.calibration()
// activate device and use block data update mode
d.Power(true)
}
+13 -17
View File
@@ -6,24 +6,20 @@ package hts221
import (
"machine"
"time"
"tinygo.org/x/drivers"
)
// New creates a new HTS221 connection. The I2C bus must already be
// configured.
//
// This function only creates the Device object, it does not touch the device.
func New(bus drivers.I2C) Device {
// turn on internal power pin (machine.P0_22) and I2C1 pullups power pin (machine.P1_00)
// and wait a moment.
ENV := machine.P0_22
ENV.Configure(machine.PinConfig{Mode: machine.PinOutput})
ENV.High()
R := machine.P1_00
R.Configure(machine.PinConfig{Mode: machine.PinOutput})
R.High()
time.Sleep(time.Millisecond * 10)
// Configure sets up the HTS221 device for communication.
func (d *Device) Configure() {
// Following lines are Nano 33 BLE specific, they have nothing to do with sensor per se
machine.HTS_PWR.Configure(machine.PinConfig{Mode: machine.PinOutput})
machine.HTS_PWR.High()
machine.I2C_PULLUP.Configure(machine.PinConfig{Mode: machine.PinOutput})
machine.I2C_PULLUP.High()
// Wait a moment
time.Sleep(10 * time.Millisecond)
return Device{bus: bus, Address: HTS221_ADDRESS}
// read calibration data
d.calibration()
// activate device and use block data update mode
d.Power(true)
}
+27 -9
View File
@@ -8,9 +8,10 @@ import (
)
type Config struct {
Width int16
Height int16
Rotation Rotation
Width int16
Height int16
Rotation Rotation
DisplayInversion bool
}
type Device struct {
@@ -103,10 +104,19 @@ func (d *Device) Configure(config Config) {
0x4E, 0xF1, 0x37, 0x07, 0x10, 0x03, 0x0E, 0x09, 0x00,
GMCTRN1, 15, 0x00, 0x0E, 0x14, 0x03, 0x11, 0x07, // Set Gamma
0x31, 0xC1, 0x48, 0x08, 0x0F, 0x0C, 0x31, 0x36, 0x0F,
}
if config.DisplayInversion {
initCmd = append(initCmd, []byte{
INVON, 0x80,
}...)
}
initCmd = append(initCmd, []byte{
SLPOUT, 0x80, // Exit Sleep
DISPON, 0x80, // Display on
0x00, // End of list
}
}...)
for i, c := 0, len(initCmd); i < c; {
cmd := initCmd[i]
if cmd == 0x00 {
@@ -239,14 +249,22 @@ func (d *Device) GetRotation() Rotation {
// SetRotation changes the rotation of the device (clock-wise)
func (d *Device) SetRotation(rotation Rotation) {
madctl := uint8(0)
switch rotation % 4 {
case 0:
switch rotation % 8 {
case Rotation0:
madctl = MADCTL_MX | MADCTL_BGR
case 1:
case Rotation90:
madctl = MADCTL_MV | MADCTL_BGR
case 2:
case Rotation180:
madctl = MADCTL_MY | MADCTL_BGR
case 3:
case Rotation270:
madctl = MADCTL_MX | MADCTL_MY | MADCTL_MV | MADCTL_BGR
case Rotation0Mirror:
madctl = MADCTL_BGR
case Rotation90Mirror:
madctl = MADCTL_MY | MADCTL_MV | MADCTL_BGR
case Rotation180Mirror:
madctl = MADCTL_MX | MADCTL_MY | MADCTL_BGR
case Rotation270Mirror:
madctl = MADCTL_MX | MADCTL_MY | MADCTL_MV | MADCTL_BGR
}
d.sendCommand(MADCTL, []uint8{madctl})
+5
View File
@@ -81,4 +81,9 @@ const (
Rotation90 Rotation = 1 // 90 degrees clock-wise rotation
Rotation180 Rotation = 2
Rotation270 Rotation = 3
Rotation0Mirror Rotation = 4
Rotation90Mirror Rotation = 5
Rotation180Mirror Rotation = 6
Rotation270Mirror Rotation = 7
)
+14 -12
View File
@@ -14,18 +14,12 @@ type Device struct {
Address uint8
}
// Connected returns whether LPS22HB has been found.
// It does a "who am I" request and checks the response.
func (d *Device) Connected() bool {
data := []byte{0}
d.bus.ReadRegister(d.Address, LPS22HB_WHO_AM_I_REG, data)
return data[0] == 0xB1
}
// Configure sets up the LPS22HB device for communication.
func (d *Device) Configure() {
// set to block update mode
d.bus.WriteRegister(d.Address, LPS22HB_CTRL1_REG, []byte{0x02})
// New creates a new LPS22HB connection. The I2C bus must already be
// configured.
//
// This function only creates the Device object, it does not touch the device.
func New(bus drivers.I2C) Device {
return Device{bus: bus, Address: LPS22HB_ADDRESS}
}
// ReadPressure returns the pressure in milli pascals (mPa).
@@ -42,6 +36,14 @@ func (d *Device) ReadPressure() (pressure int32, err error) {
return int32(pValue * 1000), nil
}
// Connected returns whether LPS22HB has been found.
// It does a "who am I" request and checks the response.
func (d *Device) Connected() bool {
data := []byte{0}
d.bus.ReadRegister(d.Address, LPS22HB_WHO_AM_I_REG, data)
return data[0] == 0xB1
}
// ReadTemperature returns the temperature in celsius milli degrees (°C/1000).
func (d *Device) ReadTemperature() (temperature int32, err error) {
d.waitForOneShot()
+4 -6
View File
@@ -5,10 +5,8 @@ package lps22hb
import "tinygo.org/x/drivers"
// New creates a new LPS22HB connection. The I2C bus must already be
// configured.
//
// This function only creates the Device object, it does not touch the device.
func New(bus drivers.I2C) Device {
return Device{bus: bus, Address: LPS22HB_ADDRESS}
// Configure sets up the LPS22HB device for communication.
func (d *Device) Configure() {
// set to block update mode
d.bus.WriteRegister(d.Address, LPS22HB_CTRL1_REG, []byte{0x02})
}
+11 -17
View File
@@ -6,24 +6,18 @@ package lps22hb
import (
"machine"
"time"
"tinygo.org/x/drivers"
)
// New creates a new LPS22HB connection. The I2C bus must already be
// configured.
//
// This function only creates the Device object, it does not touch the device.
func New(bus drivers.I2C) Device {
// turn on internal power pin (machine.P0_22) and I2C1 pullups power pin (machine.P1_00)
// and wait a moment.
ENV := machine.P0_22
ENV.Configure(machine.PinConfig{Mode: machine.PinOutput})
ENV.High()
R := machine.P1_00
R.Configure(machine.PinConfig{Mode: machine.PinOutput})
R.High()
time.Sleep(time.Millisecond * 10)
// Configure sets up the LPS22HB device for communication.
func (d *Device) Configure() {
// Following lines are Nano 33 BLE specific, they have nothing to do with sensor per se
machine.LSP_PWR.Configure(machine.PinConfig{Mode: machine.PinOutput})
machine.LSP_PWR.High()
machine.I2C_PULLUP.Configure(machine.PinConfig{Mode: machine.PinOutput})
machine.I2C_PULLUP.High()
// Wait a moment
time.Sleep(10 * time.Millisecond)
return Device{bus: bus, Address: LPS22HB_ADDRESS}
// set to block update mode
d.bus.WriteRegister(d.Address, LPS22HB_CTRL1_REG, []byte{0x02})
}
+21 -4
View File
@@ -1,6 +1,23 @@
package net
type DeviceDriver interface {
import (
"errors"
"time"
)
var (
ErrWiFiMissingSSID = errors.New("missing SSID")
ErrWiFiConnectTimeout = errors.New("WiFi connect timeout")
)
// Adapter interface is used to communicate with the network adapter.
type Adapter interface {
// functions used to connect/disconnect to/from an access point
ConnectToAccessPoint(ssid, pass string, timeout time.Duration) error
Disconnect() error
GetClientIP() (string, error)
// these functions are used once the adapter is connected to the network
GetDNS(domain string) (string, error)
ConnectTCPSocket(addr, port string) error
ConnectSSLSocket(addr, port string) error
@@ -16,12 +33,12 @@ type DeviceDriver interface {
Response(timeout int) ([]byte, error)
}
var ActiveDevice DeviceDriver
var ActiveDevice Adapter
func UseDriver(driver DeviceDriver) {
func UseDriver(a Adapter) {
// TODO: rethink and refactor this
if ActiveDevice != nil {
panic("net.ActiveDevice is already set")
}
ActiveDevice = driver
ActiveDevice = a
}
+1 -1
View File
@@ -23,7 +23,7 @@ func NewClient(o *ClientOptions) Client {
}
type mqttclient struct {
adaptor net.DeviceDriver
adaptor net.Adapter
conn net.Conn
connected bool
opts *ClientOptions
+1 -1
View File
@@ -175,7 +175,7 @@ type ClientOptionsReader struct {
// ClientOptions contains configurable options for an MQTT Client.
type ClientOptions struct {
Adaptor net.DeviceDriver
Adaptor net.Adapter
//Servers []*url.URL
Servers string
+1 -1
View File
@@ -94,7 +94,7 @@ func Dial(network, address string) (Conn, error) {
// SerialConn is a loosely net.Conn compatible implementation
type SerialConn struct {
Adaptor DeviceDriver
Adaptor Adapter
}
// UDPSerialConn is a loosely net.Conn compatible intended to support
+8
View File
@@ -0,0 +1,8 @@
package pca9685
import "errors"
var (
ErrInvalidMode1 = errors.New("pca9685: data read from MODE1 not valid")
ErrBadPeriod = errors.New("pca9685: period must be in range 1..25ms")
)
+9
View File
@@ -0,0 +1,9 @@
package pca9685
func (d *Dev) readReg(reg uint8, data []byte) error {
return d.bus.ReadRegister(d.addr, reg, data)
}
func (d *Dev) writeReg(reg uint8, data []byte) error {
return d.bus.WriteRegister(d.addr, reg, data)
}
+216
View File
@@ -0,0 +1,216 @@
package pca9685
import (
"encoding/binary"
"time"
"tinygo.org/x/drivers"
)
const (
// Internal oscillator frequency.
oscclock = 25000000
// Max value PWM value (on or off) can take.
maxtop = 1<<12 - 1
milliseconds = 1_000_000 // units in nanoseconds
)
// Dev is a handle to the PCA9685 device given an address
// (usually 0x47) and an i2c bus.
type Dev struct {
addr uint8
bus drivers.I2C
buf [4]byte
}
type PWMConfig struct {
Period uint64
}
// New creates a new instance of a PCA9685 device. It performs
// no IO on the i2c bus.
func New(bus drivers.I2C, addr uint8) Dev {
return Dev{
bus: bus,
addr: addr,
}
}
// Configure enables autoincrement, sets all PWM signals to logic low (Ground)
// and finally sets the Period.
func (d Dev) Configure(cfg PWMConfig) error {
err := d.SetAI(true)
if err != nil {
return err
}
d.SetAll(0)
d.SetDrive(true)
return d.SetPeriod(cfg.Period)
}
// SetPeriod updates the period of this PWM integrated circuit in nanoseconds.
// To set a particular frequency, use the following formula:
//
// period = 1e9 / frequency
//
// In the equation above frequency is in Hertz.
//
// If you use a period of 0, a period that works well for LEDs will be picked.
//
// PCA9685 accepts frequencies inbetween [40..1000 Hz],
// or expressed as a period [1..25ms].
func (d Dev) SetPeriod(period uint64) error {
const div = maxtop + 1
if period == 0 {
period = 1 * milliseconds
}
if period > 25*milliseconds || period < 1*milliseconds {
return ErrBadPeriod
}
// Correct for overshoot in provided frequency: https://github.com/adafruit/Adafruit-PWM-Servo-Driver-Library/issues/11
// Note: 0.96 was empirically determined to be closer. Should follow up to understand what is happening here.
freq := 96 * 1e9 / (100 * period)
prescale := byte(oscclock/(div*freq) - 1)
err := d.Sleep(true) // Enable sleep to write to PRESCALE register
if err != nil {
return err
}
d.buf[0] = prescale
err = d.writeReg(PRESCALE, d.buf[:1])
if err != nil {
return err
}
return d.Sleep(false)
}
// Top returns max value PWM can take.
func (d Dev) Top() uint32 {
return maxtop
}
// Set sets the `on` value of a PWM channel in the range [0..15].
// Example:
// d.Set(1, d.Top()/4)
// sets the dutycycle of second (LED1) channel to 25%.
func (d Dev) Set(channel uint8, on uint32) {
switch {
case on > maxtop:
panic("pca9685: value must be in range 0..4096")
case on == 0:
d.SetPhased(channel, 0, maxtop)
return
}
d.SetPhased(channel, on, 0)
}
// SetAll sets all PWM signals to a ON value. Equivalent of calling
// Dev.Set(pca9685.ALLLED, value)
func (d Dev) SetAll(on uint32) {
d.Set(ALLLED, on)
}
// IsConnected returns error if read fails or if
// driver suspects device is not connected.
func (d Dev) IsConnected() error {
// Set data to the NOT of default MODE1 contents.
// If read is succesful then data will be modified
const notdefaultMODE1 = ^defaultMODE1Value
d.buf[0] = notdefaultMODE1
err := d.readReg(MODE1, d.buf[:1])
if err != nil {
return err
} else if d.buf[0] == notdefaultMODE1 {
return ErrInvalidMode1
}
return nil
}
// SetAI enables or disables autoincrement feature on device. Useful for
// writing to many consecutive registers in one shot.
func (d Dev) SetAI(ai bool) error {
err := d.readReg(MODE1, d.buf[:1])
if err != nil {
return err
}
if ai {
d.buf[0] |= AI
} else {
d.buf[0] &^= AI
}
err = d.writeReg(MODE1, d.buf[:1])
return err
}
// SetDrive configures PWM output connection in MODE2 register.
// false: The 16 LEDn outputs are configured with an open-drain structure.
// true: The 16 LEDn outputs are configured with a totem pole structure.
func (d Dev) SetDrive(outdrv bool) error {
err := d.readReg(MODE2, d.buf[:1])
if err != nil {
return err
}
if outdrv {
d.buf[0] |= OUTDRV
} else {
d.buf[0] &^= OUTDRV
}
return d.writeReg(MODE2, d.buf[:1])
}
// Sleep sets/unsets SLEEP bit in MODE1.
// if sleepEnabled
// Stops PWM. Allows writing to PRE_SCALE register.
// else
// wakes PCA9685. Resumes PWM.
func (d Dev) Sleep(sleepEnabled bool) error {
err := d.readReg(MODE1, d.buf[:1])
if err != nil {
return err
}
if sleepEnabled {
d.buf[0] |= SLEEP
return d.writeReg(MODE1, d.buf[:1])
}
d.buf[0] &^= SLEEP
err = d.writeReg(MODE1, d.buf[:1])
// It takes 500μs max for the oscillator to be up and running once SLEEP bit
// has been set to logic 0. Timings on LEDn outputs are not guaranteed if PWM
// control registers are accessed within the 500μs window.
// There is no start-up delay required when using the EXTCLK pin as the PWM clock.
time.Sleep(1000 * time.Microsecond) // Requested by datasheet.
return err
}
// SetInverting inverts ALL PCA9685 PWMs. The channel argument merely implements PWM interface.
//
// Without inverting, a 25% duty cycle would mean the output is high for 25% of
// the time and low for the rest. Inverting flips the output as if a NOT gate
// was placed at the output, meaning that the output would be 25% low and 75%
// high with a duty cycle of 25%.
func (d Dev) SetInverting(_ uint8, inverting bool) error {
err := d.readReg(MODE2, d.buf[:1])
if err != nil {
return err
}
if inverting {
d.buf[0] |= INVRT
} else {
d.buf[0] &^= INVRT
}
return d.writeReg(MODE2, d.buf[:1])
}
// SetPhased sets PWM on and off mark.
// The ON time, which is programmable, will be the time the LED output
// will be asserted and the OFF time, which is also programmable, will be
// the time when the LED output will be negated.
// In this way, the phase shift becomes completely programmable.
// The resolution for the phase shift is 14096 of the target frequency.
func (d Dev) SetPhased(channel uint8, on, off uint32) {
binary.LittleEndian.PutUint16(d.buf[:2], uint16(on)&maxtop)
binary.LittleEndian.PutUint16(d.buf[2:4], uint16(off)&maxtop)
onLReg, _, _, _ := LED(channel)
d.writeReg(onLReg, d.buf[:4])
}
+69
View File
@@ -0,0 +1,69 @@
package pca9685
// Software reset addresses for generic i2c implementation
// and PCA specific SWRST. See section 7.6
const (
I2CSWRSTADR = 0x0
PCA9685SWRSTBYTE = 0b0000_0110 // 0x06
)
// Registries with nomenclature as seen in manual
const (
SUBADR1 uint8 = 0x2
SUBADR2 uint8 = 0x3
SUBADR3 uint8 = 0x4
MODE1 uint8 = 0x0
MODE2 uint8 = 0x1
ALLCALLADR uint8 = 0x05
SWRESET uint8 = 0b0000_0011
// Start of LED registries. corresponds to LED0_ON_L register.
// Use LED function to get registries of a specific PWM channel register.
LEDSTART = 0x06
)
// MODE1
const (
RESET byte = 0b1000_0000
EXTCLK byte = 0b0100_0000
AI byte = 0b0010_0000
SLEEP byte = 0b0001_0000
defaultMODE1Value byte = 0b0001_0001
)
// MODE2
const (
OUTDRV = 1 << 2
INVRT = 1 << 4
)
// LED channels from 0-15. Returns 4 registries associated with
// the channel PWM signal. Channel 250 (0xFA) gives ALL_LED registers.
// The L suffix represents the 8 LSB of 12 bit timing, the H suffix represents
// the 4 MSB of the timing. This way you have 0-4095 timing options. ON or OFF
// will be a number between these two or will be simply ON or OFF (fifth bit of H registry).
//
// The SetPWM implementation in this library does not do phase-shifting so OFF will always
// happen at time stamp 0. ON will solely decide duty cycle.
func LED(ch uint8) (ONL, ONH, OFFL, OFFH uint8) {
switch {
case ch == ALLLED:
return ALLLED, ALLLED + 1, ALLLED + 2, ALLLED + 3
case ch > 15:
panic("PWM channel out of range [0-15]")
default:
}
// 4 registries per channel. Starts at 6
onLReg := LEDSTART + 4*ch
return onLReg, onLReg + 1, onLReg + 2, onLReg + 3
}
const (
// ALLLED is channel that selects registries to control all leds. Use with function LED
ALLLED = 0xfa
// PRESCALE Prescaling byte
PRESCALE byte = 0xFE
)
+25
View File
@@ -0,0 +1,25 @@
package rtl8720dn
import (
"time"
"tinygo.org/x/drivers/net"
)
func (r *RTL8720DN) ConnectToAccessPoint(ssid, pass string, timeout time.Duration) error {
if len(ssid) == 0 {
return net.ErrWiFiMissingSSID
}
return r.ConnectToAP(ssid, pass)
}
func (r *RTL8720DN) Disconnect() error {
_, err := r.Rpc_wifi_disconnect()
return err
}
func (r *RTL8720DN) GetClientIP() (string, error) {
ip, _, _, err := r.GetIP()
return ip.String(), err
}
+11
View File
@@ -0,0 +1,11 @@
package shtc3
// Constants used for I2C.
const (
SHTC3_ADDRESS = 0x70
SHTC3_CMD_WAKEUP = "\x35\x17" // Wake up
SHTC3_CMD_MEASURE_HP = "\x7C\xA2" // Read sensor in high power mode with clock stretching
SHTC3_CMD_SLEEP = "\xB0\x98" // Sleep
SHTC3_CMD_SOFT_RESET = "\x80\x5D" // Soft Reset
)
+79
View File
@@ -0,0 +1,79 @@
// Package shtc3 provides a driver for the SHTC3 digital humidity sensor
// series by Sensirion.
//
// Datasheet:
// https://www.sensirion.com/fileadmin/user_upload/customers/sensirion/Dokumente/2_Humidity_Sensors/Datasheets/Sensirion_Humidity_Sensors_SHTC3_Datasheet.pdf
//
package shtc3 // import "tinygo.org/x/drivers/shtc3"
import (
"time"
"tinygo.org/x/drivers"
)
// Device wraps an I2C connection to a SHT31 device.
type Device struct {
bus drivers.I2C
}
// New creates a new SHTC3 connection. The I2C bus must already be
// configured.
//
// This function only creates the Device object, it does not initialize the device.
// You must call Configure() first in order to use the device itself.
func New(bus drivers.I2C) Device {
return Device{
bus: bus,
}
}
// Read returns the temperature in celsius milli degrees (°C/1000).
func (d *Device) ReadTemperature() (tempMilliCelsius int32, err error) {
tempMilliCelsius, _, err = d.ReadTemperatureHumidity()
return tempMilliCelsius, err
}
// Read returns the relative humidity in hundredths of a percent.
func (d *Device) ReadHumidity() (relativeHumidity int16, err error) {
_, relativeHumidity, err = d.ReadTemperatureHumidity()
return relativeHumidity, err
}
// Read returns both the temperature and relative humidity.
func (d *Device) ReadTemperatureHumidity() (tempMilliCelsius int32, relativeHumidity int16, err error) {
var rawTemp, rawHum, errx = d.rawReadings()
if errx != nil {
err = errx
return
}
tempMilliCelsius = ((21875 * int32(rawTemp)) >> 13) - 45000
relativeHumidity = int16((1250 * int32(rawHum)) >> 13)
return tempMilliCelsius, relativeHumidity, err
}
// rawReadings returns the sensor's raw values of the temperature and humidity
func (d *Device) rawReadings() (uint16, uint16, error) {
var data [6]byte
d.bus.Tx(SHTC3_ADDRESS, []byte(SHTC3_CMD_MEASURE_HP), data[:])
// ignore crc for now
return readUint(data[0], data[1]), readUint(data[3], data[4]), nil
}
// WakeUp makes device leave sleep mode
func (d *Device) WakeUp() error {
d.bus.Tx(SHTC3_ADDRESS, []byte(SHTC3_CMD_WAKEUP), nil)
time.Sleep(1 * time.Millisecond)
return nil
}
// Sleep makes device go to sleep
func (d *Device) Sleep() error {
d.bus.Tx(SHTC3_ADDRESS, []byte(SHTC3_CMD_SLEEP), nil)
return nil
}
// readUint converts two bytes to uint16
func readUint(msb byte, lsb byte) uint16 {
return (uint16(msb) << 8) | uint16(lsb)
}
+335
View File
@@ -0,0 +1,335 @@
package sx126x
const (
// SX126X physical layer properties
SX126X_FREQUENCY_STEP_SIZE = 0.9536743164
SX126X_MAX_PACKET_LENGTH = 255
SX126X_CRYSTAL_FREQ = 32.0
SX126X_DIV_EXPONENT = 25
// SX126X SPI commands
// operational modes commands
SX126X_CMD_NOP = 0x00
SX126X_CMD_SET_SLEEP = 0x84
SX126X_CMD_SET_STANDBY = 0x80
SX126X_CMD_SET_FS = 0xC1
SX126X_CMD_SET_TX = 0x83
SX126X_CMD_SET_RX = 0x82
SX126X_CMD_STOP_TIMER_ON_PREAMBLE = 0x9F
SX126X_CMD_SET_RX_DUTY_CYCLE = 0x94
SX126X_CMD_SET_CAD = 0xC5
SX126X_CMD_SET_TX_CONTINUOUS_WAVE = 0xD1
SX126X_CMD_SET_TX_INFINITE_PREAMBLE = 0xD2
SX126X_CMD_SET_REGULATOR_MODE = 0x96
SX126X_CMD_CALIBRATE = 0x89
SX126X_CMD_CALIBRATE_IMAGE = 0x98
SX126X_CMD_SET_PA_CONFIG = 0x95
SX126X_CMD_SET_RX_TX_FALLBACK_MODE = 0x93
// register and buffer access commands
SX126X_CMD_WRITE_REGISTER = 0x0D
SX126X_CMD_READ_REGISTER = 0x1D
SX126X_CMD_WRITE_BUFFER = 0x0E
SX126X_CMD_READ_BUFFER = 0x1E
// DIO and IRQ control
SX126X_CMD_SET_DIO_IRQ_PARAMS = 0x08
SX126X_CMD_GET_IRQ_STATUS = 0x12
SX126X_CMD_CLEAR_IRQ_STATUS = 0x02
SX126X_CMD_SET_DIO2_AS_RF_SWITCH_CTRL = 0x9D
SX126X_CMD_SET_DIO3_AS_TCXO_CTRL = 0x97
// RF, modulation and packet commands
SX126X_CMD_SET_RF_FREQUENCY = 0x86
SX126X_CMD_SET_PACKET_TYPE = 0x8A
SX126X_CMD_GET_PACKET_TYPE = 0x11
SX126X_CMD_SET_TX_PARAMS = 0x8E
SX126X_CMD_SET_MODULATION_PARAMS = 0x8B
SX126X_CMD_SET_PACKET_PARAMS = 0x8C
SX126X_CMD_SET_CAD_PARAMS = 0x88
SX126X_CMD_SET_BUFFER_BASE_ADDRESS = 0x8F
SX126X_CMD_SET_LORA_SYMB_NUM_TIMEOUT = 0x0A
// status commands
SX126X_CMD_GET_STATUS = 0xC0
SX126X_CMD_GET_RSSI_INST = 0x15
SX126X_CMD_GET_RX_BUFFER_STATUS = 0x13
SX126X_CMD_GET_PACKET_STATUS = 0x14
SX126X_CMD_GET_DEVICE_ERRORS = 0x17
SX126X_CMD_CLEAR_DEVICE_ERRORS = 0x07
SX126X_CMD_GET_STATS = 0x10
SX126X_CMD_RESET_STATS = 0x00
// SX126X register map
SX126X_REG_WHITENING_INITIAL_MSB = 0x06B8
SX126X_REG_WHITENING_INITIAL_LSB = 0x06B9
SX126X_REG_CRC_INITIAL_MSB = 0x06BC
SX126X_REG_CRC_INITIAL_LSB = 0x06BD
SX126X_REG_CRC_POLYNOMIAL_MSB = 0x06BE
SX126X_REG_CRC_POLYNOMIAL_LSB = 0x06BF
SX126X_REG_SYNC_WORD_0 = 0x06C0
SX126X_REG_SYNC_WORD_1 = 0x06C1
SX126X_REG_SYNC_WORD_2 = 0x06C2
SX126X_REG_SYNC_WORD_3 = 0x06C3
SX126X_REG_SYNC_WORD_4 = 0x06C4
SX126X_REG_SYNC_WORD_5 = 0x06C5
SX126X_REG_SYNC_WORD_6 = 0x06C6
SX126X_REG_SYNC_WORD_7 = 0x06C7
SX126X_REG_NODE_ADDRESS = 0x06CD
SX126X_REG_BROADCAST_ADDRESS = 0x06CE
SX126X_REG_LORA_SYNC_WORD_MSB = 0x0740
SX126X_REG_LORA_SYNC_WORD_LSB = 0x0741
SX126X_REG_RANDOM_NUMBER_0 = 0x0819
SX126X_REG_RANDOM_NUMBER_1 = 0x081A
SX126X_REG_RANDOM_NUMBER_2 = 0x081B
SX126X_REG_RANDOM_NUMBER_3 = 0x081C
SX126X_REG_RX_GAIN = 0x08AC
SX126X_REG_OCP_CONFIGURATION = 0x08E7
SX126X_REG_XTA_TRIM = 0x0911
SX126X_REG_XTB_TRIM = 0x0912
// undocumented registers
SX126X_REG_SENSITIVITY_CONFIG = 0x0889 // SX1268 datasheet v1.1, section 15.1
SX126X_REG_TX_CLAMP_CONFIG = 0x08D8 // SX1268 datasheet v1.1, section 15.2
SX126X_REG_RTC_STOP = 0x0920 // SX1268 datasheet v1.1, section 15.3
SX126X_REG_RTC_EVENT = 0x0944 // SX1268 datasheet v1.1, section 15.3
SX126X_REG_IQ_CONFIG = 0x0736 // SX1268 datasheet v1.1, section 15.4
SX126X_REG_RX_GAIN_RETENTION_0 = 0x029F // SX1268 datasheet v1.1, section 9.6
SX126X_REG_RX_GAIN_RETENTION_1 = 0x02A0 // SX1268 datasheet v1.1, section 9.6
SX126X_REG_RX_GAIN_RETENTION_2 = 0x02A1 // SX1268 datasheet v1.1, section 9.6
// SX126X SPI command variables
//SX126X_CMD_SET_SLEEP MSB LSB DESCRIPTION
SX126X_SLEEP_START_COLD = 0b00000000 // 2 2 sleep mode: cold start, configuration is lost (default)
SX126X_SLEEP_START_WARM = 0b00000100 // 2 2 warm start, configuration is retained
SX126X_SLEEP_RTC_OFF = 0b00000000 // 0 0 wake on RTC timeout: disabled
SX126X_SLEEP_RTC_ON = 0b00000001 // 0 0 enabled
//SX126X_CMD_SET_STANDBY
SX126X_STANDBY_RC = 0x00 // 7 0 standby mode: 13 MHz RC oscillator
SX126X_STANDBY_XOSC = 0x01 // 7 0 32 MHz crystal oscillator
//SX126X_CMD_SET_RX
SX126X_RX_TIMEOUT_NONE = 0x000000 // 23 0 Rx timeout duration: no timeout (Rx single mode)
SX126X_RX_TIMEOUT_INF = 0xFFFFFF // 23 0 infinite (Rx continuous mode)
//SX126X_CMD_SET_TX
SX126X_TX_TIMEOUT_NONE = 0x000000 // 23 0 Tx timeout duration: no timeout (Tx single mode)
//SX126X_CMD_STOP_TIMER_ON_PREAMBLE
SX126X_STOP_ON_PREAMBLE_OFF = 0x00 // 7 0 stop timer on: sync word or header (default)
SX126X_STOP_ON_PREAMBLE_ON = 0x01 // 7 0 preamble detection
//SX126X_CMD_SET_REGULATOR_MODE
SX126X_REGULATOR_LDO = 0x00 // 7 0 set regulator mode: LDO (default)
SX126X_REGULATOR_DC_DC = 0x01 // 7 0 DC-DC
//SX126X_CMD_CALIBRATE
SX126X_CALIBRATE_IMAGE_OFF = 0b00000000 // 6 6 image calibration: disabled
SX126X_CALIBRATE_IMAGE_ON = 0b01000000 // 6 6 enabled
SX126X_CALIBRATE_ADC_BULK_P_OFF = 0b00000000 // 5 5 ADC bulk P calibration: disabled
SX126X_CALIBRATE_ADC_BULK_P_ON = 0b00100000 // 5 5 enabled
SX126X_CALIBRATE_ADC_BULK_N_OFF = 0b00000000 // 4 4 ADC bulk N calibration: disabled
SX126X_CALIBRATE_ADC_BULK_N_ON = 0b00010000 // 4 4 enabled
SX126X_CALIBRATE_ADC_PULSE_OFF = 0b00000000 // 3 3 ADC pulse calibration: disabled
SX126X_CALIBRATE_ADC_PULSE_ON = 0b00001000 // 3 3 enabled
SX126X_CALIBRATE_PLL_OFF = 0b00000000 // 2 2 PLL calibration: disabled
SX126X_CALIBRATE_PLL_ON = 0b00000100 // 2 2 enabled
SX126X_CALIBRATE_RC13M_OFF = 0b00000000 // 1 1 13 MHz RC osc. calibration: disabled
SX126X_CALIBRATE_RC13M_ON = 0b00000010 // 1 1 enabled
SX126X_CALIBRATE_RC64K_OFF = 0b00000000 // 0 0 64 kHz RC osc. calibration: disabled
SX126X_CALIBRATE_RC64K_ON = 0b00000001 // 0 0 enabled
SX126X_CALIBRATE_ALL = 0b01111111 // 6 0 calibrate all blocks
//SX126X_CMD_CALIBRATE_IMAGE
SX126X_CAL_IMG_430_MHZ_1 = 0x6B
SX126X_CAL_IMG_430_MHZ_2 = 0x6F
SX126X_CAL_IMG_470_MHZ_1 = 0x75
SX126X_CAL_IMG_470_MHZ_2 = 0x81
SX126X_CAL_IMG_779_MHZ_1 = 0xC1
SX126X_CAL_IMG_779_MHZ_2 = 0xC5
SX126X_CAL_IMG_863_MHZ_1 = 0xD7
SX126X_CAL_IMG_863_MHZ_2 = 0xDB
SX126X_CAL_IMG_902_MHZ_1 = 0xE1
SX126X_CAL_IMG_902_MHZ_2 = 0xE9
//SX126X_CMD_SET_PA_CONFIG
SX126X_PA_CONFIG_HP_MAX = 0x07
SX126X_PA_CONFIG_PA_LUT = 0x01
SX126X_PA_CONFIG_SX1262_8 = 0x00
//SX126X_CMD_SET_RX_TX_FALLBACK_MODE
SX126X_RX_TX_FALLBACK_MODE_FS = 0x40 // 7 0 after Rx/Tx go to: FS mode
SX126X_RX_TX_FALLBACK_MODE_STDBY_XOSC = 0x30 // 7 0 standby with crystal oscillator
SX126X_RX_TX_FALLBACK_MODE_STDBY_RC = 0x20 // 7 0 standby with RC oscillator (default)
//SX126X_CMD_SET_DIO_IRQ_PARAMS
SX126X_IRQ_TIMEOUT = 0b1000000000 // 9 9 Rx or Tx timeout
SX126X_IRQ_CAD_DETECTED = 0b0100000000 // 8 8 channel activity detected
SX126X_IRQ_CAD_DONE = 0b0010000000 // 7 7 channel activity detection finished
SX126X_IRQ_CRC_ERR = 0b0001000000 // 6 6 wrong CRC received
SX126X_IRQ_HEADER_ERR = 0b0000100000 // 5 5 LoRa header CRC error
SX126X_IRQ_HEADER_VALID = 0b0000010000 // 4 4 valid LoRa header received
SX126X_IRQ_SYNC_WORD_VALID = 0b0000001000 // 3 3 valid sync word detected
SX126X_IRQ_PREAMBLE_DETECTED = 0b0000000100 // 2 2 preamble detected
SX126X_IRQ_RX_DONE = 0b0000000010 // 1 1 packet received
SX126X_IRQ_TX_DONE = 0b0000000001 // 0 0 packet transmission completed
SX126X_IRQ_ALL = 0b1111111111 // 9 0 all interrupts
SX126X_IRQ_NONE = 0b0000000000 // 9 0 no interrupts
//SX126X_CMD_SET_DIO2_AS_RF_SWITCH_CTRL
SX126X_DIO2_AS_IRQ = 0x00 // 7 0 DIO2 configuration: IRQ
SX126X_DIO2_AS_RF_SWITCH = 0x01 // 7 0 RF switch control
//SX126X_CMD_SET_DIO3_AS_TCXO_CTRL
SX126X_DIO3_OUTPUT_1_6 = 0x00 // 7 0 DIO3 voltage output for TCXO: 1.6 V
SX126X_DIO3_OUTPUT_1_7 = 0x01 // 7 0 1.7 V
SX126X_DIO3_OUTPUT_1_8 = 0x02 // 7 0 1.8 V
SX126X_DIO3_OUTPUT_2_2 = 0x03 // 7 0 2.2 V
SX126X_DIO3_OUTPUT_2_4 = 0x04 // 7 0 2.4 V
SX126X_DIO3_OUTPUT_2_7 = 0x05 // 7 0 2.7 V
SX126X_DIO3_OUTPUT_3_0 = 0x06 // 7 0 3.0 V
SX126X_DIO3_OUTPUT_3_3 = 0x07 // 7 0 3.3 V
//SX126X_CMD_SET_PACKET_TYPE
SX126X_PACKET_TYPE_GFSK = 0x00 // 7 0 packet type: GFSK
SX126X_PACKET_TYPE_LORA = 0x01 // 7 0 LoRa
//SX126X_CMD_SET_TX_PARAMS
SX126X_PA_RAMP_10U = 0x00 // 7 0 ramp time: 10 us
SX126X_PA_RAMP_20U = 0x01 // 7 0 20 us
SX126X_PA_RAMP_40U = 0x02 // 7 0 40 us
SX126X_PA_RAMP_80U = 0x03 // 7 0 80 us
SX126X_PA_RAMP_200U = 0x04 // 7 0 200 us
SX126X_PA_RAMP_800U = 0x05 // 7 0 800 us
SX126X_PA_RAMP_1700U = 0x06 // 7 0 1700 us
SX126X_PA_RAMP_3400U = 0x07 // 7 0 3400 us
//SX126X_CMD_SET_MODULATION_PARAMS
SX126X_GFSK_FILTER_NONE = 0x00 // 7 0 GFSK filter: none
SX126X_GFSK_FILTER_GAUSS_0_3 = 0x08 // 7 0 Gaussian, BT = 0.3
SX126X_GFSK_FILTER_GAUSS_0_5 = 0x09 // 7 0 Gaussian, BT = 0.5
SX126X_GFSK_FILTER_GAUSS_0_7 = 0x0A // 7 0 Gaussian, BT = 0.7
SX126X_GFSK_FILTER_GAUSS_1 = 0x0B // 7 0 Gaussian, BT = 1
SX126X_GFSK_RX_BW_4_8 = 0x1F // 7 0 GFSK Rx bandwidth: 4.8 kHz
SX126X_GFSK_RX_BW_5_8 = 0x17 // 7 0 5.8 kHz
SX126X_GFSK_RX_BW_7_3 = 0x0F // 7 0 7.3 kHz
SX126X_GFSK_RX_BW_9_7 = 0x1E // 7 0 9.7 kHz
SX126X_GFSK_RX_BW_11_7 = 0x16 // 7 0 11.7 kHz
SX126X_GFSK_RX_BW_14_6 = 0x0E // 7 0 14.6 kHz
SX126X_GFSK_RX_BW_19_5 = 0x1D // 7 0 19.5 kHz
SX126X_GFSK_RX_BW_23_4 = 0x15 // 7 0 23.4 kHz
SX126X_GFSK_RX_BW_29_3 = 0x0D // 7 0 29.3 kHz
SX126X_GFSK_RX_BW_39_0 = 0x1C // 7 0 39.0 kHz
SX126X_GFSK_RX_BW_46_9 = 0x14 // 7 0 46.9 kHz
SX126X_GFSK_RX_BW_58_6 = 0x0C // 7 0 58.6 kHz
SX126X_GFSK_RX_BW_78_2 = 0x1B // 7 0 78.2 kHz
SX126X_GFSK_RX_BW_93_8 = 0x13 // 7 0 93.8 kHz
SX126X_GFSK_RX_BW_117_3 = 0x0B // 7 0 117.3 kHz
SX126X_GFSK_RX_BW_156_2 = 0x1A // 7 0 156.2 kHz
SX126X_GFSK_RX_BW_187_2 = 0x12 // 7 0 187.2 kHz
SX126X_GFSK_RX_BW_234_3 = 0x0A // 7 0 234.3 kHz
SX126X_GFSK_RX_BW_312_0 = 0x19 // 7 0 312.0 kHz
SX126X_GFSK_RX_BW_373_6 = 0x11 // 7 0 373.6 kHz
SX126X_GFSK_RX_BW_467_0 = 0x09 // 7 0 467.0 kHz
SX126X_LORA_BW_7_8 = 0x00 // 7 0 LoRa bandwidth: 7.8 kHz
SX126X_LORA_BW_10_4 = 0x08 // 7 0 10.4 kHz
SX126X_LORA_BW_15_6 = 0x01 // 7 0 15.6 kHz
SX126X_LORA_BW_20_8 = 0x09 // 7 0 20.8 kHz
SX126X_LORA_BW_31_25 = 0x02 // 7 0 31.25 kHz
SX126X_LORA_BW_41_7 = 0x0A // 7 0 41.7 kHz
SX126X_LORA_BW_62_5 = 0x03 // 7 0 62.5 kHz
SX126X_LORA_BW_125_0 = 0x04 // 7 0 125.0 kHz
SX126X_LORA_BW_250_0 = 0x05 // 7 0 250.0 kHz
SX126X_LORA_BW_500_0 = 0x06 // 7 0 500.0 kHz
SX126X_LORA_CR_4_5 = 0x01 // 7 0 LoRa coding rate: 4/5
SX126X_LORA_CR_4_6 = 0x02 // 7 0 4/6
SX126X_LORA_CR_4_7 = 0x03 // 7 0 4/7
SX126X_LORA_CR_4_8 = 0x04 // 7 0 4/8
SX126X_LORA_LOW_DATA_RATE_OPTIMIZE_OFF = 0x00 // 7 0 LoRa low data rate optimization: disabled
SX126X_LORA_LOW_DATA_RATE_OPTIMIZE_ON = 0x01 // 7 0 enabled
//SX126X_CMD_SET_PACKET_PARAMS
SX126X_GFSK_PREAMBLE_DETECT_OFF = 0x00 // 7 0 GFSK minimum preamble length before reception starts: detector disabled
SX126X_GFSK_PREAMBLE_DETECT_8 = 0x04 // 7 0 8 bits
SX126X_GFSK_PREAMBLE_DETECT_16 = 0x05 // 7 0 16 bits
SX126X_GFSK_PREAMBLE_DETECT_24 = 0x06 // 7 0 24 bits
SX126X_GFSK_PREAMBLE_DETECT_32 = 0x07 // 7 0 32 bits
SX126X_GFSK_ADDRESS_FILT_OFF = 0x00 // 7 0 GFSK address filtering: disabled
SX126X_GFSK_ADDRESS_FILT_NODE = 0x01 // 7 0 node only
SX126X_GFSK_ADDRESS_FILT_NODE_BROADCAST = 0x02 // 7 0 node and broadcast
SX126X_GFSK_PACKET_FIXED = 0x00 // 7 0 GFSK packet type: fixed (payload length known in advance to both sides)
SX126X_GFSK_PACKET_VARIABLE = 0x01 // 7 0 variable (payload length added to packet)
SX126X_GFSK_CRC_OFF = 0x01 // 7 0 GFSK packet CRC: disabled
SX126X_GFSK_CRC_1_BYTE = 0x00 // 7 0 1 byte
SX126X_GFSK_CRC_2_BYTE = 0x02 // 7 0 2 byte
SX126X_GFSK_CRC_1_BYTE_INV = 0x04 // 7 0 1 byte, inverted
SX126X_GFSK_CRC_2_BYTE_INV = 0x06 // 7 0 2 byte, inverted
SX126X_GFSK_WHITENING_OFF = 0x00 // 7 0 GFSK data whitening: disabled
SX126X_GFSK_WHITENING_ON = 0x01 // 7 0 enabled
SX126X_LORA_HEADER_EXPLICIT = 0x00 // 7 0 LoRa header mode: explicit
SX126X_LORA_HEADER_IMPLICIT = 0x01 // 7 0 implicit
SX126X_LORA_CRC_OFF = 0x00 // 7 0 LoRa CRC mode: disabled
SX126X_LORA_CRC_ON = 0x01 // 7 0 enabled
SX126X_LORA_IQ_STANDARD = 0x00 // 7 0 LoRa IQ setup: standard
SX126X_LORA_IQ_INVERTED = 0x01 // 7 0 inverted
//SX126X_CMD_SET_CAD_PARAMS
SX126X_CAD_ON_1_SYMB = 0x00 // 7 0 number of symbols used for CAD: 1
SX126X_CAD_ON_2_SYMB = 0x01 // 7 0 2
SX126X_CAD_ON_4_SYMB = 0x02 // 7 0 4
SX126X_CAD_ON_8_SYMB = 0x03 // 7 0 8
SX126X_CAD_ON_16_SYMB = 0x04 // 7 0 16
SX126X_CAD_GOTO_STDBY = 0x00 // 7 0 after CAD is done, always go to STDBY_RC mode
SX126X_CAD_GOTO_RX = 0x01 // 7 0 after CAD is done, go to Rx mode if activity is detected
//SX126X_CMD_GET_STATUS
SX126X_STATUS_MODE_STDBY_RC = 0b00100000 // 6 4 current chip mode: STDBY_RC
SX126X_STATUS_MODE_STDBY_XOSC = 0b00110000 // 6 4 STDBY_XOSC
SX126X_STATUS_MODE_FS = 0b01000000 // 6 4 FS
SX126X_STATUS_MODE_RX = 0b01010000 // 6 4 RX
SX126X_STATUS_MODE_TX = 0b01100000 // 6 4 TX
SX126X_STATUS_DATA_AVAILABLE = 0b00000100 // 3 1 command status: packet received and data can be retrieved
SX126X_STATUS_CMD_TIMEOUT = 0b00000110 // 3 1 SPI command timed out
SX126X_STATUS_CMD_INVALID = 0b00001000 // 3 1 invalid SPI command
SX126X_STATUS_CMD_FAILED = 0b00001010 // 3 1 SPI command failed to execute
SX126X_STATUS_TX_DONE = 0b00001100 // 3 1 packet transmission done
SX126X_STATUS_SPI_FAILED = 0b11111111 // 7 0 SPI transaction failed
//SX126X_CMD_GET_PACKET_STATUS
SX126X_GFSK_RX_STATUS_PREAMBLE_ERR = 0b10000000 // 7 7 GFSK Rx status: preamble error
SX126X_GFSK_RX_STATUS_SYNC_ERR = 0b01000000 // 6 6 sync word error
SX126X_GFSK_RX_STATUS_ADRS_ERR = 0b00100000 // 5 5 address error
SX126X_GFSK_RX_STATUS_CRC_ERR = 0b00010000 // 4 4 CRC error
SX126X_GFSK_RX_STATUS_LENGTH_ERR = 0b00001000 // 3 3 length error
SX126X_GFSK_RX_STATUS_ABORT_ERR = 0b00000100 // 2 2 abort error
SX126X_GFSK_RX_STATUS_PACKET_RECEIVED = 0b00000010 // 2 2 packet received
SX126X_GFSK_RX_STATUS_PACKET_SENT = 0b00000001 // 2 2 packet sent
//SX126X_CMD_GET_DEVICE_ERRORS
SX126X_PA_RAMP_ERR = 0b100000000 // 8 8 device errors: PA ramping failed
SX126X_PLL_LOCK_ERR = 0b001000000 // 6 6 PLL failed to lock
SX126X_XOSC_START_ERR = 0b000100000 // 5 5 crystal oscillator failed to start
SX126X_IMG_CALIB_ERR = 0b000010000 // 4 4 image calibration failed
SX126X_ADC_CALIB_ERR = 0b000001000 // 3 3 ADC calibration failed
SX126X_PLL_CALIB_ERR = 0b000000100 // 2 2 PLL calibration failed
SX126X_RC13M_CALIB_ERR = 0b000000010 // 1 1 RC13M calibration failed
SX126X_RC64K_CALIB_ERR = 0b000000001 // 0 0 RC64K calibration failed
// SX126X SPI register variables
//SX126X_REG_LORA_SYNC_WORD_MSB + LSB
SX126X_SYNC_WORD_PUBLIC = 0x34 // actually 0x3444 NOTE: The low nibbles in each byte (0x_4_4) are masked out since apparently, they're reserved.
SX126X_SYNC_WORD_PRIVATE = 0x12 // actually 0x1424 You couldn't make this up if you tried.
SX126X_LORA_MAC_PUBLIC_SYNCWORD = 0x3444
SX126X_LORA_MAC_PRIVATE_SYNCWORD = 0x1424
SX126X_LORA_SF5 = 0x05
SX126X_LORA_SF6 = 0x06
SX126X_LORA_SF7 = 0x07
SX126X_LORA_SF8 = 0x08
SX126X_LORA_SF9 = 0x09
SX126X_LORA_SF10 = 0x0A
SX126X_LORA_SF11 = 0x0B
SX126X_LORA_SF12 = 0x0C
)
+85
View File
@@ -0,0 +1,85 @@
//go:build stm32wlx
// +build stm32wlx
package sx126x
import (
"device/stm32"
"errors"
"machine"
"tinygo.org/x/drivers"
)
// New creates a new SX126x connection.
func New(spi drivers.SPI) *Device {
c := make(chan RadioEvent, 10)
d := Device{
spi: spi,
radioEventChan: c,
}
if d.spi == machine.SPI3 {
d.SubGhzInit()
d.SetDeviceType(DEVICE_TYPE_SX1262)
} else {
panic("Driver only support SUBGHZSPI (SPI3) on stm32wlx targets")
}
return &d
}
//SpiSetNss Sets the NSS line
func (d *Device) SpiSetNss(state bool) {
if state {
stm32.PWR.SUBGHZSPICR.SetBits(stm32.PWR_SUBGHZSPICR_NSS)
} else {
stm32.PWR.SUBGHZSPICR.ClearBits(stm32.PWR_SUBGHZSPICR_NSS)
}
}
// WaitBusy sleep until all busy flags clears
func (d *Device) WaitBusy() error {
count := 100
var rfbusyms, rfbusys bool
for count > 0 {
rfbusyms = stm32.PWR.SR2.HasBits(stm32.PWR_SR2_RFBUSYMS)
rfbusys = stm32.PWR.SR2.HasBits(stm32.PWR_SR2_RFBUSYS)
if !(rfbusyms && rfbusys) {
return nil
}
count--
}
return errors.New("WaitBusy Timeout")
}
// SubGhzInit() configures internal SX1262's SPI bus.
func (d *Device) SubGhzInit() {
// Enable APB3 Periph clock and delay
stm32.RCC.APB3ENR.SetBits(stm32.RCC_APB3ENR_SUBGHZSPIEN)
_ = stm32.RCC.APB3ENR.Get()
// Disable radio reset and wait it's ready
stm32.RCC.CSR.ClearBits(stm32.RCC_CSR_RFRST)
for stm32.RCC.CSR.HasBits(stm32.RCC_CSR_RFRSTF) {
}
// Set NSS line low
stm32.PWR.SUBGHZSPICR.SetBits(stm32.PWR_SUBGHZSPICR_NSS)
// Enable radio busy wakeup from Standby for CPU
stm32.PWR.CR3.SetBits(stm32.PWR_CR3_EWRFBUSY)
// Clear busy flag
stm32.PWR.SCR.Set(stm32.PWR_SCR_CWRFBUSYF)
// Enable SUBGHZ Spi
// - /8 Prescaler
// - Software Slave Management (NSS)
// - FIFO Threshold and 8bit size
stm32.SPI3.CR1.ClearBits(stm32.SPI_CR1_SPE)
stm32.SPI3.CR1.Set(stm32.SPI_CR1_MSTR | stm32.SPI_CR1_SSI | (0b010 << 3) | stm32.SPI_CR1_SSM)
stm32.SPI3.CR2.Set(stm32.SPI_CR2_FRXTH | (0b111 << 8))
stm32.SPI3.CR1.SetBits(stm32.SPI_CR1_SPE)
}
+699
View File
@@ -0,0 +1,699 @@
// Package sx126x provides a driver for SX126x LoRa transceivers.
// Inspired from https://github.com/Lora-net/sx126x_driver/
package sx126x
import (
"errors"
"time"
"tinygo.org/x/drivers"
)
// SX126X radio transceiver RF_IN and RF_OUT may be connected
// to RF Switch. This interface allows the creation of struct
// that can drive the RF Switch (Used in Lora RX and Lora Tx)
type RFSwitch interface {
InitRFSwitch()
SetRfSwitchMode(mode int) error
}
const (
DEVICE_TYPE_SX1261 = iota
DEVICE_TYPE_SX1262 = iota
DEVICE_TYPE_SX1268 = iota
)
const (
RFSWITCH_RX = iota
RFSWITCH_TX_LP = iota
RFSWITCH_TX_HP = iota
)
const (
RadioEventRxDone = iota
RadioEventTxDone = iota
RadioEventTimeout = iota
RadioEventWatchdog = iota
RadioEventCrcError = iota
RadioEventUnhandled = iota
)
// RadioEvent are used for communicating in the radio Event Channel
type RadioEvent struct {
EventType int
IRQStatus uint16
EventData []byte
}
const (
PERIOD_PER_SEC = (uint32)(1000000 / 15.625) // SX1261 DS 13.1.4
SPI_BUFFER_SIZE = 256
)
// Device wraps an SPI connection to a SX127x device.
type Device struct {
spi drivers.SPI // SPI bus for module communication
radioEventChan chan RadioEvent // Channel for Receiving events
loraConf LoraConfig // Current Lora configuration
rfswitch RFSwitch // RF Switch, if any
deepSleep bool // Internal Sleep state
deviceType int // sx1261,sx1262,sx1268 (defaults sx1261)
spiBuffer [SPI_BUFFER_SIZE]uint8
}
// Config holds the LoRa configuration parameters
type LoraConfig struct {
Freq uint32 // Frequency
Cr uint8 // Coding Rate
Sf uint8 // Spread Factor
Bw uint8 // Bandwidth
Ldr uint8 // Low Data Rate
Preamble uint16 // PreambleLength
SyncWord uint16 // Sync Word
HeaderType uint8 // Header : Implicit/explicit
Crc uint8 // CRC : Yes/No
Iq uint8 // iq : Standard/inverted
LoraTxPowerDBm int8 // Tx power in Dbm
}
const (
SX126X_RTC_FREQ_IN_HZ uint32 = 64000
)
var (
errUndefinedLoraConf = errors.New("Undefined Lora configuration")
)
// --------------------------------------------------
// Helper functions
// --------------------------------------------------
// timeoutMsToRtcSteps converts Timeout (in ms) to RTC Steps
func timeoutMsToRtcSteps(timeoutMs uint32) uint32 {
r := uint32(timeoutMs * (SX126X_RTC_FREQ_IN_HZ / 1000))
return r
}
// --------------------------------------------------
// Channel and events
// --------------------------------------------------
//NewRadioEvent() returns a new RadioEvent that can be used in the RadioChannel
func NewRadioEvent(eType int, irqStatus uint16, eData []byte) RadioEvent {
r := RadioEvent{EventType: eType, IRQStatus: irqStatus, EventData: eData}
return r
}
// Get the RadioEvent channel of the device
func (d *Device) GetRadioEventChan() chan RadioEvent {
return d.radioEventChan
}
// Specify device type (SX1261/2/8)
func (d *Device) SetDeviceType(devType int) {
d.deviceType = devType
}
// SetRfSwitch let you define a custom RF Switch driver if needed
func (d *Device) SetRfSwitch(rfswitch RFSwitch) {
d.rfswitch = rfswitch
d.rfswitch.InitRFSwitch()
}
// --------------------------------------------------
// Operational modes functions
// --------------------------------------------------
// DetectDevice() tries to detect the radio module by changing SyncWord value
func (d *Device) DetectDevice() bool {
bak := d.GetSyncWord()
d.SetSyncWord(0xBEEF)
tmp := d.GetSyncWord()
if tmp != 0xBEEF {
return false
} else {
d.SetSyncWord(bak)
return true
}
}
// SetSleep sets the device in SLEEP mode with the lowest current consumption possible.
func (d *Device) SetSleep() {
d.ExecSetCommand(SX126X_CMD_SET_SLEEP, []uint8{SX126X_SLEEP_START_WARM | SX126X_SLEEP_RTC_OFF})
}
// SetStandby sets the device in a configuration mode which is at an intermediate level of consumption
func (d *Device) SetStandby() {
d.ExecSetCommand(SX126X_CMD_SET_STANDBY, []uint8{SX126X_STANDBY_RC})
}
// SetFs sets the device in frequency synthesis mode where the PLL is locked to the carrier frequency.
func (d *Device) SetFs() {
d.ExecSetCommand(SX126X_CMD_SET_FS, []uint8{})
}
// SetTxContinuousWave set device in test mode to generate a continuous wave (RF tone)
func (d *Device) SetTxContinuousWave() {
if d.rfswitch != nil {
d.rfswitch.SetRfSwitchMode(RFSWITCH_TX_HP)
}
d.ExecSetCommand(SX126X_CMD_SET_TX_CONTINUOUS_WAVE, []uint8{})
}
// SetTxContinuousPreamble set device in test mode to constantly modulate LoRa preamble symbols.
// Take care to initialize all Lora settings like it's done in LoraTx before calling this function
// If you don't init properly all the settings, it'll fail
func (d *Device) SetTxContinuousPreamble() {
if d.rfswitch != nil {
d.rfswitch.SetRfSwitchMode(RFSWITCH_TX_HP)
}
d.ExecSetCommand(SX126X_CMD_SET_TX_INFINITE_PREAMBLE, []uint8{})
}
// SetTx() sets the device in TX mode
// timeout is expressed in RTC Step unit (15uS)
// The device will stay in Tx until countdown or packet transmitted
// Value of 0x000000 will disable timer and device will stay TX
func (d *Device) SetTx(timeoutRtcStep uint32) {
var p [3]uint8
p[0] = uint8((timeoutRtcStep >> 16) & 0xFF)
p[1] = uint8((timeoutRtcStep >> 8) & 0xFF)
p[2] = uint8((timeoutRtcStep >> 0) & 0xFF)
d.ExecSetCommand(SX126X_CMD_SET_TX, p[:])
}
// SetRx() sets the device in RX mode
// timeout is expressed in RTC Step unit (15uS)
// Value of 0x000000 => No timeout. Rx Single mode.
// Value of 0xffffff => Rx Continuous mode
// Other values => Timeout active. The device remains in RX until countdown or packet received
func (d *Device) SetRx(timeoutRtcStep uint32) {
var p [3]uint8
p[0] = uint8(((timeoutRtcStep >> 16) & 0xFF))
p[1] = uint8(((timeoutRtcStep >> 8) & 0xFF))
p[2] = uint8(((timeoutRtcStep >> 0) & 0xFF))
d.ExecSetCommand(SX126X_CMD_SET_RX, p[:])
}
// StopTimerOnPreamble allows the user to select if the timer is stopped upon preamble detection of SyncWord / header detection.
func (d *Device) StopTimerOnPreamble(enable bool) {
var p [1]uint8
if enable {
p[0] = 1
} else {
p[0] = 0
}
d.ExecSetCommand(SX126X_CMD_STOP_TIMER_ON_PREAMBLE, p[:])
}
// SetRegulatorMode sets the regulator more (depends on hardware implementation)
func (d *Device) SetRegulatorMode(mode uint8) {
p := []uint8{mode}
d.ExecSetCommand(SX126X_CMD_SET_REGULATOR_MODE, p[:])
}
// Calibrate starts the calibration of a block defined by calibParam
func (d *Device) Calibrate(calibParam uint8) {
p := []uint8{calibParam}
d.ExecSetCommand(SX126X_CMD_CALIBRATE, p[:])
}
// CalibrateImage calibrates the image rejection of the device for the device operating
func (d *Device) CalibrateImage(freq uint32) {
var calFreq [2]uint8
if freq > 900000000 {
calFreq[0] = 0xE1
calFreq[1] = 0xE9
} else if freq > 850000000 {
calFreq[0] = 0xD7
calFreq[1] = 0xD8
} else if freq > 770000000 {
calFreq[0] = 0xC1
calFreq[1] = 0xC5
} else if freq > 460000000 {
calFreq[0] = 0x75
calFreq[1] = 0x81
} else if freq > 425000000 {
calFreq[0] = 0x6B
calFreq[1] = 0x6F
}
d.ExecSetCommand(SX126X_CMD_CALIBRATE_IMAGE, calFreq[:])
}
// SetPaConfig sets the Power Amplifier configuration
// deviceSel: 0 for SX1262, 1 for SX1261
func (d *Device) SetPaConfig(paDutyCycle, hpMax, deviceSel, paLut uint8) {
var p [4]uint8
p[0] = paDutyCycle
p[1] = hpMax
p[2] = deviceSel
p[3] = paLut
d.ExecSetCommand(SX126X_CMD_SET_PA_CONFIG, p[:])
}
// SetRxTxFallbackMode defines into which mode the chip goes after a successful transmission or after a packet reception.
func (d *Device) SetRxTxFallbackMode(fallbackMode uint8) {
d.ExecSetCommand(SX126X_CMD_SET_RX_TX_FALLBACK_MODE, []uint8{fallbackMode})
}
// --------------------------------------------------
// Registers and Buffers
// --------------------------------------------------
// ReadRegister reads register value
func (d *Device) ReadRegister(addr, size uint16) ([]uint8, error) {
d.CheckDeviceReady()
d.SpiSetNss(false)
// Send command
cmd := []uint8{SX126X_CMD_READ_REGISTER, uint8((addr & 0xFF00) >> 8), uint8(addr & 0x00FF), 0x00}
d.spi.Tx(cmd, nil)
ret := d.spiBuffer[0:size]
d.spi.Tx(nil, ret)
d.SpiSetNss(true)
d.WaitBusy()
return ret, nil
}
// WriteRegister writes value to register
func (d *Device) WriteRegister(addr uint16, data []uint8) {
d.CheckDeviceReady()
d.SpiSetNss(false)
cmd := []uint8{SX126X_CMD_WRITE_REGISTER, uint8((addr & 0xFF00) >> 8), uint8(addr & 0x00FF)}
d.spi.Tx(append(cmd, data...), nil)
d.SpiSetNss(true)
d.WaitBusy()
}
// WriteBuffer write data from current buffer position
func (d *Device) WriteBuffer(data []uint8) {
p := []uint8{0}
p = append(p, data...)
d.ExecSetCommand(SX126X_CMD_WRITE_BUFFER, p)
}
// ReadBuffer Reads size bytes from current buffer position
func (d *Device) ReadBuffer(size uint8) []uint8 {
ret := d.ExecGetCommand(SX126X_CMD_READ_BUFFER, size)
return ret
}
// --------------------------------------------------
// DIO and IRQ
// --------------------------------------------------
// SetDioIrqParams configures DIO Irq
func (d *Device) SetDioIrqParams(irqMask, dio1Mask, dio2Mask, dio3Mask uint16) {
var p [8]uint8
p[0] = uint8((irqMask >> 8) & 0xFF)
p[1] = uint8(irqMask & 0xFF)
p[2] = uint8((dio1Mask >> 8) & 0xFF)
p[3] = uint8(dio1Mask & 0xFF)
p[4] = uint8((dio2Mask >> 8) & 0xFF)
p[5] = uint8(dio2Mask & 0xFF)
p[6] = uint8((dio3Mask >> 8) & 0xFF)
p[7] = uint8(dio3Mask & 0xFF)
d.ExecSetCommand(SX126X_CMD_SET_DIO_IRQ_PARAMS, p[:])
}
// GetIrqStatus returns IRQ status
func (d *Device) GetIrqStatus() (irqStatus uint16) {
r := d.ExecGetCommand(SX126X_CMD_GET_IRQ_STATUS, 2)
ret := (uint16(r[0]) << 8) | uint16(r[1])
return ret
}
// ClearIrqStatus clears IRQ flags
func (d *Device) ClearIrqStatus(clearIrqParams uint16) {
var p [2]uint8
p[0] = uint8((clearIrqParams >> 8) & 0xFF)
p[1] = uint8(clearIrqParams & 0xFF)
d.ExecSetCommand(SX126X_CMD_CLEAR_IRQ_STATUS, p[:])
}
// --------------------------------------------------
// Communication Status Information
// --------------------------------------------------
// GetStatus returns radio status(13.5.1)
func (d *Device) GetStatus() (radioStatus uint8) {
r := d.ExecGetCommand(SX126X_CMD_GET_STATUS, 1)
return r[0]
}
// GetRxBufferStatus returns the length of the last received packet (PayloadLengthRx)
// and the address of the first byte received (RxStartBufferPointer). (13.5.2)
func (d *Device) GetRxBufferStatus() (payloadLengthRx uint8, rxStartBufferPointer uint8) {
r := d.ExecGetCommand(SX126X_CMD_GET_RX_BUFFER_STATUS, 2)
return r[0], r[1]
}
// GetPackeType returns current Packet Type (13.4.3)
func (d *Device) GetPacketType() (packetType uint8) {
r := d.ExecGetCommand(SX126X_CMD_GET_PACKET_TYPE, 1)
return r[0]
}
// GetDeviceErrors returns current Device Errors
func (d *Device) GetDeviceErrors() uint16 {
r := d.ExecGetCommand(SX126X_CMD_GET_DEVICE_ERRORS, 2)
ret := uint16(r[0]<<8 + r[1])
return ret
}
// ClearDeviceErrors clears device Errors
func (d *Device) ClearDeviceErrors() {
p := [2]uint8{0x00, 0x00}
d.ExecSetCommand(SX126X_CMD_CLEAR_DEVICE_ERRORS, p[:])
}
// GetStats returns the number of informations received on a few last packets
// Lora: NbPktReceived, NbPktCrcError, NbPktHeaderErr
func (d *Device) GetLoraStats() (nbPktReceived, nbPktCrcError, nbPktHeaderErr uint16) {
r := d.ExecGetCommand(SX126X_CMD_GET_STATS, 6)
return uint16(r[0]<<8 | r[1]), uint16(r[2]<<8 | r[3]), uint16(r[4]<<8 | r[5])
}
// ---------------------------------------
// PACKET / RADIO / PROTOCOL CONFIGURATION
// ---------------------------------------
// SetPacketType sets the packet type
func (d *Device) SetPacketType(packetType uint8) {
var p [1]uint8
p[0] = packetType
d.ExecSetCommand(SX126X_CMD_SET_PACKET_TYPE, p[:])
}
// SetSyncWord defines the Sync Word to yse
func (d *Device) SetSyncWord(syncword uint16) {
var p [2]uint8
d.loraConf.SyncWord = syncword
p[0] = uint8((syncword >> 8) & 0xFF)
p[1] = uint8((syncword >> 0) & 0xFF)
d.WriteRegister(SX126X_REG_LORA_SYNC_WORD_MSB, p[:])
}
// GetSyncWord gets the Sync Word to use
func (d *Device) GetSyncWord() uint16 {
p, _ := d.ReadRegister(SX126X_REG_LORA_SYNC_WORD_MSB, 2)
r := uint16(p[0])<<8 + uint16(p[1])
return r
}
// SetLoraPublicNetwork sets Sync Word to 0x3444 (Public) or 0x1424 (Private)
func (d *Device) SetLoraPublicNetwork(enable bool) {
if enable {
d.SetSyncWord(SX126X_LORA_MAC_PUBLIC_SYNCWORD)
} else {
d.SetSyncWord(SX126X_LORA_MAC_PRIVATE_SYNCWORD)
}
}
// SetPacketParam sets various packet-related params
func (d *Device) SetPacketParam(preambleLength uint16, headerType, crcType, payloadLength, invertIQ uint8) {
var p [6]uint8
p[0] = uint8((preambleLength >> 8) & 0xFF)
p[1] = uint8(preambleLength & 0xFF)
p[2] = headerType
p[3] = payloadLength
p[4] = crcType
p[5] = invertIQ
d.ExecSetCommand(SX126X_CMD_SET_PACKET_PARAMS, p[:])
}
// SetBufferBaseAddress sets base address for buffer
func (d *Device) SetBufferBaseAddress(txBaseAddress, rxBaseAddress uint8) {
var p [2]uint8
p[0] = txBaseAddress
p[1] = rxBaseAddress
d.ExecSetCommand(SX126X_CMD_SET_BUFFER_BASE_ADDRESS, p[:])
}
// SetRfFrequency sets the radio frequency
func (d *Device) SetRfFrequency(frequency uint32) {
var p [4]uint8
freq := uint32((uint64(frequency) << 25) / 32000000)
p[0] = uint8((freq >> 24) & 0xFF)
p[1] = uint8((freq >> 16) & 0xFF)
p[2] = uint8((freq >> 8) & 0xFF)
p[3] = uint8((freq >> 0) & 0xFF)
d.ExecSetCommand(SX126X_CMD_SET_RF_FREQUENCY, p[:])
}
// SetCurrentLimit sets max current in the module
func (d *Device) SetCurrentLimit(limit uint8) {
if limit > 140 {
limit = 140
}
rawLimit := uint8(float32(limit) / 2.5)
p := []uint8{rawLimit}
d.WriteRegister(SX126X_REG_OCP_CONFIGURATION, p[:])
}
// SetTxConfig sets power and rampup time
func (d *Device) SetTxParams(power int8, rampTime uint8) {
var p [2]uint8
if d.deviceType == DEVICE_TYPE_SX1261 {
if power == 15 {
d.SetPaConfig(0x06, 0x00, 0x01, 0x01)
} else {
d.SetPaConfig(0x04, 0x00, 0x01, 0x01)
}
if power > 14 {
power = 14
} else if power < -3 {
power = -3
}
d.SetCurrentLimit(80) // Set max current limit to 80mA
} else { // sx1262 and sx1268
d.SetPaConfig(0x04, 0x07, 0x00, 0x01)
if power > 22 {
power = 22
} else if power < -3 {
power = -3
}
d.SetCurrentLimit(140) // Set max current limit to 140 mA
}
p[0] = uint8(power)
p[1] = rampTime
d.ExecSetCommand(SX126X_CMD_SET_TX_PARAMS, p[:])
}
// SetModulationParams sets the Lora modulation frequency
func (d *Device) SetModulationParams(spreadingFactor, bandwidth, codingRate, lowDataRateOptimize uint8) {
var p [4]uint8
p[0] = spreadingFactor
p[1] = bandwidth
p[2] = codingRate
p[3] = lowDataRateOptimize
d.ExecSetCommand(SX126X_CMD_SET_MODULATION_PARAMS, p[:])
}
// CheckDeviceReady sleep until all busy flags clears
func (d *Device) CheckDeviceReady() error {
if d.deepSleep == true {
d.SpiSetNss(false)
time.Sleep(time.Millisecond)
d.SpiSetNss(true)
d.deepSleep = false
}
return d.WaitBusy()
}
// ExecSetCommand send a command to configure the peripheral
func (d *Device) ExecSetCommand(cmd uint8, buf []uint8) {
d.CheckDeviceReady()
if cmd == SX126X_CMD_SET_SLEEP {
d.deepSleep = true
} else {
d.deepSleep = false
}
d.SpiSetNss(false)
// Send command and params
d.spi.Tx(append([]uint8{cmd}, buf...), nil)
d.SpiSetNss(true)
if cmd != SX126X_CMD_SET_SLEEP {
d.WaitBusy()
}
}
// ExecGetCommand queries the peripheral the peripheral
func (d *Device) ExecGetCommand(cmd uint8, size uint8) []uint8 {
d.CheckDeviceReady()
d.SpiSetNss(false)
// Send the command and flush first status byte (as not used)
d.spi.Tx([]uint8{cmd, 0x00}, nil)
d.spi.Tx(nil, d.spiBuffer[:size])
d.SpiSetNss(true)
d.WaitBusy()
return d.spiBuffer[:size]
}
//
// Configuration
//
// SetLoraFrequency() Sets current Lora Frequency
// NB: Change will be applied at next RX / TX
func (d *Device) SetLoraFrequency(freq uint32) {
d.loraConf.Freq = d.loraConf.Freq
}
// SetLoraIqMode() defines the current IQ Mode (Standard/Inverted)
// NB: Change will be applied at next RX / TX
func (d *Device) SetLoraIqMode(mode uint8) {
if mode == 0 {
d.loraConf.Iq = SX126X_LORA_IQ_STANDARD
} else {
d.loraConf.Iq = SX126X_LORA_IQ_INVERTED
}
}
// SetLoraCodingRate() sets current Lora Coding Rate
// NB: Change will be applied at next RX / TX
func (d *Device) SetLoraCodingRate(cr uint8) {
d.loraConf.Cr = cr
}
// SetLoraBandwidth() sets current Lora Bandwidth
// NB: Change will be applied at next RX / TX
func (d *Device) SetLoraBandwidth(bw uint8) {
d.loraConf.Cr = bw
}
// SetLoraCrc() sets current CRC mode (ON/OFF)
// NB: Change will be applied at next RX / TX
func (d *Device) SetLoraCrc(enable bool) {
if enable {
d.loraConf.Crc = SX126X_LORA_CRC_ON
} else {
d.loraConf.Crc = SX126X_LORA_CRC_OFF
}
}
//SetLoraSpreadingFactor setc surrent Lora Spreading Factor
// NB: Change will be applied at next RX / TX
func (d *Device) SetLoraSpreadingFactor(sf uint8) {
d.loraConf.Sf = sf
}
//
// Lora functions
//
//
// LoraConfig() defines Lora configuration for next Lora operations
func (d *Device) LoraConfig(cnf LoraConfig) {
// Save given configuration
d.loraConf = cnf
// Switch to standby prior to configuration changes
d.SetStandby()
// Clear errors, disable radio interrupts for the moment
d.ClearDeviceErrors()
d.ClearIrqStatus(SX126X_IRQ_ALL)
d.SetDioIrqParams(0x00, 0x00, 0x00, 0x00)
// Define radio operation mode
d.SetPacketType(SX126X_PACKET_TYPE_LORA)
d.SetRfFrequency(d.loraConf.Freq)
d.SetModulationParams(d.loraConf.Sf, d.loraConf.Bw, d.loraConf.Cr, d.loraConf.Ldr)
d.SetTxParams(d.loraConf.LoraTxPowerDBm, SX126X_PA_RAMP_200U)
d.SetSyncWord(d.loraConf.SyncWord)
d.SetBufferBaseAddress(0, 0)
}
// LoraTx sends a lora packet, (with timeout)
func (d *Device) LoraTx(pkt []uint8, timeoutMs uint32) error {
if d.loraConf.Freq == 0 {
return errUndefinedLoraConf
}
if d.rfswitch != nil {
err := d.rfswitch.SetRfSwitchMode(RFSWITCH_TX_HP)
if err != nil {
return err
}
}
d.ClearIrqStatus(SX126X_IRQ_ALL)
irqVal := uint16(SX126X_IRQ_TX_DONE | SX126X_IRQ_TIMEOUT | SX126X_IRQ_CRC_ERR)
d.SetStandby()
d.SetPacketType(SX126X_PACKET_TYPE_LORA)
d.SetRfFrequency(d.loraConf.Freq)
d.SetTxParams(d.loraConf.LoraTxPowerDBm, SX126X_PA_RAMP_200U)
d.SetBufferBaseAddress(0, 0)
d.WriteBuffer(pkt)
d.SetModulationParams(d.loraConf.Sf, d.loraConf.Bw, d.loraConf.Cr, d.loraConf.Ldr)
d.SetPacketParam(d.loraConf.Preamble, d.loraConf.HeaderType, d.loraConf.Crc, uint8(len(pkt)), d.loraConf.Iq)
d.SetDioIrqParams(irqVal, irqVal, SX126X_IRQ_NONE, SX126X_IRQ_NONE)
d.SetSyncWord(d.loraConf.SyncWord)
d.SetTx(timeoutMsToRtcSteps(timeoutMs))
msg := <-d.GetRadioEventChan()
if msg.EventType != RadioEventTxDone {
return errors.New("Unexpected Radio Event while TX")
}
return nil
}
// LoraRx tries to receive a Lora packet (with timeout in milliseconds)
func (d *Device) LoraRx(timeoutMs uint32) ([]uint8, error) {
if d.loraConf.Freq == 0 {
return nil, errUndefinedLoraConf
}
if d.rfswitch != nil {
err := d.rfswitch.SetRfSwitchMode(RFSWITCH_RX)
if err != nil {
return nil, err
}
}
d.ClearIrqStatus(SX126X_IRQ_ALL)
irqVal := uint16(SX126X_IRQ_RX_DONE | SX126X_IRQ_TIMEOUT | SX126X_IRQ_CRC_ERR)
d.SetStandby()
d.SetBufferBaseAddress(0, 0)
d.SetModulationParams(d.loraConf.Sf, d.loraConf.Bw, d.loraConf.Cr, d.loraConf.Ldr)
d.SetPacketParam(d.loraConf.Preamble, d.loraConf.HeaderType, d.loraConf.Crc, 0xFF, d.loraConf.Iq)
d.SetDioIrqParams(irqVal, irqVal, SX126X_IRQ_NONE, SX126X_IRQ_NONE)
d.SetRx(timeoutMsToRtcSteps(timeoutMs))
msg := <-d.GetRadioEventChan()
if msg.EventType == RadioEventTimeout {
return nil, nil
} else if msg.EventType != RadioEventRxDone {
return nil, errors.New("Unexpected Radio Event while RX")
}
pLen, pStart := d.GetRxBufferStatus()
d.SetBufferBaseAddress(0, pStart+1)
pkt := d.ReadBuffer(pLen + 1)
pkt = pkt[1:]
return pkt, nil
}
// HandleInterrupt must be called by main code on DIO state change.
func (d *Device) HandleInterrupt() {
st := d.GetIrqStatus()
d.ClearIrqStatus(SX126X_IRQ_ALL)
rChan := d.GetRadioEventChan()
if (st & SX126X_IRQ_RX_DONE) > 0 {
rChan <- NewRadioEvent(RadioEventRxDone, st, nil)
}
if (st & SX126X_IRQ_TX_DONE) > 0 {
rChan <- NewRadioEvent(RadioEventTxDone, st, nil)
}
if (st & SX126X_IRQ_TIMEOUT) > 0 {
rChan <- NewRadioEvent(RadioEventTimeout, st, nil)
}
if (st & SX126X_IRQ_CRC_ERR) > 0 {
rChan <- NewRadioEvent(RadioEventCrcError, st, nil)
}
}
+1 -1
View File
@@ -2,4 +2,4 @@ package drivers
// Version returns a user-readable string showing the version of the drivers package for support purposes.
// Update this value before release of new version of software.
const Version = "0.18.0"
const Version = "0.19.0"
+31
View File
@@ -0,0 +1,31 @@
package wifinina
import (
"time"
"tinygo.org/x/drivers/net"
)
func (d *Device) ConnectToAccessPoint(ssid, pass string, timeout time.Duration) error {
if len(ssid) == 0 {
return net.ErrWiFiMissingSSID
}
start := time.Now()
d.SetPassphrase(ssid, pass)
for time.Since(start) < timeout {
st, _ := d.GetConnectionStatus()
if st == StatusConnected {
return nil
}
time.Sleep(100 * time.Millisecond)
}
return net.ErrWiFiConnectTimeout
}
func (d *Device) GetClientIP() (string, error) {
ip, _, _, err := d.GetIP()
return ip.String(), err
}
+65 -82
View File
@@ -4,50 +4,34 @@ import (
"errors"
"strconv"
"time"
"tinygo.org/x/drivers/net"
)
const (
ReadBufferSize = 128
)
func (d *Device) NewDriver() net.DeviceDriver {
return &Driver{dev: d, sock: NoSocketAvail}
}
type Driver struct {
dev *Device
sock uint8
readBuf readBuffer
proto uint8
ip uint32
port uint16
}
type readBuffer struct {
data [ReadBufferSize]byte
head int
size int
}
func (drv *Driver) GetDNS(domain string) (string, error) {
ipAddr, err := drv.dev.GetHostByName(domain)
func (d *Device) GetDNS(domain string) (string, error) {
ipAddr, err := d.GetHostByName(domain)
return ipAddr.String(), err
}
func (drv *Driver) ConnectTCPSocket(addr, portStr string) error {
return drv.connectSocket(addr, portStr, ProtoModeTCP)
func (d *Device) ConnectTCPSocket(addr, portStr string) error {
return d.connectSocket(addr, portStr, ProtoModeTCP)
}
func (drv *Driver) ConnectSSLSocket(addr, portStr string) error {
return drv.connectSocket(addr, portStr, ProtoModeTLS)
func (d *Device) ConnectSSLSocket(addr, portStr string) error {
return d.connectSocket(addr, portStr, ProtoModeTLS)
}
func (drv *Driver) connectSocket(addr, portStr string, mode uint8) error {
func (d *Device) connectSocket(addr, portStr string, mode uint8) error {
drv.proto, drv.ip, drv.port = mode, 0, 0
d.proto, d.ip, d.port = mode, 0, 0
// convert port to uint16
port, err := convertPort(portStr)
@@ -61,7 +45,7 @@ func (drv *Driver) connectSocket(addr, portStr string, mode uint8) error {
if mode != ProtoModeTLS {
// look up the hostname if necessary; if an IP address was specified, the
// same will be returned. Otherwise, an IPv4 for the hostname is returned.
ipAddr, err := drv.dev.GetHostByName(addr)
ipAddr, err := d.GetHostByName(addr)
if err != nil {
return err
}
@@ -70,32 +54,33 @@ func (drv *Driver) connectSocket(addr, portStr string, mode uint8) error {
}
// check to see if socket is already set; if so, stop it
if drv.sock != NoSocketAvail {
if err := drv.stop(); err != nil {
if d.sock != NoSocketAvail {
if err := d.stop(); err != nil {
return err
}
}
// get a socket from the device
if drv.sock, err = drv.dev.GetSocket(); err != nil {
if d.sock, err = d.GetSocket(); err != nil {
return err
}
// attempt to start the client
if err := drv.dev.StartClient(hostname, ip, port, drv.sock, mode); err != nil {
if err := d.StartClient(hostname, ip, port, d.sock, mode); err != nil {
return err
}
// FIXME: this 4 second timeout is simply mimicking the Arduino driver
for t := newTimer(4 * time.Second); !t.Expired(); {
connected, err := drv.IsConnected()
start := time.Now()
for time.Since(start) < 4*time.Second {
connected, err := d.IsConnected()
if err != nil {
return err
}
if connected {
return nil
}
wait(1 * time.Millisecond)
time.Sleep(1 * time.Millisecond)
}
return ErrConnectionTimeout
@@ -109,12 +94,12 @@ func convertPort(portStr string) (uint16, error) {
return uint16(p64), nil
}
func (drv *Driver) ConnectUDPSocket(addr, portStr, lportStr string) (err error) {
func (d *Device) ConnectUDPSocket(addr, portStr, lportStr string) (err error) {
drv.proto, drv.ip, drv.port = ProtoModeUDP, 0, 0
d.proto, d.ip, d.port = ProtoModeUDP, 0, 0
// convert remote port to uint16
if drv.port, err = convertPort(portStr); err != nil {
if d.port, err = convertPort(portStr); err != nil {
return err
}
@@ -126,73 +111,73 @@ func (drv *Driver) ConnectUDPSocket(addr, portStr, lportStr string) (err error)
// look up the hostname if necessary; if an IP address was specified, the
// same will be returned. Otherwise, an IPv4 for the hostname is returned.
ipAddr, err := drv.dev.GetHostByName(addr)
ipAddr, err := d.GetHostByName(addr)
if err != nil {
return err
}
drv.ip = ipAddr.AsUint32()
d.ip = ipAddr.AsUint32()
// check to see if socket is already set; if so, stop it
// TODO: we can probably have more than one socket at once right?
if drv.sock != NoSocketAvail {
if err := drv.stop(); err != nil {
if d.sock != NoSocketAvail {
if err := d.stop(); err != nil {
return err
}
}
// get a socket from the device
if drv.sock, err = drv.dev.GetSocket(); err != nil {
if d.sock, err = d.GetSocket(); err != nil {
return err
}
// start listening for UDP packets on the local port
if err := drv.dev.StartServer(lport, drv.sock, drv.proto); err != nil {
if err := d.StartServer(lport, d.sock, d.proto); err != nil {
return err
}
return nil
}
func (drv *Driver) DisconnectSocket() error {
return drv.stop()
func (d *Device) DisconnectSocket() error {
return d.stop()
}
func (drv *Driver) StartSocketSend(size int) error {
func (d *Device) StartSocketSend(size int) error {
// not needed for WiFiNINA???
return nil
}
func (drv *Driver) Response(timeout int) ([]byte, error) {
func (d *Device) Response(timeout int) ([]byte, error) {
return nil, nil
}
func (drv *Driver) Write(b []byte) (n int, err error) {
if drv.sock == NoSocketAvail {
func (d *Device) Write(b []byte) (n int, err error) {
if d.sock == NoSocketAvail {
return 0, ErrNoSocketAvail
}
if len(b) == 0 {
return 0, ErrNoData
}
if drv.proto == ProtoModeUDP {
if err := drv.dev.StartClient("", drv.ip, drv.port, drv.sock, drv.proto); err != nil {
if d.proto == ProtoModeUDP {
if err := d.StartClient("", d.ip, d.port, d.sock, d.proto); err != nil {
return 0, errors.New("error in startClient: " + err.Error())
}
if _, err := drv.dev.InsertDataBuf(b, drv.sock); err != nil {
if _, err := d.InsertDataBuf(b, d.sock); err != nil {
return 0, errors.New("error in insertDataBuf: " + err.Error())
}
if _, err := drv.dev.SendUDPData(drv.sock); err != nil {
if _, err := d.SendUDPData(d.sock); err != nil {
return 0, errors.New("error in sendUDPData: " + err.Error())
}
return len(b), nil
} else {
written, err := drv.dev.SendData(b, drv.sock)
written, err := d.SendData(b, d.sock)
if err != nil {
return 0, err
}
if written == 0 {
return 0, ErrDataNotWritten
}
if sent, _ := drv.dev.CheckDataSent(drv.sock); !sent {
if sent, _ := d.CheckDataSent(d.sock); !sent {
return 0, ErrCheckDataError
}
return len(b), nil
@@ -201,8 +186,8 @@ func (drv *Driver) Write(b []byte) (n int, err error) {
return len(b), nil
}
func (drv *Driver) ReadSocket(b []byte) (n int, err error) {
avail, err := drv.available()
func (d *Device) ReadSocket(b []byte) (n int, err error) {
avail, err := d.available()
if err != nil {
println("ReadSocket error: " + err.Error())
return 0, err
@@ -214,37 +199,37 @@ func (drv *Driver) ReadSocket(b []byte) (n int, err error) {
if avail < length {
length = avail
}
copy(b, drv.readBuf.data[drv.readBuf.head:drv.readBuf.head+length])
drv.readBuf.head += length
drv.readBuf.size -= length
copy(b, d.readBuf.data[d.readBuf.head:d.readBuf.head+length])
d.readBuf.head += length
d.readBuf.size -= length
return length, nil
}
// IsSocketDataAvailable returns of there is socket data available
func (drv *Driver) IsSocketDataAvailable() bool {
n, err := drv.available()
func (d *Device) IsSocketDataAvailable() bool {
n, err := d.available()
return err == nil && n > 0
}
func (drv *Driver) available() (int, error) {
if drv.readBuf.size == 0 {
n, err := drv.dev.GetDataBuf(drv.sock, drv.readBuf.data[:])
func (d *Device) available() (int, error) {
if d.readBuf.size == 0 {
n, err := d.GetDataBuf(d.sock, d.readBuf.data[:])
if n > 0 {
drv.readBuf.head = 0
drv.readBuf.size = n
d.readBuf.head = 0
d.readBuf.size = n
}
if err != nil {
return int(n), err
}
}
return drv.readBuf.size, nil
return d.readBuf.size, nil
}
func (drv *Driver) IsConnected() (bool, error) {
if drv.sock == NoSocketAvail {
func (d *Device) IsConnected() (bool, error) {
if d.sock == NoSocketAvail {
return false, nil
}
s, err := drv.status()
s, err := d.status()
if err != nil {
return false, err
}
@@ -260,28 +245,26 @@ func (drv *Driver) IsConnected() (bool, error) {
return isConnected, nil
}
func (drv *Driver) status() (uint8, error) {
if drv.sock == NoSocketAvail {
func (d *Device) status() (uint8, error) {
if d.sock == NoSocketAvail {
return TCPStateClosed, nil
}
return drv.dev.GetClientState(drv.sock)
return d.GetClientState(d.sock)
}
func (drv *Driver) stop() error {
if drv.sock == NoSocketAvail {
func (d *Device) stop() error {
if d.sock == NoSocketAvail {
return nil
}
drv.dev.StopClient(drv.sock)
for t := newTimer(5 * time.Second); !t.Expired(); {
st, _ := drv.status()
d.StopClient(d.sock)
start := time.Now()
for time.Since(start) < 5*time.Second {
st, _ := d.status()
if st == TCPStateClosed {
break
}
// FIXME: without the time.Sleep below this blocks until TCPStateClosed,
// however with it got goroutine stack overflows; not sure if this is still
// an issue so should investigate further
//time.Sleep(1 * time.Millisecond)
time.Sleep(1 * time.Millisecond)
}
drv.sock = NoSocketAvail
d.sock = NoSocketAvail
return nil
}
-28
View File
@@ -1,28 +0,0 @@
package wifinina
import "time"
func wait(duration time.Duration) {
newTimer(duration).WaitUntilExpired()
}
type timer struct {
start int64
interval int64
}
func newTimer(interval time.Duration) timer {
return timer{
start: time.Now().UnixNano(),
interval: int64(interval),
}
}
func (t timer) Expired() bool {
return time.Now().UnixNano() > (t.start + t.interval)
}
func (t timer) WaitUntilExpired() {
for !t.Expired() {
}
}
+20 -11
View File
@@ -281,9 +281,16 @@ type Device struct {
buf [64]byte
ssids [10]string
sock uint8
readBuf readBuffer
proto uint8
ip uint32
port uint16
}
// New returns a new Wifinina driver.
// New returns a new Wifinina device.
func New(bus drivers.SPI, csPin, ackPin, gpio0Pin, resetPin machine.Pin) *Device {
return &Device{
SPI: bus,
@@ -295,8 +302,7 @@ func New(bus drivers.SPI, csPin, ackPin, gpio0Pin, resetPin machine.Pin) *Device
}
func (d *Device) Configure() {
net.UseDriver(d.NewDriver())
net.UseDriver(d)
pinUseDevice(d)
d.CS.Configure(machine.PinConfig{Mode: machine.PinOutput})
@@ -379,7 +385,7 @@ func (d *Device) CheckDataSent(sock uint8) (bool, error) {
if sent > 0 {
return true, nil
}
wait(100 * time.Microsecond)
time.Sleep(100 * time.Microsecond)
}
return false, lastErr
}
@@ -1023,8 +1029,7 @@ func (d *Device) checkStartCmd() (bool, error) {
}
func (d *Device) waitForChipSelect() (err error) {
err = d.waitForChipReady()
if err == nil {
if err = d.waitForChipReady(); err == nil {
err = d.spiChipSelect()
}
return
@@ -1034,12 +1039,14 @@ func (d *Device) waitForChipReady() error {
if _debug {
println("waitForChipReady()\r")
}
for t := newTimer(10 * time.Second); !(d.ACK.Get() == false); {
if t.Expired() {
return ErrTimeoutChipReady
start := time.Now()
for time.Since(start) < 10*time.Second {
if !d.ACK.Get() {
return nil
}
time.Sleep(1 * time.Millisecond)
}
return nil
return ErrTimeoutChipReady
}
func (d *Device) spiChipSelect() error {
@@ -1047,10 +1054,12 @@ func (d *Device) spiChipSelect() error {
println("spiChipSelect()\r")
}
d.CS.Low()
for t := newTimer(5 * time.Millisecond); !t.Expired(); {
start := time.Now()
for time.Since(start) < 5*time.Millisecond {
if d.ACK.Get() {
return nil
}
time.Sleep(100 * time.Microsecond)
}
return ErrTimeoutChipSelect
}
+188
View File
@@ -0,0 +1,188 @@
// Package xpt2046 implements a driver for the XPT2046 resistive touch controller as packaged on the TFT_320QVT board
//
// Datasheet: http://grobotronics.com/images/datasheets/xpt2046-datasheet.pdf
package xpt2046
import (
"machine"
"time"
"tinygo.org/x/drivers/touch"
)
type Device struct {
t_clk machine.Pin
t_cs machine.Pin
t_din machine.Pin
t_dout machine.Pin
t_irq machine.Pin
precision uint8
}
type Config struct {
Precision uint8
}
func New(t_clk, t_cs, t_din, t_dout, t_irq machine.Pin) Device {
return Device{
precision: 10,
t_clk: t_clk,
t_cs: t_cs,
t_din: t_din,
t_dout: t_dout,
t_irq: t_irq,
}
}
func (d *Device) Configure(config *Config) error {
if config.Precision == 0 {
d.precision = 10
} else {
d.precision = config.Precision
}
d.t_clk.Configure(machine.PinConfig{Mode: machine.PinOutput})
d.t_cs.Configure(machine.PinConfig{Mode: machine.PinOutput})
d.t_din.Configure(machine.PinConfig{Mode: machine.PinOutput})
d.t_dout.Configure(machine.PinConfig{Mode: machine.PinInput})
d.t_irq.Configure(machine.PinConfig{Mode: machine.PinInput})
d.t_clk.Low()
d.t_cs.High()
d.t_din.Low()
d.readRaw() //Set Powerdown mode to enable T_IRQ
return nil
}
func busSleep() {
time.Sleep(5 * time.Nanosecond)
}
func pulseHigh(p machine.Pin) {
p.High()
busSleep()
p.Low()
busSleep()
}
func (d *Device) writeCommand(data uint8) {
for count := uint8(0); count < 8; count++ {
d.t_din.Set((data & 0x80) != 0)
data <<= 1
pulseHigh(d.t_clk)
}
}
func (d *Device) readData() uint16 {
data := uint16(0)
for count := uint8(0); count < 12; count++ {
data <<= 1
pulseHigh(d.t_clk)
if d.t_dout.Get() {
data |= 1
}
}
pulseHigh(d.t_clk) //13
pulseHigh(d.t_clk) //14
pulseHigh(d.t_clk) //15
pulseHigh(d.t_clk) //16
return data
}
func (d *Device) ReadTouchPoint() touch.Point {
tx := uint32(0)
ty := uint32(0)
tz := uint32(0)
sampleCount := uint8(0)
d.t_cs.Low()
for ; sampleCount < d.precision && d.Touched(); sampleCount++ {
rx, ry, rz := d.readRaw()
tx += uint32(rx)
ty += uint32(ry)
tz += uint32(rz)
}
d.t_cs.High()
if sampleCount > 0 {
x := int(tx / uint32(sampleCount))
y := int(ty / uint32(sampleCount))
z := int(tz / uint32(sampleCount))
return touch.Point{
X: x,
Y: y,
Z: z,
}
} else {
return touch.Point{
X: 0,
Y: 0,
Z: 0,
}
}
}
func (d *Device) Touched() bool {
avail := !d.t_irq.Get()
return avail
}
func (d *Device) readRaw() (int32, int32, int32) {
d.t_cs.Low()
//S = 1 --> Required Control bit
//A2-A0 = 001 --> Y-Position
//MODE = 0 --> 12 bit conversion
//SER/DFR = 0 --> Differential preferred for X,Y position
//PD1-PD0 = 00 --> Powerdown and enable PEN_IRQ
d.writeCommand(0x90)
ty := d.readData()
//S = 1 --> Required Control bit
//A2-A0 = 101 --> X-Position
//MODE = 0 --> 12 bit conversion
//SER/DFR = 0 --> Differential preferred for X,Y position
//PD1-PD0 = 00 --> Powerdown and enable PEN_IRQ
d.writeCommand(0xD0)
tx := d.readData()
//S = 1 --> Required Control bit
//A2-A0 = 011 --> Z1-position (pressure)
//MODE = 0 --> 12 bit conversion
//SER/DFR = 0 --> Differential preferred for pressure
//PD1-PD0 = 00 --> Powerdown and enable PEN_IRQ
d.writeCommand(0xB0)
tz1 := int32(d.readData())
//S = 1 --> Required Control bit
//A2-A0 = 100 --> Z2-position (pressure)
//MODE = 0 --> 12 bit conversion
//SER/DFR = 0 --> Differential preferred for pressure
//PD1-PD0 = 00 --> Powerdown and enable PEN_IRQ
d.writeCommand(0xC0)
tz2 := int32(d.readData())
tz := int32(0)
if tz1 != 0 {
//Touch pressure is proportional to the ratio of z2 to z1 and the x position.
tz = int32(tx) * ((tz2 << 12) / (tz1 << 12))
}
d.t_cs.High()
//Scale X&Y to 16 bit for consistency across touch drivers
return int32(tx) << 4, int32(4096-ty) << 4, tz
}