mirror of
https://github.com/tinygo-org/drivers.git
synced 2026-08-03 06:27:47 +00:00
Compare commits
38 Commits
st7789-async
...
dht20
| Author | SHA1 | Date | |
|---|---|---|---|
| 3c1aaa9a9f | |||
| 3f4a9d82f7 | |||
| 19c9fe6db3 | |||
| 315dbef694 | |||
| ee3842f639 | |||
| 1bf1a11067 | |||
| 848f5def03 | |||
| 4a0bcba87c | |||
| 7d983647ad | |||
| 831982ad33 | |||
| 9063f46313 | |||
| 2a621dc3b1 | |||
| 7dbca2a543 | |||
| 50b6f18cc2 | |||
| bb0e6b8ba8 | |||
| 5eeba138a4 | |||
| 1095629f62 | |||
| bf0b383176 | |||
| f206f6f56f | |||
| a0293643b3 | |||
| 60801ba852 | |||
| a74770b2e6 | |||
| 3ed09d3fe1 | |||
| 22581dfd58 | |||
| 31f09d93b8 | |||
| 0ea969f25a | |||
| 8665d44e0f | |||
| c344e5d879 | |||
| 308763f500 | |||
| c41f2e472d | |||
| aa7bd47c68 | |||
| e7931c6a22 | |||
| 3c5e17423a | |||
| 0262122ccd | |||
| 7d24c3c285 | |||
| 5c0f0480bc | |||
| 8642886f73 | |||
| 1a96fc4547 |
+125
@@ -1,3 +1,128 @@
|
|||||||
|
0.28.0
|
||||||
|
---
|
||||||
|
- **new devices**
|
||||||
|
- **epd2in66b**
|
||||||
|
- Waveshare 2.66inch E-Paper Display Module (B) for Raspberry Pi Pico (#673)
|
||||||
|
- **mcp9808**
|
||||||
|
- Add driver for MCP9808 i2c temperature sensor (#676)
|
||||||
|
|
||||||
|
- **enhancements**
|
||||||
|
- **encoders**
|
||||||
|
- add atsamd21, atsamd51, atsame5x
|
||||||
|
- **pixel**
|
||||||
|
- add support for Monochrome types such as the SSD1306 display
|
||||||
|
- **rtl8720dn**
|
||||||
|
- implement ConnectModeAP
|
||||||
|
- **servo**
|
||||||
|
- add function SetAngle() to simplify API for most common use case
|
||||||
|
- **ssd1306**
|
||||||
|
- add DrawBitmap() function to complete Displayer interface
|
||||||
|
- add rotation functions for Displayer interface
|
||||||
|
- add Sleep() function for Displayer interface
|
||||||
|
- **uc8151**
|
||||||
|
- improvements to speed and also add flicker-free mode based on @antirez code example
|
||||||
|
- update to support all functions needed by tinygl and board package Displayer interface
|
||||||
|
- **wifinina**
|
||||||
|
- implement ConnectModeAP
|
||||||
|
|
||||||
|
- **bugfixes**
|
||||||
|
- **ft6336**
|
||||||
|
- ignore bogus touch events
|
||||||
|
- **pixel**
|
||||||
|
- fix Image[Monochrome].Set for larger images
|
||||||
|
- **uc8151**
|
||||||
|
- correct DrawBitmap() also refactor SendCommand() and SendData() for clarity
|
||||||
|
- **ws2812**
|
||||||
|
- Fix typo and move initialization of neo to init()
|
||||||
|
|
||||||
|
- **examples**
|
||||||
|
- **ws2812**
|
||||||
|
- Simplify examples/ws2812
|
||||||
|
|
||||||
|
|
||||||
|
0.27.0
|
||||||
|
---
|
||||||
|
- **core**
|
||||||
|
- prepare for CGo changes in TinyGo
|
||||||
|
|
||||||
|
- **new devices**
|
||||||
|
- **adafruit4650**
|
||||||
|
- support for Adafruit 4650 feather OLED
|
||||||
|
- **net**
|
||||||
|
- new networking support based on tinygo net package
|
||||||
|
- **pixel**
|
||||||
|
- add package for efficiently working with raw pixel buffers
|
||||||
|
- **rotary**
|
||||||
|
- Adding driver for rotary encoder support
|
||||||
|
- **seesaw**
|
||||||
|
- Adding support for Adafruit Seesaw platform
|
||||||
|
- **sgp30**
|
||||||
|
- add SGP30 air quality sensor
|
||||||
|
- **sk6812**
|
||||||
|
- added support for SK6812 to WS2812 device (#610)
|
||||||
|
|
||||||
|
- **enhancements**
|
||||||
|
- **epd2in13**
|
||||||
|
- add Sleep method like other displays
|
||||||
|
- unify rotation configuration with other displays
|
||||||
|
- use better black/white approximation
|
||||||
|
- **ili9341**
|
||||||
|
- add DrawBitmap method
|
||||||
|
- **lora/lorawan**
|
||||||
|
- LoRa WAN US915 Support
|
||||||
|
- LoRa WAN add setter functions
|
||||||
|
- refactor shared functionality for channels/regions
|
||||||
|
- **mcp2515**
|
||||||
|
- Add more line speeds to mcp2515.go (#626)
|
||||||
|
- **rtl8720dn**
|
||||||
|
- use drivers package version as the driver version
|
||||||
|
- **ssd1306**
|
||||||
|
- improvements needed for Thumby SPI display
|
||||||
|
- **st7735**
|
||||||
|
- make the display generic over RGB565 and RGB444
|
||||||
|
- **st7789**
|
||||||
|
- add DrawBitmap method
|
||||||
|
- make the display generic over RGB565 and RGB444
|
||||||
|
- **wifinina**
|
||||||
|
- add ResetIsHigh cfg switch for MKR 1010 (copied from #561)
|
||||||
|
- maintenence. Also see PR #4085 in the main TinyGo repo
|
||||||
|
- use drivers package version as the driver version
|
||||||
|
|
||||||
|
- **bugfixes**
|
||||||
|
- **adxl345**
|
||||||
|
- Use int16 for ADXL345 readings (#656)
|
||||||
|
- **at24cx**
|
||||||
|
- fixed the description of the device struct
|
||||||
|
- **rtl8720dn**
|
||||||
|
- allow connecting to open wifi access points
|
||||||
|
- fix check for bad Wifi connect
|
||||||
|
- **sh1106**
|
||||||
|
- fix I2C interface and add smoketest
|
||||||
|
- fixed the description of the device struct
|
||||||
|
- **wifinina**
|
||||||
|
- add 'unknown failure' reason code for AP connect
|
||||||
|
- fix concurrency issues with multiple sockets
|
||||||
|
- fix wifinina UDP send
|
||||||
|
|
||||||
|
- **examples**
|
||||||
|
- **ds3231**
|
||||||
|
- fix the description in the example
|
||||||
|
- **lorawan**
|
||||||
|
- add missing functions for simulated interface
|
||||||
|
- modify atcmd and basic demo to support choosing any one of the supported regions at compile time by using ldflags
|
||||||
|
- **net**
|
||||||
|
- all networking examples now using netdev and netlink.
|
||||||
|
|
||||||
|
- **build**
|
||||||
|
- **all**
|
||||||
|
- fix broken testrunner
|
||||||
|
- migrated legacy I2C
|
||||||
|
- add natiu package for tests
|
||||||
|
- **smoketest**
|
||||||
|
- add stack-size param for net tests.
|
||||||
|
- allow stack-size flag as it is needed for net examples
|
||||||
|
|
||||||
|
|
||||||
0.26.0
|
0.26.0
|
||||||
---
|
---
|
||||||
- **core**
|
- **core**
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
Copyright (c) 2018-2023 The TinyGo Authors. All rights reserved.
|
Copyright (c) 2018-2024 The TinyGo Authors. All rights reserved.
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
Redistribution and use in source and binary forms, with or without
|
||||||
modification, are permitted provided that the following conditions are
|
modification, are permitted provided that the following conditions are
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ rwildcard=$(foreach d,$(wildcard $1*),$(call rwildcard,$d/,$2) $(filter $(subst
|
|||||||
# Recursively find all *_test.go files from cwd & reduce to unique dir names
|
# Recursively find all *_test.go files from cwd & reduce to unique dir names
|
||||||
HAS_TESTS = $(sort $(dir $(call rwildcard,,*_test.go)))
|
HAS_TESTS = $(sort $(dir $(call rwildcard,,*_test.go)))
|
||||||
# Exclude anything we explicitly don't want to test for whatever reason
|
# Exclude anything we explicitly don't want to test for whatever reason
|
||||||
EXCLUDE_TESTS = image
|
EXCLUDE_TESTS = image waveshare-epd/epd2in66b
|
||||||
TESTS = $(filter-out $(addsuffix /%,$(EXCLUDE_TESTS)),$(HAS_TESTS))
|
TESTS = $(filter-out $(addsuffix /%,$(EXCLUDE_TESTS)),$(HAS_TESTS))
|
||||||
|
|
||||||
unit-test:
|
unit-test:
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
[](https://pkg.go.dev/tinygo.org/x/drivers) [](https://github.com/tinygo-org/drivers/actions/workflows/build.yml)
|
[](https://pkg.go.dev/tinygo.org/x/drivers) [](https://github.com/tinygo-org/drivers/actions/workflows/build.yml)
|
||||||
|
|
||||||
|
|
||||||
This package provides a collection of 101 different hardware drivers for devices such as sensors and displays that can be used together with [TinyGo](https://tinygo.org).
|
This package provides a collection of over 100 different hardware drivers for devices such as sensors, displays, wireless adaptors, and actuators, that can be used together with [TinyGo](https://tinygo.org).
|
||||||
|
|
||||||
For the complete list, please see:
|
For the complete list, please see:
|
||||||
https://tinygo.org/docs/reference/devices/
|
https://tinygo.org/docs/reference/devices/
|
||||||
|
|||||||
+7
-7
@@ -95,16 +95,16 @@ func (d *Device) Restart() {
|
|||||||
func (d *Device) ReadAcceleration() (x int32, y int32, z int32, err error) {
|
func (d *Device) ReadAcceleration() (x int32, y int32, z int32, err error) {
|
||||||
rx, ry, rz := d.ReadRawAcceleration()
|
rx, ry, rz := d.ReadRawAcceleration()
|
||||||
|
|
||||||
x = d.dataFormat.convertToIS(rx)
|
x = int32(d.dataFormat.convertToIS(rx))
|
||||||
y = d.dataFormat.convertToIS(ry)
|
y = int32(d.dataFormat.convertToIS(ry))
|
||||||
z = d.dataFormat.convertToIS(rz)
|
z = int32(d.dataFormat.convertToIS(rz))
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadRawAcceleration reads the sensor values and returns the raw x, y and z axis
|
// ReadRawAcceleration reads the sensor values and returns the raw x, y and z axis
|
||||||
// from the adxl345.
|
// from the adxl345.
|
||||||
func (d *Device) ReadRawAcceleration() (x int32, y int32, z int32) {
|
func (d *Device) ReadRawAcceleration() (x int16, y int16, z int16) {
|
||||||
data := []byte{0, 0, 0, 0, 0, 0}
|
data := []byte{0, 0, 0, 0, 0, 0}
|
||||||
legacy.ReadRegister(d.bus, uint8(d.Address), REG_DATAX0, data)
|
legacy.ReadRegister(d.bus, uint8(d.Address), REG_DATAX0, data)
|
||||||
|
|
||||||
@@ -140,7 +140,7 @@ func (d *Device) SetRange(sensorRange Range) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// convertToIS adjusts the raw values from the adxl345 with the range configuration
|
// convertToIS adjusts the raw values from the adxl345 with the range configuration
|
||||||
func (d *dataFormat) convertToIS(rawValue int32) int32 {
|
func (d *dataFormat) convertToIS(rawValue int16) int16 {
|
||||||
switch d.sensorRange {
|
switch d.sensorRange {
|
||||||
case RANGE_2G:
|
case RANGE_2G:
|
||||||
return rawValue * 4 // rawValue * 2 * 1000 / 512
|
return rawValue * 4 // rawValue * 2 * 1000 / 512
|
||||||
@@ -190,6 +190,6 @@ func (b *bwRate) toByte() (bits uint8) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// readInt converts two bytes to int16
|
// readInt converts two bytes to int16
|
||||||
func readIntLE(msb byte, lsb byte) int32 {
|
func readIntLE(msb byte, lsb byte) int16 {
|
||||||
return int32(uint16(msb) | uint16(lsb)<<8)
|
return int16(uint16(msb) | uint16(lsb)<<8)
|
||||||
}
|
}
|
||||||
|
|||||||
+144
@@ -0,0 +1,144 @@
|
|||||||
|
// Package dht20 implements a driver for the DHT20 temperature and humidity sensor.
|
||||||
|
//
|
||||||
|
// Datasheet: https://cdn-shop.adafruit.com/product-files/5183/5193_DHT20.pdf
|
||||||
|
|
||||||
|
package dht20
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"tinygo.org/x/drivers"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
errUpdateCalledTooSoon = errors.New("Update() called within 80ms is invalid")
|
||||||
|
)
|
||||||
|
|
||||||
|
// Device wraps an I2C connection to a DHT20 device.
|
||||||
|
type Device struct {
|
||||||
|
bus drivers.I2C
|
||||||
|
Address uint16
|
||||||
|
data [8]uint8
|
||||||
|
temperature float32
|
||||||
|
humidity float32
|
||||||
|
prevAccessTime time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
// New creates a new DHT20 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: defaultAddress, // Using the address defined in registers.go
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure sets up the device for communication and initializes the registers if needed.
|
||||||
|
func (d *Device) Configure() error {
|
||||||
|
// Get the status word
|
||||||
|
d.data[0] = 0x71
|
||||||
|
err := d.bus.Tx(d.Address, d.data[:1], d.data[:1])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if d.data[0] != 0x18 {
|
||||||
|
// Initialize registers
|
||||||
|
err := d.initRegisters()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Set the previous access time to the current time
|
||||||
|
d.prevAccessTime = time.Now()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// initRegisters initializes the registers 0x1B, 0x1C, and 0x1E to 0x00.
|
||||||
|
func (d *Device) initRegisters() error {
|
||||||
|
// Initialize register 0x1B
|
||||||
|
d.data[0] = 0x1B
|
||||||
|
d.data[1] = 0x00
|
||||||
|
err := d.bus.Tx(d.Address, d.data[:2], nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize register 0x1C
|
||||||
|
d.data[0] = 0x1C
|
||||||
|
d.data[1] = 0x00
|
||||||
|
err = d.bus.Tx(d.Address, d.data[:2], nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize register 0x1E
|
||||||
|
d.data[0] = 0x1E
|
||||||
|
d.data[1] = 0x00
|
||||||
|
err = d.bus.Tx(d.Address, d.data[:2], nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update reads data from the sensor and updates the temperature and humidity values.
|
||||||
|
// Note that the values obtained by this function are from the previous call to Update.
|
||||||
|
// If you want to use the most recent values, shorten the interval at which Update is called.
|
||||||
|
func (d *Device) Update(which drivers.Measurement) error {
|
||||||
|
if which&drivers.Temperature == 0 && which&drivers.Humidity == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if 80ms have passed since the last access
|
||||||
|
if time.Since(d.prevAccessTime) < 80*time.Millisecond {
|
||||||
|
return errUpdateCalledTooSoon
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the status word Bit[7]
|
||||||
|
d.data[0] = 0x71
|
||||||
|
err := d.bus.Tx(d.Address, d.data[:1], d.data[:1])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if (d.data[0] & 0x80) == 0 {
|
||||||
|
// Read 7 bytes of data from the sensor
|
||||||
|
err := d.bus.Tx(d.Address, nil, d.data[:7])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
rawHumidity := uint32(d.data[1])<<12 | uint32(d.data[2])<<4 | uint32(d.data[3])>>4
|
||||||
|
rawTemperature := uint32(d.data[3]&0x0F)<<16 | uint32(d.data[4])<<8 | uint32(d.data[5])
|
||||||
|
|
||||||
|
// Convert raw values to human-readable values
|
||||||
|
d.humidity = float32(rawHumidity) / 1048576.0 * 100
|
||||||
|
d.temperature = float32(rawTemperature)/1048576.0*200 - 50
|
||||||
|
|
||||||
|
// Trigger the next measurement
|
||||||
|
d.data[0] = 0xAC
|
||||||
|
d.data[1] = 0x33
|
||||||
|
d.data[2] = 0x00
|
||||||
|
err = d.bus.Tx(d.Address, d.data[:3], nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the previous access time to the current time
|
||||||
|
d.prevAccessTime = time.Now()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Temperature returns the last measured temperature.
|
||||||
|
func (d *Device) Temperature() float32 {
|
||||||
|
return d.temperature
|
||||||
|
}
|
||||||
|
|
||||||
|
// Humidity returns the last measured humidity.
|
||||||
|
func (d *Device) Humidity() float32 {
|
||||||
|
return d.humidity
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package dht20
|
||||||
|
|
||||||
|
// Constants/addresses used for I2C.
|
||||||
|
|
||||||
|
// The I2C address which this device listens to.
|
||||||
|
const defaultAddress = 0x38
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package encoders
|
||||||
|
|
||||||
|
type QuadratureDevice struct {
|
||||||
|
cfg QuadratureConfig
|
||||||
|
impl quadratureImpl
|
||||||
|
}
|
||||||
|
|
||||||
|
type QuadratureConfig struct {
|
||||||
|
Precision int
|
||||||
|
}
|
||||||
|
|
||||||
|
type quadratureImpl interface {
|
||||||
|
configure(cfg QuadratureConfig) error
|
||||||
|
readValue() int
|
||||||
|
writeValue(int)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (enc *QuadratureDevice) Configure(cfg QuadratureConfig) error {
|
||||||
|
if cfg.Precision < 1 {
|
||||||
|
cfg.Precision = 4
|
||||||
|
}
|
||||||
|
enc.cfg = cfg
|
||||||
|
return enc.impl.configure(cfg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Position returns the stored int value for the encoder
|
||||||
|
func (enc *QuadratureDevice) Position() int {
|
||||||
|
return enc.impl.readValue() / enc.cfg.Precision
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetPosition overwrites the currently stored value with the specified int value
|
||||||
|
func (enc *QuadratureDevice) SetPosition(v int) {
|
||||||
|
enc.impl.writeValue(v * enc.cfg.Precision)
|
||||||
|
}
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
//go:build tinygo && (rp2040 || stm32 || k210 || esp32c3 || nrf || sam || (avr && (atmega328p || atmega328pb)))
|
||||||
|
|
||||||
|
// Implementation based on:
|
||||||
|
// https://gist.github.com/aykevl/3fc1683ed77bb0a9c07559dfe857304a
|
||||||
|
|
||||||
|
// Note: build constraints in this file list targets that define machine.PinToggle.
|
||||||
|
// If this is supported for additional targets in the future, they can be added above.
|
||||||
|
|
||||||
|
package encoders
|
||||||
|
|
||||||
|
import (
|
||||||
|
"machine"
|
||||||
|
"runtime/volatile"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
states = []int8{0, -1, 1, 0, 1, 0, 0, -1, -1, 0, 0, 1, 0, 1, -1, 0}
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewQuadratureViaInterrupt returns a rotary encoder device that uses GPIO
|
||||||
|
// interrupts and a lookup table to keep track of quadrature state changes.
|
||||||
|
//
|
||||||
|
// This constructur is only available for TinyGo targets for which machine.PinToggle
|
||||||
|
// is defined as a valid interrupt type.
|
||||||
|
func NewQuadratureViaInterrupt(pinA, pinB machine.Pin) *QuadratureDevice {
|
||||||
|
return &QuadratureDevice{impl: &quadInterruptImpl{pinA: pinA, pinB: pinB, oldAB: 0b00000011}}
|
||||||
|
}
|
||||||
|
|
||||||
|
type quadInterruptImpl struct {
|
||||||
|
pinA machine.Pin
|
||||||
|
pinB machine.Pin
|
||||||
|
|
||||||
|
// precision int
|
||||||
|
|
||||||
|
oldAB int
|
||||||
|
value volatile.Register32
|
||||||
|
}
|
||||||
|
|
||||||
|
func (enc *quadInterruptImpl) configure(cfg QuadratureConfig) error {
|
||||||
|
enc.pinA.Configure(machine.PinConfig{Mode: machine.PinInputPullup})
|
||||||
|
enc.pinA.SetInterrupt(machine.PinToggle, enc.interrupt)
|
||||||
|
|
||||||
|
enc.pinB.Configure(machine.PinConfig{Mode: machine.PinInputPullup})
|
||||||
|
enc.pinB.SetInterrupt(machine.PinToggle, enc.interrupt)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (enc *quadInterruptImpl) interrupt(pin machine.Pin) {
|
||||||
|
aHigh, bHigh := enc.pinA.Get(), enc.pinB.Get()
|
||||||
|
enc.oldAB <<= 2
|
||||||
|
if aHigh {
|
||||||
|
enc.oldAB |= 1 << 1
|
||||||
|
}
|
||||||
|
if bHigh {
|
||||||
|
enc.oldAB |= 1
|
||||||
|
}
|
||||||
|
enc.writeValue(enc.readValue() + int(states[enc.oldAB&0x0f]))
|
||||||
|
}
|
||||||
|
|
||||||
|
// readValue gets the value using volatile operations and returns it as an int
|
||||||
|
func (enc *quadInterruptImpl) readValue() int {
|
||||||
|
return int(enc.value.Get())
|
||||||
|
}
|
||||||
|
|
||||||
|
// writeValue set the value to the specified int using volatile operations
|
||||||
|
func (enc *quadInterruptImpl) writeValue(v int) {
|
||||||
|
enc.value.Set(uint32(v))
|
||||||
|
}
|
||||||
+3
-4
@@ -122,7 +122,7 @@ func (d *Device) NetDisconnect() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *Device) NetNotify(cb func(netlink.Event)) {
|
func (d *Device) NetNotify(cb func(netlink.Event)) {
|
||||||
// Not supported
|
fmt.Printf("\r\n%s\r\n", netlink.ErrNotSupported)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Device) GetHostByName(name string) (netip.Addr, error) {
|
func (d *Device) GetHostByName(name string) (netip.Addr, error) {
|
||||||
@@ -218,8 +218,8 @@ func (d *Device) Listen(sockfd int, backlog int) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Device) Accept(sockfd int, ip netip.AddrPort) (int, error) {
|
func (d *Device) Accept(sockfd int) (int, netip.AddrPort, error) {
|
||||||
return -1, netdev.ErrNotSupported
|
return -1, netip.AddrPort{}, netdev.ErrNotSupported
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Device) sendChunk(sockfd int, buf []byte, deadline time.Time) (int, error) {
|
func (d *Device) sendChunk(sockfd int, buf []byte, deadline time.Time) (int, error) {
|
||||||
@@ -473,7 +473,6 @@ func (d *Device) parseIPD(end int) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// load up the socket data
|
// load up the socket data
|
||||||
//d.data = append(d.data, d.response[e+1:end]...)
|
|
||||||
d.data = append(d.data, d.response[e+1:e+1+v]...)
|
d.data = append(d.data, d.response[e+1:e+1+v]...)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"machine"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"tinygo.org/x/drivers"
|
||||||
|
"tinygo.org/x/drivers/dht20"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
i2c = machine.I2C0
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
i2c.Configure(machine.I2CConfig{})
|
||||||
|
sensor := dht20.New(i2c)
|
||||||
|
sensor.Configure()
|
||||||
|
|
||||||
|
// Trigger the first measurement
|
||||||
|
sensor.Update(drivers.AllMeasurements)
|
||||||
|
|
||||||
|
for {
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
|
||||||
|
// Update sensor dasta
|
||||||
|
sensor.Update(drivers.AllMeasurements)
|
||||||
|
temp := sensor.Temperature()
|
||||||
|
hum := sensor.Humidity()
|
||||||
|
|
||||||
|
// Note: The sensor values are from the previous measurement (1 second ago)
|
||||||
|
println("Temperature:", strconv.FormatFloat(float64(temp), 'f', 2, 64), "°C")
|
||||||
|
println("Humidity:", strconv.FormatFloat(float64(hum), 'f', 2, 64), "%")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
//go:build macropad_rp2040
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"machine"
|
||||||
|
|
||||||
|
"tinygo.org/x/drivers/encoders"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
enc = encoders.NewQuadratureViaInterrupt(machine.ROT_A, machine.ROT_B)
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
|
||||||
|
enc.Configure(encoders.QuadratureConfig{
|
||||||
|
Precision: 4,
|
||||||
|
})
|
||||||
|
|
||||||
|
for oldValue := 0; ; {
|
||||||
|
if newValue := enc.Position(); newValue != oldValue {
|
||||||
|
println("value: ", newValue)
|
||||||
|
oldValue = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"machine"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"tinygo.org/x/drivers/mcp9808"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
|
||||||
|
//tinygo monitor
|
||||||
|
time.Sleep(time.Millisecond * 5000)
|
||||||
|
|
||||||
|
//Configure I2C (in this case, I2C0 on RPI Pico), and wire the module accordingly
|
||||||
|
machine.I2C0.Configure(machine.I2CConfig{
|
||||||
|
SCL: machine.GP1,
|
||||||
|
SDA: machine.GP0,
|
||||||
|
})
|
||||||
|
|
||||||
|
//Create sensor
|
||||||
|
sensor := mcp9808.New(machine.I2C0)
|
||||||
|
if !sensor.Connected() {
|
||||||
|
println("MCP9808 not found")
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
println("MCP9808 found")
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(time.Millisecond * 1000)
|
||||||
|
|
||||||
|
//Set resolution
|
||||||
|
sensor.SetResolution(mcp9808.Maximum)
|
||||||
|
|
||||||
|
time.Sleep(time.Millisecond * 1000)
|
||||||
|
|
||||||
|
//Read temp.
|
||||||
|
temp, err := sensor.ReadTemperature()
|
||||||
|
if err != nil {
|
||||||
|
println("MCP9808 error reading temperature")
|
||||||
|
println(err.Error())
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
fmt.Printf("Temperature: %.2f \n", temp)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -9,7 +9,7 @@
|
|||||||
// examples/net/webclient (for HTTP)
|
// examples/net/webclient (for HTTP)
|
||||||
// examples/net/tlsclient (for HTTPS)
|
// examples/net/tlsclient (for HTTPS)
|
||||||
|
|
||||||
//go:build pyportal || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 || wioterminal
|
//go:build ninafw || wioterminal
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
// examples/net/webclient (for HTTP)
|
// examples/net/webclient (for HTTP)
|
||||||
// examples/net/tlsclient (for HTTPS)
|
// examples/net/tlsclient (for HTTPS)
|
||||||
|
|
||||||
//go:build pyportal || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 || wioterminal
|
//go:build ninafw || wioterminal
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
// examples/net/webclient (for HTTP)
|
// examples/net/webclient (for HTTP)
|
||||||
// examples/net/tlsclient (for HTTPS)
|
// examples/net/tlsclient (for HTTPS)
|
||||||
|
|
||||||
//go:build pyportal || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 || wioterminal
|
//go:build ninafw || wioterminal
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
// examples/net/webclient (for HTTP)
|
// examples/net/webclient (for HTTP)
|
||||||
// examples/net/tlsclient (for HTTPS)
|
// examples/net/tlsclient (for HTTPS)
|
||||||
|
|
||||||
//go:build pyportal || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 || wioterminal
|
//go:build ninafw || wioterminal
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
// Note: It may be necessary to increase the stack size when using
|
// Note: It may be necessary to increase the stack size when using
|
||||||
// paho.mqtt.golang. Use the -stack-size=4KB command line option.
|
// paho.mqtt.golang. Use the -stack-size=4KB command line option.
|
||||||
|
|
||||||
//go:build pyportal || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 || wioterminal || challenger_rp2040
|
//go:build ninafw || wioterminal || challenger_rp2040
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
// Note: It may be necessary to increase the stack size when using
|
// Note: It may be necessary to increase the stack size when using
|
||||||
// paho.mqtt.golang. Use the -stack-size=4KB command line option.
|
// paho.mqtt.golang. Use the -stack-size=4KB command line option.
|
||||||
|
|
||||||
//go:build pyportal || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 || wioterminal || challenger_rp2040
|
//go:build ninafw || wioterminal || challenger_rp2040
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
// It creates a UDP connection to request the current time and parse the
|
// It creates a UDP connection to request the current time and parse the
|
||||||
// response from a NTP server. The system time is set to NTP time.
|
// response from a NTP server. The system time is set to NTP time.
|
||||||
|
|
||||||
//go:build pyportal || arduino_nano33 || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 || wioterminal || challenger_rp2040
|
//go:build ninafw || wioterminal || challenger_rp2040
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
//go:build ninafw || wioterminal
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"tinygo.org/x/drivers/netlink"
|
||||||
|
"tinygo.org/x/drivers/netlink/probe"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ssid string
|
||||||
|
pass string
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
time.Sleep(2 * time.Second)
|
||||||
|
|
||||||
|
link, _ := probe.Probe()
|
||||||
|
|
||||||
|
err := link.NetConnect(&netlink.ConnectParams{
|
||||||
|
Ssid: ssid,
|
||||||
|
Passphrase: pass,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,120 @@
|
|||||||
|
// This example is the classic snake network test. The snake is feed a steady
|
||||||
|
// diet of pkts and the pkts work themselves thru the snake segments and exit
|
||||||
|
// the tail. Each snake segment is a TCP socket connection to a server. The
|
||||||
|
// server echos pkts received back to the snake, and serves each segment on a
|
||||||
|
// different port. (See server/main.go for server).
|
||||||
|
//
|
||||||
|
// snake | server
|
||||||
|
// |
|
||||||
|
// head ----->---|-->--+
|
||||||
|
// seg a | |
|
||||||
|
// +---<-|--<--+
|
||||||
|
// | |
|
||||||
|
// +-->--|-->--+
|
||||||
|
// seg b | |
|
||||||
|
// +---<-|--<--+
|
||||||
|
// | |
|
||||||
|
// +-->--|-->--+
|
||||||
|
// seg c | |
|
||||||
|
// +---<-|--<--+
|
||||||
|
// | |
|
||||||
|
// +-->--|-->--+
|
||||||
|
// ... | |
|
||||||
|
// +---<-|--<--+
|
||||||
|
// | |
|
||||||
|
// +-->--|-->--+
|
||||||
|
// seg n | |
|
||||||
|
// tail -------<-|--<--+
|
||||||
|
// |
|
||||||
|
|
||||||
|
// The snake segments are linked by channels and each segment is run as a go
|
||||||
|
// func. This forces segments to connect and run concurrently, which is a good
|
||||||
|
// test of the underlying driver's ability to handle concurrent connections.
|
||||||
|
|
||||||
|
//go:build ninafw || wioterminal
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
_ "embed"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:embed main.go
|
||||||
|
var code string
|
||||||
|
|
||||||
|
var (
|
||||||
|
server string = "10.0.0.100:8080"
|
||||||
|
)
|
||||||
|
|
||||||
|
func segment(in chan []byte, out chan []byte) {
|
||||||
|
var buf [512]byte
|
||||||
|
for {
|
||||||
|
c, err := net.Dial("tcp", server)
|
||||||
|
for ; err != nil; c, err = net.Dial("tcp", server) {
|
||||||
|
println(err.Error())
|
||||||
|
time.Sleep(5 * time.Second)
|
||||||
|
}
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case msg := <-in:
|
||||||
|
_, err := c.Write(msg)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err.Error())
|
||||||
|
}
|
||||||
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
n, err := c.Read(buf[:])
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err.Error())
|
||||||
|
}
|
||||||
|
out <- buf[:n]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func feedit(head chan []byte) {
|
||||||
|
for i := 0; i < 100; i++ {
|
||||||
|
head <- []byte(fmt.Sprintf("\n---%d---\n", i))
|
||||||
|
for _, line := range strings.Split(code, "\n") {
|
||||||
|
if len(line) == 0 {
|
||||||
|
line = " "
|
||||||
|
}
|
||||||
|
head <- []byte(line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var head = make(chan []byte)
|
||||||
|
var a = make(chan []byte)
|
||||||
|
var b = make(chan []byte)
|
||||||
|
var c = make(chan []byte)
|
||||||
|
var d = make(chan []byte)
|
||||||
|
var e = make(chan []byte)
|
||||||
|
var f = make(chan []byte)
|
||||||
|
var tail = make(chan []byte)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
|
||||||
|
// The snake
|
||||||
|
go segment(head, a)
|
||||||
|
go segment(a, b)
|
||||||
|
go segment(b, c)
|
||||||
|
go segment(c, d)
|
||||||
|
go segment(d, e)
|
||||||
|
go segment(e, f)
|
||||||
|
go segment(f, tail)
|
||||||
|
|
||||||
|
go feedit(head)
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case msg := <-tail:
|
||||||
|
println(string(msg))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// Listen for connections
|
||||||
|
l, err := net.Listen("tcp", ":8080")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err.Error())
|
||||||
|
}
|
||||||
|
defer l.Close()
|
||||||
|
println("Listening on port", ":8080")
|
||||||
|
for {
|
||||||
|
// Wait for a connection
|
||||||
|
conn, err := l.Accept()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
println("Accepted connection from", conn.RemoteAddr().String())
|
||||||
|
// Service the new connection in a goroutine.
|
||||||
|
// The loop then returns to accepting, so that
|
||||||
|
// multiple connections may be served concurrently
|
||||||
|
go func(c net.Conn) {
|
||||||
|
// Echo all incoming data
|
||||||
|
io.Copy(c, c)
|
||||||
|
// Shut down the connection
|
||||||
|
c.Close()
|
||||||
|
}(conn)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
//
|
//
|
||||||
// nc -lk 8080
|
// nc -lk 8080
|
||||||
|
|
||||||
//go:build pyportal || arduino_nano33 || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 || wioterminal || challenger_rp2040
|
//go:build ninafw || wioterminal || challenger_rp2040
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
//
|
//
|
||||||
// nc -lk 8080
|
// nc -lk 8080
|
||||||
|
|
||||||
//go:build pyportal || arduino_nano33 || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 || wioterminal || challenger_rp2040 || pico
|
//go:build ninafw || wioterminal || challenger_rp2040 || pico
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
//
|
//
|
||||||
// $ nc 10.0.0.2 8080 <file >copy ; cmp file copy
|
// $ nc 10.0.0.2 8080 <file >copy ; cmp file copy
|
||||||
|
|
||||||
//go:build pyportal || arduino_nano33 || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 || wioterminal
|
//go:build ninafw || wioterminal
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
@@ -30,16 +30,18 @@ var (
|
|||||||
var buf [1024]byte
|
var buf [1024]byte
|
||||||
|
|
||||||
func echo(conn net.Conn) {
|
func echo(conn net.Conn) {
|
||||||
|
println("Client", conn.RemoteAddr(), "connected")
|
||||||
defer conn.Close()
|
defer conn.Close()
|
||||||
_, err := io.CopyBuffer(conn, conn, buf[:])
|
_, err := io.CopyBuffer(conn, conn, buf[:])
|
||||||
if err != nil && err != io.EOF {
|
if err != nil && err != io.EOF {
|
||||||
log.Fatal(err.Error())
|
log.Fatal(err.Error())
|
||||||
}
|
}
|
||||||
|
println("Client", conn.RemoteAddr(), "closed")
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
||||||
time.Sleep(time.Second)
|
time.Sleep(2 * time.Second)
|
||||||
|
|
||||||
link, _ := probe.Probe()
|
link, _ := probe.Probe()
|
||||||
|
|
||||||
@@ -51,6 +53,7 @@ func main() {
|
|||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
println("Starting TCP server listening on", port)
|
||||||
l, err := net.Listen("tcp", port)
|
l, err := net.Listen("tcp", port)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err.Error())
|
log.Fatal(err.Error())
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
//
|
//
|
||||||
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security
|
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security
|
||||||
|
|
||||||
//go:build pyportal || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 || wioterminal
|
//go:build ninafw || wioterminal
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
// }
|
// }
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
//go:build pyportal || arduino_nano33 || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 || wioterminal
|
//go:build ninafw || wioterminal
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
// Note: It may be necessary to increase the stack size when using "net/http".
|
// Note: It may be necessary to increase the stack size when using "net/http".
|
||||||
// Use the -stack-size=4KB command line option.
|
// Use the -stack-size=4KB command line option.
|
||||||
|
|
||||||
//go:build pyportal || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 || wioterminal
|
//go:build ninafw || wioterminal
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
// Note: It may be necessary to increase the stack size when using
|
// Note: It may be necessary to increase the stack size when using
|
||||||
// "golang.org/x/net/websocket". Use the -stack-size=4KB command line option.
|
// "golang.org/x/net/websocket". Use the -stack-size=4KB command line option.
|
||||||
|
|
||||||
//go:build pyportal || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 || wioterminal
|
//go:build ninafw || wioterminal
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
// Note: It may be necessary to increase the stack size when using
|
// Note: It may be necessary to increase the stack size when using
|
||||||
// "golang.org/x/net/websocket". Use the -stack-size=4KB command line option.
|
// "golang.org/x/net/websocket". Use the -stack-size=4KB command line option.
|
||||||
|
|
||||||
//go:build pyportal || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 || wioterminal
|
//go:build ninafw || wioterminal
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
// Note: It may be necessary to increase the stack size when using "net/http".
|
// Note: It may be necessary to increase the stack size when using "net/http".
|
||||||
// Use the -stack-size=4KB command line option.
|
// Use the -stack-size=4KB command line option.
|
||||||
|
|
||||||
//go:build pyportal || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 || wioterminal
|
//go:build ninafw || wioterminal
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
// Connects to a pcf8591 ADC via I2C.
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"machine"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"tinygo.org/x/drivers/pcf8591"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
i2c = machine.I2C0
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
i2c.Configure(machine.I2CConfig{})
|
||||||
|
adc := pcf8591.New(i2c)
|
||||||
|
adc.Configure()
|
||||||
|
|
||||||
|
// get "CH0" aka "machine.ADC" interface to channel 0 from ADC.
|
||||||
|
p := adc.CH0
|
||||||
|
|
||||||
|
for {
|
||||||
|
val := p.Get()
|
||||||
|
println(val)
|
||||||
|
time.Sleep(50 * time.Millisecond)
|
||||||
|
}
|
||||||
|
}
|
||||||
+19
-13
@@ -25,19 +25,25 @@ func main() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
println("setting to 0°")
|
|
||||||
s.SetMicroseconds(1000)
|
|
||||||
time.Sleep(3 * time.Second)
|
|
||||||
|
|
||||||
println("setting to 45°")
|
|
||||||
s.SetMicroseconds(1500)
|
|
||||||
time.Sleep(3 * time.Second)
|
|
||||||
|
|
||||||
println("setting to 90°")
|
|
||||||
s.SetMicroseconds(2000)
|
|
||||||
time.Sleep(3 * time.Second)
|
|
||||||
|
|
||||||
for {
|
for {
|
||||||
time.Sleep(time.Second)
|
println("setting to 0°")
|
||||||
|
s.SetAngle(0)
|
||||||
|
time.Sleep(3 * time.Second)
|
||||||
|
|
||||||
|
println("setting to 45°")
|
||||||
|
s.SetAngle(45)
|
||||||
|
time.Sleep(3 * time.Second)
|
||||||
|
|
||||||
|
println("setting to 90°")
|
||||||
|
s.SetAngle(90)
|
||||||
|
time.Sleep(3 * time.Second)
|
||||||
|
|
||||||
|
println("setting to 135°")
|
||||||
|
s.SetAngle(135)
|
||||||
|
time.Sleep(3 * time.Second)
|
||||||
|
|
||||||
|
println("setting to 180°")
|
||||||
|
s.SetAngle(180)
|
||||||
|
time.Sleep(3 * time.Second)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
// This example using the SSD1306 OLED display over SPI on the Thumby board
|
||||||
|
// A very tiny 72x40 display.
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image/color"
|
||||||
|
"machine"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"tinygo.org/x/drivers/ssd1306"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
machine.SPI0.Configure(machine.SPIConfig{})
|
||||||
|
display := ssd1306.NewSPI(machine.SPI0, machine.THUMBY_DC_PIN, machine.THUMBY_RESET_PIN, machine.THUMBY_CS_PIN)
|
||||||
|
display.Configure(ssd1306.Config{
|
||||||
|
Width: 72,
|
||||||
|
Height: 40,
|
||||||
|
ResetCol: ssd1306.ResetValue{28, 99},
|
||||||
|
ResetPage: ssd1306.ResetValue{0, 5},
|
||||||
|
})
|
||||||
|
|
||||||
|
display.ClearDisplay()
|
||||||
|
|
||||||
|
x := int16(36)
|
||||||
|
y := int16(20)
|
||||||
|
deltaX := int16(1)
|
||||||
|
deltaY := int16(1)
|
||||||
|
for {
|
||||||
|
pixel := display.GetPixel(x, y)
|
||||||
|
c := color.RGBA{255, 255, 255, 255}
|
||||||
|
if pixel {
|
||||||
|
c = color.RGBA{0, 0, 0, 255}
|
||||||
|
}
|
||||||
|
display.SetPixel(x, y, c)
|
||||||
|
display.Display()
|
||||||
|
|
||||||
|
x += deltaX
|
||||||
|
y += deltaY
|
||||||
|
|
||||||
|
if x == 0 || x == 71 {
|
||||||
|
deltaX = -deltaX
|
||||||
|
}
|
||||||
|
|
||||||
|
if y == 0 || y == 39 {
|
||||||
|
deltaY = -deltaY
|
||||||
|
}
|
||||||
|
time.Sleep(1 * time.Millisecond)
|
||||||
|
}
|
||||||
|
}
|
||||||
+40
-12
@@ -3,7 +3,9 @@ package main
|
|||||||
import (
|
import (
|
||||||
"image/color"
|
"image/color"
|
||||||
"machine"
|
"machine"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"tinygo.org/x/drivers"
|
||||||
"tinygo.org/x/drivers/uc8151"
|
"tinygo.org/x/drivers/uc8151"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -14,30 +16,56 @@ func main() {
|
|||||||
led = machine.LED
|
led = machine.LED
|
||||||
led.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
led.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||||
machine.SPI0.Configure(machine.SPIConfig{
|
machine.SPI0.Configure(machine.SPIConfig{
|
||||||
Frequency: 12000000,
|
Frequency: 12 * machine.MHz,
|
||||||
SCK: machine.EPD_SCK_PIN,
|
SCK: machine.EPD_SCK_PIN,
|
||||||
SDO: machine.EPD_SDO_PIN,
|
SDO: machine.EPD_SDO_PIN,
|
||||||
})
|
})
|
||||||
|
|
||||||
display = uc8151.New(machine.SPI0, machine.EPD_CS_PIN, machine.EPD_DC_PIN, machine.EPD_RESET_PIN, machine.EPD_BUSY_PIN)
|
display = uc8151.New(machine.SPI0, machine.EPD_CS_PIN, machine.EPD_DC_PIN, machine.EPD_RESET_PIN, machine.EPD_BUSY_PIN)
|
||||||
display.Configure(uc8151.Config{
|
display.Configure(uc8151.Config{
|
||||||
Rotation: uc8151.ROTATION_270,
|
Rotation: drivers.Rotation270,
|
||||||
Speed: uc8151.MEDIUM,
|
Speed: uc8151.TURBO,
|
||||||
Blocking: true,
|
FlickerFree: true,
|
||||||
|
Blocking: false,
|
||||||
})
|
})
|
||||||
black := color.RGBA{1, 1, 1, 255}
|
black := color.RGBA{1, 1, 1, 255}
|
||||||
|
|
||||||
display.ClearBuffer()
|
display.ClearDisplay()
|
||||||
display.Display()
|
|
||||||
for i := int16(0); i < 37; i++ {
|
mod := int16(1)
|
||||||
for j := int16(0); j < 16; j++ {
|
for {
|
||||||
if (i+j)%2 == 0 {
|
// checkerboard
|
||||||
showRect(i*8, j*8, 8, 8, black)
|
for i := int16(0); i < 11; i++ {
|
||||||
|
if mod == 1 {
|
||||||
|
mod = 0
|
||||||
|
} else {
|
||||||
|
mod = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
display.ClearBuffer()
|
||||||
|
for i := int16(0); i < 37; i++ {
|
||||||
|
for j := int16(0); j < 16; j++ {
|
||||||
|
if (i+j)%2 == mod {
|
||||||
|
showRect(i*8, j*8, 8, 8, black)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
display.Display()
|
||||||
|
time.Sleep(500 * time.Millisecond)
|
||||||
|
}
|
||||||
|
|
||||||
|
// moving line
|
||||||
|
for i := int16(16); i < 21; i++ {
|
||||||
|
display.ClearBuffer()
|
||||||
|
for j := int16(0); j < 16; j++ {
|
||||||
|
if (i+j)%2 == 0 {
|
||||||
|
showRect(i*8, j*8, 8, 8, black)
|
||||||
|
}
|
||||||
|
display.Display()
|
||||||
|
time.Sleep(250 * time.Millisecond)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
display.Display()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func showRect(x int16, y int16, w int16, h int16, c color.RGBA) {
|
func showRect(x int16, y int16, w int16, h int16, c color.RGBA) {
|
||||||
|
|||||||
@@ -0,0 +1,84 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image/color"
|
||||||
|
"machine"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"tinygo.org/x/drivers"
|
||||||
|
"tinygo.org/x/drivers/waveshare-epd/epd2in66b"
|
||||||
|
"tinygo.org/x/tinyfont"
|
||||||
|
"tinygo.org/x/tinyfont/freemono"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
black = color.RGBA{0, 0, 0, 0xff}
|
||||||
|
white = color.RGBA{0xff, 0xff, 0xff, 0xff}
|
||||||
|
red = color.RGBA{0xff, 0, 0, 0xff}
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
machine.Serial.Configure(machine.UARTConfig{})
|
||||||
|
time.Sleep(2 * time.Second)
|
||||||
|
|
||||||
|
machine.SPI1.Configure(machine.SPIConfig{
|
||||||
|
Frequency: epd2in66b.Baudrate,
|
||||||
|
})
|
||||||
|
|
||||||
|
println("started")
|
||||||
|
|
||||||
|
// in case you have a Pico module, you can directly use
|
||||||
|
// dev, err := epd2in66b.NewPicoModule()
|
||||||
|
|
||||||
|
display := epd2in66b.New(machine.SPI1)
|
||||||
|
|
||||||
|
cfg := epd2in66b.Config{
|
||||||
|
DataPin: machine.GP8,
|
||||||
|
ChipSelectPin: machine.GP9,
|
||||||
|
ResetPin: machine.GP12,
|
||||||
|
BusyPin: machine.GP13,
|
||||||
|
}
|
||||||
|
err := display.Configure(cfg)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = display.Reset()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
println("draw checkerboard")
|
||||||
|
drawCheckerBoard(&display)
|
||||||
|
|
||||||
|
println("draw 'hello'")
|
||||||
|
tinyfont.WriteLineRotated(&display, &freemono.Bold24pt7b, 40, 10, "Hello!", white, tinyfont.ROTATION_90)
|
||||||
|
tinyfont.WriteLineRotated(&display, &freemono.Bold12pt7b, 10, 10, "tinygo rocks", white, tinyfont.ROTATION_90)
|
||||||
|
err = display.Display()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func drawCheckerBoard(display drivers.Displayer) {
|
||||||
|
s := 8
|
||||||
|
width, height := display.Size()
|
||||||
|
for x := 0; x <= int(width)-s; x += s {
|
||||||
|
for y := 0; y <= int(height)-s; y += s {
|
||||||
|
c := red
|
||||||
|
if (x/s)%2 == (y/s)%2 {
|
||||||
|
c = black
|
||||||
|
}
|
||||||
|
|
||||||
|
showRect(display, x, y, s, s, c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func showRect(display drivers.Displayer, x int, y int, w int, h int, c color.RGBA) {
|
||||||
|
for i := x; i < x+w; i++ {
|
||||||
|
for j := y; j < y+h; j++ {
|
||||||
|
display.SetPixel(int16(i), int16(j), c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,7 +4,8 @@ package main
|
|||||||
|
|
||||||
import "machine"
|
import "machine"
|
||||||
|
|
||||||
// Replace neo and led in the code below to match the pin
|
func init() {
|
||||||
// that you are using if different.
|
// Replace neo in the code below to match the pin
|
||||||
var neo = machine.D2
|
// that you are using if different.
|
||||||
var led = machine.LED
|
neo = machine.D2
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,8 +4,9 @@ package main
|
|||||||
|
|
||||||
import "machine"
|
import "machine"
|
||||||
|
|
||||||
// This is the pin assignment for the Digispark only.
|
func init() {
|
||||||
// Replace neo and led in the code below to match the pin
|
// This is the pin assignment for the Digispark only.
|
||||||
// that you are using if different.
|
// Replace neo and led in the code below to match the pin
|
||||||
var neo machine.Pin = 0
|
// that you are using if different.
|
||||||
var led = machine.LED
|
neo = machine.Pin(0)
|
||||||
|
}
|
||||||
|
|||||||
@@ -12,11 +12,12 @@ import (
|
|||||||
"tinygo.org/x/drivers/ws2812"
|
"tinygo.org/x/drivers/ws2812"
|
||||||
)
|
)
|
||||||
|
|
||||||
var leds [10]color.RGBA
|
var (
|
||||||
|
neo machine.Pin
|
||||||
|
leds [10]color.RGBA
|
||||||
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
led.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
|
||||||
|
|
||||||
neo.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
neo.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||||
|
|
||||||
ws := ws2812.NewWS2812(neo)
|
ws := ws2812.NewWS2812(neo)
|
||||||
@@ -35,7 +36,6 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ws.WriteColors(leds[:])
|
ws.WriteColors(leds[:])
|
||||||
led.Set(rg)
|
|
||||||
time.Sleep(100 * time.Millisecond)
|
time.Sleep(100 * time.Millisecond)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
//go:build !digispark && !arduino && !qtpy && !m5stamp_c3 && !thingplus_rp2040
|
//go:build !digispark && !arduino
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import "machine"
|
import "machine"
|
||||||
|
|
||||||
// Replace neo and led in the code below to match the pin
|
func init() {
|
||||||
// that you are using if different.
|
// Replace neo in the code below to match the pin
|
||||||
var neo machine.Pin = machine.WS2812
|
// that you are using if different.
|
||||||
var led = machine.LED
|
neo = machine.WS2812
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,10 +0,0 @@
|
|||||||
//go:build qtpy || m5stamp_c3
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import "machine"
|
|
||||||
|
|
||||||
// Replace neo and led in the code below to match the pin
|
|
||||||
// that you are using if different.
|
|
||||||
var neo machine.Pin = machine.WS2812
|
|
||||||
var led = machine.NoPin
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
//go:build thingplus_rp2040
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import "machine"
|
|
||||||
|
|
||||||
// This is the pin assignment for the internal neopixel of the
|
|
||||||
// Sparkfun thingplus rp2040.
|
|
||||||
// Replace neo and led in the code below to match the pin
|
|
||||||
// that you are using if different.
|
|
||||||
var neo machine.Pin = machine.GPIO8
|
|
||||||
var led = machine.LED
|
|
||||||
+2
-1
@@ -80,7 +80,8 @@ func (d *Device) Read() []byte {
|
|||||||
func (d *Device) ReadTouchPoint() touch.Point {
|
func (d *Device) ReadTouchPoint() touch.Point {
|
||||||
d.Read()
|
d.Read()
|
||||||
z := 0xFFFFF
|
z := 0xFFFFF
|
||||||
if d.buf[0] == 0 {
|
switch d.buf[0] {
|
||||||
|
case 0, 255:
|
||||||
z = 0
|
z = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ module tinygo.org/x/drivers
|
|||||||
|
|
||||||
go 1.18
|
go 1.18
|
||||||
|
|
||||||
|
replace tinygo.org/x/drivers/mcp9808 => /home/kasterby/Documents/drivers/mcp9808
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/eclipse/paho.mqtt.golang v1.2.0
|
github.com/eclipse/paho.mqtt.golang v1.2.0
|
||||||
github.com/frankban/quicktest v1.10.2
|
github.com/frankban/quicktest v1.10.2
|
||||||
|
|||||||
+112
-1
@@ -236,16 +236,83 @@ func (d *Device) getMode() (byte, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *Device) configRate(speed, clock byte) error {
|
func (d *Device) configRate(speed, clock byte) error {
|
||||||
// TODO: add another baudrate
|
|
||||||
var cfg1, cfg2, cfg3 byte
|
var cfg1, cfg2, cfg3 byte
|
||||||
set := true
|
set := true
|
||||||
switch clock {
|
switch clock {
|
||||||
case Clock16MHz:
|
case Clock16MHz:
|
||||||
switch speed {
|
switch speed {
|
||||||
|
case CAN5kBps:
|
||||||
|
cfg1 = mcp16mHz5kBpsCfg1
|
||||||
|
cfg2 = mcp16mHz5kBpsCfg2
|
||||||
|
cfg3 = mcp16mHz5kBpsCfg3
|
||||||
|
case CAN10kBps:
|
||||||
|
cfg1 = mcp16mHz10kBpsCfg1
|
||||||
|
cfg2 = mcp16mHz10kBpsCfg2
|
||||||
|
cfg3 = mcp16mHz10kBpsCfg3
|
||||||
|
case CAN20kBps:
|
||||||
|
cfg1 = mcp16mHz20kBpsCfg1
|
||||||
|
cfg2 = mcp16mHz20kBpsCfg2
|
||||||
|
cfg3 = mcp16mHz20kBpsCfg3
|
||||||
|
case CAN25kBps:
|
||||||
|
cfg1 = mcp16mHz25kBpsCfg1
|
||||||
|
cfg2 = mcp16mHz25kBpsCfg2
|
||||||
|
cfg3 = mcp16mHz25kBpsCfg3
|
||||||
|
case CAN31k25Bps:
|
||||||
|
cfg1 = mcp16mHz31k25BpsCfg1
|
||||||
|
cfg2 = mcp16mHz31k25BpsCfg2
|
||||||
|
cfg3 = mcp16mHz31k25BpsCfg3
|
||||||
|
case CAN33kBps:
|
||||||
|
cfg1 = mcp16mHz33kBpsCfg1
|
||||||
|
cfg2 = mcp16mHz33kBpsCfg2
|
||||||
|
cfg3 = mcp16mHz33kBpsCfg3
|
||||||
|
case CAN40kBps:
|
||||||
|
cfg1 = mcp16mHz40kBpsCfg1
|
||||||
|
cfg2 = mcp16mHz40kBpsCfg2
|
||||||
|
cfg3 = mcp16mHz40kBpsCfg3
|
||||||
|
case CAN47kBps:
|
||||||
|
cfg1 = mcp16mHz47kBpsCfg1
|
||||||
|
cfg2 = mcp16mHz47kBpsCfg2
|
||||||
|
cfg3 = mcp16mHz47kBpsCfg3
|
||||||
|
case CAN50kBps:
|
||||||
|
cfg1 = mcp16mHz50kBpsCfg1
|
||||||
|
cfg2 = mcp16mHz50kBpsCfg2
|
||||||
|
cfg3 = mcp16mHz50kBpsCfg3
|
||||||
|
case CAN80kBps:
|
||||||
|
cfg1 = mcp16mHz80kBpsCfg1
|
||||||
|
cfg2 = mcp16mHz80kBpsCfg2
|
||||||
|
cfg3 = mcp16mHz80kBpsCfg3
|
||||||
|
case CAN83k3Bps:
|
||||||
|
cfg1 = mcp16mHz83k3BpsCfg1
|
||||||
|
cfg2 = mcp16mHz83k3BpsCfg2
|
||||||
|
cfg3 = mcp16mHz83k3BpsCfg3
|
||||||
|
case CAN95kBps:
|
||||||
|
cfg1 = mcp16mHz95kBpsCfg1
|
||||||
|
cfg2 = mcp16mHz95kBpsCfg2
|
||||||
|
cfg3 = mcp16mHz95kBpsCfg3
|
||||||
|
case CAN100kBps:
|
||||||
|
cfg1 = mcp16mHz100kBpsCfg1
|
||||||
|
cfg2 = mcp16mHz100kBpsCfg2
|
||||||
|
cfg3 = mcp16mHz100kBpsCfg3
|
||||||
|
case CAN125kBps:
|
||||||
|
cfg1 = mcp16mHz125kBpsCfg1
|
||||||
|
cfg2 = mcp16mHz125kBpsCfg2
|
||||||
|
cfg3 = mcp16mHz125kBpsCfg3
|
||||||
|
case CAN200kBps:
|
||||||
|
cfg1 = mcp16mHz200kBpsCfg1
|
||||||
|
cfg2 = mcp16mHz200kBpsCfg2
|
||||||
|
cfg3 = mcp16mHz200kBpsCfg3
|
||||||
|
case CAN250kBps:
|
||||||
|
cfg1 = mcp16mHz250kBpsCfg1
|
||||||
|
cfg2 = mcp16mHz250kBpsCfg2
|
||||||
|
cfg3 = mcp16mHz250kBpsCfg3
|
||||||
case CAN500kBps:
|
case CAN500kBps:
|
||||||
cfg1 = mcp16mHz500kBpsCfg1
|
cfg1 = mcp16mHz500kBpsCfg1
|
||||||
cfg2 = mcp16mHz500kBpsCfg2
|
cfg2 = mcp16mHz500kBpsCfg2
|
||||||
cfg3 = mcp16mHz500kBpsCfg3
|
cfg3 = mcp16mHz500kBpsCfg3
|
||||||
|
case CAN666kBps:
|
||||||
|
cfg1 = mcp16mHz666kBpsCfg1
|
||||||
|
cfg2 = mcp16mHz666kBpsCfg2
|
||||||
|
cfg3 = mcp16mHz666kBpsCfg3
|
||||||
case CAN1000kBps:
|
case CAN1000kBps:
|
||||||
cfg1 = mcp16mHz1000kBpsCfg1
|
cfg1 = mcp16mHz1000kBpsCfg1
|
||||||
cfg2 = mcp16mHz1000kBpsCfg2
|
cfg2 = mcp16mHz1000kBpsCfg2
|
||||||
@@ -255,6 +322,50 @@ func (d *Device) configRate(speed, clock byte) error {
|
|||||||
}
|
}
|
||||||
case Clock8MHz:
|
case Clock8MHz:
|
||||||
switch speed {
|
switch speed {
|
||||||
|
case CAN5kBps:
|
||||||
|
cfg1 = mcp8mHz5kBpsCfg1
|
||||||
|
cfg2 = mcp8mHz5kBpsCfg2
|
||||||
|
cfg3 = mcp8mHz5kBpsCfg3
|
||||||
|
case CAN10kBps:
|
||||||
|
cfg1 = mcp8mHz10kBpsCfg1
|
||||||
|
cfg2 = mcp8mHz10kBpsCfg2
|
||||||
|
cfg3 = mcp8mHz10kBpsCfg3
|
||||||
|
case CAN20kBps:
|
||||||
|
cfg1 = mcp8mHz20kBpsCfg1
|
||||||
|
cfg2 = mcp8mHz20kBpsCfg2
|
||||||
|
cfg3 = mcp8mHz20kBpsCfg3
|
||||||
|
case CAN31k25Bps:
|
||||||
|
cfg1 = mcp8mHz31k25BpsCfg1
|
||||||
|
cfg2 = mcp8mHz31k25BpsCfg2
|
||||||
|
cfg3 = mcp8mHz31k25BpsCfg3
|
||||||
|
case CAN40kBps:
|
||||||
|
cfg1 = mcp8mHz40kBpsCfg1
|
||||||
|
cfg2 = mcp8mHz40kBpsCfg2
|
||||||
|
cfg3 = mcp8mHz40kBpsCfg3
|
||||||
|
case CAN50kBps:
|
||||||
|
cfg1 = mcp8mHz50kBpsCfg1
|
||||||
|
cfg2 = mcp8mHz50kBpsCfg2
|
||||||
|
cfg3 = mcp8mHz50kBpsCfg3
|
||||||
|
case CAN80kBps:
|
||||||
|
cfg1 = mcp8mHz80kBpsCfg1
|
||||||
|
cfg2 = mcp8mHz80kBpsCfg2
|
||||||
|
cfg3 = mcp8mHz80kBpsCfg3
|
||||||
|
case CAN100kBps:
|
||||||
|
cfg1 = mcp8mHz100kBpsCfg1
|
||||||
|
cfg2 = mcp8mHz100kBpsCfg2
|
||||||
|
cfg3 = mcp8mHz100kBpsCfg3
|
||||||
|
case CAN125kBps:
|
||||||
|
cfg1 = mcp8mHz125kBpsCfg1
|
||||||
|
cfg2 = mcp8mHz125kBpsCfg2
|
||||||
|
cfg3 = mcp8mHz125kBpsCfg3
|
||||||
|
case CAN200kBps:
|
||||||
|
cfg1 = mcp8mHz200kBpsCfg1
|
||||||
|
cfg2 = mcp8mHz200kBpsCfg2
|
||||||
|
cfg3 = mcp8mHz200kBpsCfg3
|
||||||
|
case CAN250kBps:
|
||||||
|
cfg1 = mcp8mHz250kBpsCfg1
|
||||||
|
cfg2 = mcp8mHz250kBpsCfg2
|
||||||
|
cfg3 = mcp8mHz250kBpsCfg3
|
||||||
case CAN500kBps:
|
case CAN500kBps:
|
||||||
cfg1 = mcp8mHz500kBpsCfg1
|
cfg1 = mcp8mHz500kBpsCfg1
|
||||||
cfg2 = mcp8mHz500kBpsCfg2
|
cfg2 = mcp8mHz500kBpsCfg2
|
||||||
|
|||||||
@@ -0,0 +1,98 @@
|
|||||||
|
// Package mcp9808 implements a driver for the MCP9808 High Accuracy I2C Temperature Sensor
|
||||||
|
//
|
||||||
|
// Datasheet: https://cdn-shop.adafruit.com/datasheets/MCP9808.pdf
|
||||||
|
// Module: https://www.adafruit.com/product/1782
|
||||||
|
// Only implemented: temperature reading, resolution read & set
|
||||||
|
package mcp9808
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"tinygo.org/x/drivers"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Device struct {
|
||||||
|
bus drivers.I2C
|
||||||
|
Address uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(bus drivers.I2C) Device {
|
||||||
|
return Device{bus, MCP9808_I2CADDR_DEFAULT}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Device) Connected() bool {
|
||||||
|
data := make([]byte, 2)
|
||||||
|
d.Read(MCP9808_REG_DEVICE_ID, &data)
|
||||||
|
return binary.BigEndian.Uint16(data) == MCP9808_DEVICE_ID
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Device) ReadTemperature() (float64, error) {
|
||||||
|
data := make([]byte, 2)
|
||||||
|
var temp float64
|
||||||
|
if err := d.Read(MCP9808_REG_AMBIENT_TEMP, &data); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
data[0] = data[0] & 0x1F
|
||||||
|
if data[0]&0x10 == 0x10 {
|
||||||
|
data[0] = data[0] & 0x0F
|
||||||
|
temp = float64(data[0])*16 + float64(data[1])/16.0 - 256
|
||||||
|
}
|
||||||
|
temp = float64(data[0])*16 + float64(data[1])/16.0
|
||||||
|
return temp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Device) ReadResolution() (resolution, error) {
|
||||||
|
data := make([]byte, 2)
|
||||||
|
err := d.Read(MCP9808_REG_RESOLUTION, &data)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
switch data[0] {
|
||||||
|
case 0:
|
||||||
|
return Low, nil
|
||||||
|
case 1:
|
||||||
|
return Medium, nil
|
||||||
|
case 2:
|
||||||
|
return High, nil
|
||||||
|
case 3:
|
||||||
|
return Maximum, nil
|
||||||
|
|
||||||
|
default:
|
||||||
|
return 0, errors.New("unknown resolution")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Device) SetResolution(r resolution) error {
|
||||||
|
switch r {
|
||||||
|
case Low:
|
||||||
|
if err := d.Write(MCP9808_REG_RESOLUTION, []byte{0x00}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
case Medium:
|
||||||
|
if err := d.Write(MCP9808_REG_RESOLUTION, []byte{0x01}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
case High:
|
||||||
|
if err := d.Write(MCP9808_REG_RESOLUTION, []byte{0x02}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
case Maximum:
|
||||||
|
if err := d.Write(MCP9808_REG_RESOLUTION, []byte{0x03}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Device) Write(register byte, data []byte) error {
|
||||||
|
buf := append([]byte{register}, data...)
|
||||||
|
return d.bus.Tx(d.Address, buf, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Device) Read(register byte, data *[]byte) error {
|
||||||
|
return d.bus.Tx(d.Address, []byte{register}, *data)
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
// Package mcp9808 implements a driver for the MCP9808 High Accuracy I2C Temperature Sensor
|
||||||
|
//
|
||||||
|
// Datasheet: https://cdn-shop.adafruit.com/datasheets/MCP9808.pdf
|
||||||
|
// Module: https://www.adafruit.com/product/1782
|
||||||
|
package mcp9808
|
||||||
|
|
||||||
|
// Constants/addresses used for I2C.
|
||||||
|
const (
|
||||||
|
MCP9808_DEVICE_ID = 0x0400
|
||||||
|
MCP9808_MANUF_ID = 0x0054
|
||||||
|
|
||||||
|
MCP9808_I2CADDR_DEFAULT = 0x18 //default I2C address
|
||||||
|
MCP9808_REG_CONFIG = 0x01 //MCP9808 config register
|
||||||
|
|
||||||
|
MCP9808_REG_CONFIG_SHUTDOWN = 0x0100 //shutdown config
|
||||||
|
MCP9808_REG_CONFIG_CRITLOCKED = 0x0080 //critical trip lock
|
||||||
|
MCP9808_REG_CONFIG_WINLOCKED = 0x0040 //alarm window lock
|
||||||
|
MCP9808_REG_CONFIG_INTCLR = 0x0020 //interrupt clear
|
||||||
|
MCP9808_REG_CONFIG_ALERTSTAT = 0x0010 //alert output status
|
||||||
|
MCP9808_REG_CONFIG_ALERTCTRL = 0x0008 //alert output control
|
||||||
|
MCP9808_REG_CONFIG_ALERTSEL = 0x0004 //alert output select
|
||||||
|
MCP9808_REG_CONFIG_ALERTPOL = 0x0002 //alert output polarity
|
||||||
|
MCP9808_REG_CONFIG_ALERTMODE = 0x0001 //alert output mode
|
||||||
|
|
||||||
|
MCP9808_REG_UPPER_TEMP = 0x02 //upper alert boundary
|
||||||
|
MCP9808_REG_LOWER_TEMP = 0x03 //lower alert boundery
|
||||||
|
MCP9808_REG_CRIT_TEMP = 0x04 //critical temperature
|
||||||
|
MCP9808_REG_AMBIENT_TEMP = 0x05 //ambient temperature
|
||||||
|
MCP9808_REG_MANUF_ID = 0x06 //manufacturer ID
|
||||||
|
MCP9808_REG_DEVICE_ID = 0x07 //device ID
|
||||||
|
MCP9808_REG_RESOLUTION = 0x08 //resolution
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
======= ============ ==============
|
||||||
|
value resolution reading Time
|
||||||
|
======= ============ ==============
|
||||||
|
|
||||||
|
0 0.5°C 30 ms
|
||||||
|
1 0.25°C 65 ms
|
||||||
|
2 0.125°C 130 ms
|
||||||
|
3 0.0625°C 250 ms
|
||||||
|
|
||||||
|
======= ============ ==============
|
||||||
|
*/
|
||||||
|
type resolution uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
Low resolution = iota
|
||||||
|
Medium
|
||||||
|
High
|
||||||
|
Maximum
|
||||||
|
)
|
||||||
+3
-1
@@ -36,9 +36,11 @@ var (
|
|||||||
ErrFamilyNotSupported = errors.New("Address family not supported")
|
ErrFamilyNotSupported = errors.New("Address family not supported")
|
||||||
ErrProtocolNotSupported = errors.New("Socket protocol/type not supported")
|
ErrProtocolNotSupported = errors.New("Socket protocol/type not supported")
|
||||||
ErrStartingDHCPClient = errors.New("Error starting DHPC client")
|
ErrStartingDHCPClient = errors.New("Error starting DHPC client")
|
||||||
|
ErrStartingDHCPServer = errors.New("Error starting DHPC server")
|
||||||
ErrNoMoreSockets = errors.New("No more sockets")
|
ErrNoMoreSockets = errors.New("No more sockets")
|
||||||
ErrClosingSocket = errors.New("Error closing socket")
|
ErrClosingSocket = errors.New("Error closing socket")
|
||||||
ErrNotSupported = errors.New("Not supported")
|
ErrNotSupported = errors.New("Not supported")
|
||||||
|
ErrInvalidSocketFd = errors.New("Invalid socket fd")
|
||||||
)
|
)
|
||||||
|
|
||||||
// Duplicate of non-exported net.errTimeout
|
// Duplicate of non-exported net.errTimeout
|
||||||
@@ -82,7 +84,7 @@ type Netdever interface {
|
|||||||
Bind(sockfd int, ip netip.AddrPort) error
|
Bind(sockfd int, ip netip.AddrPort) error
|
||||||
Connect(sockfd int, host string, ip netip.AddrPort) error
|
Connect(sockfd int, host string, ip netip.AddrPort) error
|
||||||
Listen(sockfd int, backlog int) error
|
Listen(sockfd int, backlog int) error
|
||||||
Accept(sockfd int, ip netip.AddrPort) (int, error)
|
Accept(sockfd int) (int, netip.AddrPort, error)
|
||||||
Send(sockfd int, buf []byte, flags int, deadline time.Time) (int, error)
|
Send(sockfd int, buf []byte, flags int, deadline time.Time) (int, error)
|
||||||
Recv(sockfd int, buf []byte, flags int, deadline time.Time) (int, error)
|
Recv(sockfd int, buf []byte, flags int, deadline time.Time) (int, error)
|
||||||
Close(sockfd int) error
|
Close(sockfd int) error
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ var (
|
|||||||
ErrConnectFailed = errors.New("Connect failed")
|
ErrConnectFailed = errors.New("Connect failed")
|
||||||
ErrConnectTimeout = errors.New("Connect timed out")
|
ErrConnectTimeout = errors.New("Connect timed out")
|
||||||
ErrMissingSSID = errors.New("Missing WiFi SSID")
|
ErrMissingSSID = errors.New("Missing WiFi SSID")
|
||||||
|
ErrShortPassphrase = errors.New("Invalid Wifi Passphrase < 8 chars")
|
||||||
ErrAuthFailure = errors.New("Wifi authentication failure")
|
ErrAuthFailure = errors.New("Wifi authentication failure")
|
||||||
ErrAuthTypeNoGood = errors.New("Wifi authorization type not supported")
|
ErrAuthTypeNoGood = errors.New("Wifi authorization type not supported")
|
||||||
ErrConnectModeNoGood = errors.New("Connect mode not supported")
|
ErrConnectModeNoGood = errors.New("Connect mode not supported")
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//go:build pyportal || arduino_nano33 || nano_rp2040 || metro_m4_airlift || matrixportal_m4
|
//go:build ninafw && !arduino_mkrwifi1010
|
||||||
|
|
||||||
package probe
|
package probe
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,84 @@
|
|||||||
|
// Package pcf8591 implements a driver for the PCF8591 Analog to Digital/Digital to Analog Converter.
|
||||||
|
//
|
||||||
|
// Datasheet: https://www.nxp.com/docs/en/data-sheet/PCF8591.pdf
|
||||||
|
package pcf8591 // import "tinygo.org/x/drivers/pcf8591"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"machine"
|
||||||
|
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"tinygo.org/x/drivers"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Device wraps PCF8591 ADC functions.
|
||||||
|
type Device struct {
|
||||||
|
bus drivers.I2C
|
||||||
|
Address uint16
|
||||||
|
CH0 ADCPin
|
||||||
|
CH1 ADCPin
|
||||||
|
CH2 ADCPin
|
||||||
|
CH3 ADCPin
|
||||||
|
}
|
||||||
|
|
||||||
|
// ADCPin is the implementation of the ADConverter interface.
|
||||||
|
type ADCPin struct {
|
||||||
|
machine.Pin
|
||||||
|
d *Device
|
||||||
|
}
|
||||||
|
|
||||||
|
// New returns a new PCF8591 driver. Pass in a fully configured I2C bus.
|
||||||
|
func New(b drivers.I2C) *Device {
|
||||||
|
d := &Device{
|
||||||
|
bus: b,
|
||||||
|
Address: defaultAddress,
|
||||||
|
}
|
||||||
|
|
||||||
|
// setup all channels
|
||||||
|
d.CH0 = d.GetADC(0)
|
||||||
|
d.CH1 = d.GetADC(1)
|
||||||
|
d.CH2 = d.GetADC(2)
|
||||||
|
d.CH3 = d.GetADC(3)
|
||||||
|
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure here just for interface compatibility.
|
||||||
|
func (d *Device) Configure() {
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read analog data from channel
|
||||||
|
func (d *Device) Read(ch int) (uint16, error) {
|
||||||
|
if ch < 0 || ch > 3 {
|
||||||
|
return 0, errors.New("invalid channel for pcf8591 Read")
|
||||||
|
}
|
||||||
|
|
||||||
|
return d.GetADC(ch).Get(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetADC returns an ADC for a specific channel.
|
||||||
|
func (d *Device) GetADC(ch int) ADCPin {
|
||||||
|
return ADCPin{machine.Pin(ch), d}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the current reading for a specific ADCPin.
|
||||||
|
func (p ADCPin) Get() uint16 {
|
||||||
|
// TODO: also implement DAC
|
||||||
|
tx := make([]byte, 2)
|
||||||
|
tx[0] = byte(p.Pin)
|
||||||
|
|
||||||
|
rx := make([]byte, 2)
|
||||||
|
|
||||||
|
// The result from the measurement triggered by the first write,
|
||||||
|
// however, the second write is required to get the result.
|
||||||
|
// See section 8.4 "A/D Conversion" in the datasheet for more info
|
||||||
|
p.d.bus.Tx(p.d.Address, tx, rx)
|
||||||
|
p.d.bus.Tx(p.d.Address, tx, rx)
|
||||||
|
|
||||||
|
// scale result to 16bit value like other ADCs
|
||||||
|
return uint16(rx[1] << 8)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure here just for interface compatibility.
|
||||||
|
func (p ADCPin) Configure() {
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package pcf8591
|
||||||
|
|
||||||
|
// PCF8591 Default Address
|
||||||
|
const defaultAddress = 0x48
|
||||||
|
|
||||||
|
// control bit for DAC
|
||||||
|
const PCF8591_ENABLE_DAC = 0x40
|
||||||
+49
-8
@@ -22,12 +22,13 @@ func NewImage[T Color](width, height int) Image[T] {
|
|||||||
}
|
}
|
||||||
var zeroColor T
|
var zeroColor T
|
||||||
var data unsafe.Pointer
|
var data unsafe.Pointer
|
||||||
if zeroColor.BitsPerPixel()%8 == 0 {
|
switch {
|
||||||
|
case zeroColor.BitsPerPixel()%8 == 0:
|
||||||
// Typical formats like RGB888 and RGB565.
|
// Typical formats like RGB888 and RGB565.
|
||||||
// Each color starts at a whole byte offset from the start.
|
// Each color starts at a whole byte offset from the start.
|
||||||
buf := make([]T, width*height)
|
buf := make([]T, width*height)
|
||||||
data = unsafe.Pointer(&buf[0])
|
data = unsafe.Pointer(&buf[0])
|
||||||
} else {
|
default:
|
||||||
// Formats like RGB444 that have 12 bits per pixel.
|
// Formats like RGB444 that have 12 bits per pixel.
|
||||||
// We access these as bytes, so allocate the buffer as a byte slice.
|
// We access these as bytes, so allocate the buffer as a byte slice.
|
||||||
bufBits := width * height * zeroColor.BitsPerPixel()
|
bufBits := width * height * zeroColor.BitsPerPixel()
|
||||||
@@ -80,10 +81,11 @@ func (img Image[T]) Len() int {
|
|||||||
func (img Image[T]) RawBuffer() []uint8 {
|
func (img Image[T]) RawBuffer() []uint8 {
|
||||||
var zeroColor T
|
var zeroColor T
|
||||||
var numBytes int
|
var numBytes int
|
||||||
if zeroColor.BitsPerPixel()%8 == 0 {
|
switch {
|
||||||
|
case zeroColor.BitsPerPixel()%8 == 0:
|
||||||
// Each color starts at a whole byte offset.
|
// Each color starts at a whole byte offset.
|
||||||
numBytes = int(unsafe.Sizeof(zeroColor)) * int(img.width) * int(img.height)
|
numBytes = int(unsafe.Sizeof(zeroColor)) * int(img.width) * int(img.height)
|
||||||
} else {
|
default:
|
||||||
// Formats like RGB444 that aren't a whole number of bytes.
|
// Formats like RGB444 that aren't a whole number of bytes.
|
||||||
numBits := zeroColor.BitsPerPixel() * int(img.width) * int(img.height)
|
numBits := zeroColor.BitsPerPixel() * int(img.width) * int(img.height)
|
||||||
numBytes = (numBits + 7) / 8 // round up (see NewImage)
|
numBytes = (numBits + 7) / 8 // round up (see NewImage)
|
||||||
@@ -99,7 +101,20 @@ func (img Image[T]) Size() (int, int) {
|
|||||||
func (img Image[T]) setPixel(index int, c T) {
|
func (img Image[T]) setPixel(index int, c T) {
|
||||||
var zeroColor T
|
var zeroColor T
|
||||||
|
|
||||||
if zeroColor.BitsPerPixel()%8 == 0 {
|
switch {
|
||||||
|
case zeroColor.BitsPerPixel() == 1:
|
||||||
|
// Monochrome.
|
||||||
|
x := index % int(img.width)
|
||||||
|
y := index / int(img.width)
|
||||||
|
offset := x + (y/8)*int(img.width)
|
||||||
|
ptr := (*byte)(unsafe.Add(img.data, offset))
|
||||||
|
if c != zeroColor {
|
||||||
|
*((*byte)(ptr)) |= 1 << uint8(y%8)
|
||||||
|
} else {
|
||||||
|
*((*byte)(ptr)) &^= 1 << uint8(y%8)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
case zeroColor.BitsPerPixel()%8 == 0:
|
||||||
// Each color starts at a whole byte offset.
|
// Each color starts at a whole byte offset.
|
||||||
// This is the easy case.
|
// This is the easy case.
|
||||||
offset := index * int(unsafe.Sizeof(zeroColor))
|
offset := index * int(unsafe.Sizeof(zeroColor))
|
||||||
@@ -147,7 +162,15 @@ func (img Image[T]) Get(x, y int) T {
|
|||||||
var zeroColor T
|
var zeroColor T
|
||||||
index := y*int(img.width) + x // index into img.data
|
index := y*int(img.width) + x // index into img.data
|
||||||
|
|
||||||
if zeroColor.BitsPerPixel()%8 == 0 {
|
switch {
|
||||||
|
case zeroColor.BitsPerPixel() == 1:
|
||||||
|
// Monochrome.
|
||||||
|
var c Monochrome
|
||||||
|
offset := x + (y/8)*int(img.width)
|
||||||
|
ptr := (*byte)(unsafe.Add(img.data, offset))
|
||||||
|
c = (*ptr >> uint8(y%8) & 0x1) == 1
|
||||||
|
return any(c).(T)
|
||||||
|
case zeroColor.BitsPerPixel()%8 == 0:
|
||||||
// Colors like RGB565, RGB888, etc.
|
// Colors like RGB565, RGB888, etc.
|
||||||
offset := index * int(unsafe.Sizeof(zeroColor))
|
offset := index * int(unsafe.Sizeof(zeroColor))
|
||||||
ptr := unsafe.Add(img.data, offset)
|
ptr := unsafe.Add(img.data, offset)
|
||||||
@@ -181,8 +204,26 @@ func (img Image[T]) Get(x, y int) T {
|
|||||||
func (img Image[T]) FillSolidColor(color T) {
|
func (img Image[T]) FillSolidColor(color T) {
|
||||||
var zeroColor T
|
var zeroColor T
|
||||||
|
|
||||||
// Fast pass for colors of 8, 16, 24, etc bytes in size.
|
switch {
|
||||||
if zeroColor.BitsPerPixel()%8 == 0 {
|
case zeroColor.BitsPerPixel() == 1:
|
||||||
|
// Monochrome.
|
||||||
|
var colorByte uint8
|
||||||
|
if color != zeroColor {
|
||||||
|
colorByte = 0xff
|
||||||
|
}
|
||||||
|
numBytes := int(img.width) * int(img.height) / 8
|
||||||
|
for i := 0; i < numBytes; i++ {
|
||||||
|
// TODO: this can be optimized a lot.
|
||||||
|
// - The store can be done as a 32-bit integer, after checking for
|
||||||
|
// alignment.
|
||||||
|
// - Perhaps the loop can be unrolled to improve copy performance.
|
||||||
|
ptr := (*byte)(unsafe.Add(img.data, i))
|
||||||
|
*((*byte)(ptr)) = colorByte
|
||||||
|
}
|
||||||
|
return
|
||||||
|
|
||||||
|
case zeroColor.BitsPerPixel()%8 == 0:
|
||||||
|
// Fast pass for colors of 8, 16, 24, etc bytes in size.
|
||||||
ptr := img.data
|
ptr := img.data
|
||||||
for i := 0; i < img.Len(); i++ {
|
for i := 0; i < img.Len(); i++ {
|
||||||
// TODO: this can be optimized a lot.
|
// TODO: this can be optimized a lot.
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
package pixel_test
|
package pixel_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
goimage "image"
|
||||||
"image/color"
|
"image/color"
|
||||||
|
"math/rand"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"tinygo.org/x/drivers/pixel"
|
"tinygo.org/x/drivers/pixel"
|
||||||
@@ -62,3 +64,101 @@ func TestImageRGB444BE(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestImageMonochrome(t *testing.T) {
|
||||||
|
image := pixel.NewImage[pixel.Monochrome](5, 3)
|
||||||
|
if width, height := image.Size(); width != 5 && height != 3 {
|
||||||
|
t.Errorf("image.Size(): expected 5, 3 but got %d, %d", width, height)
|
||||||
|
}
|
||||||
|
for _, expected := range []color.RGBA{
|
||||||
|
{R: 0xff, G: 0xff, B: 0xff},
|
||||||
|
{G: 0xff},
|
||||||
|
{R: 0xff, G: 0xff},
|
||||||
|
{G: 0xff, B: 0xff},
|
||||||
|
{R: 0x00},
|
||||||
|
{G: 0x00, A: 0xff},
|
||||||
|
{B: 0x00, A: 0xff},
|
||||||
|
} {
|
||||||
|
encoded := pixel.NewColor[pixel.Monochrome](expected.R, expected.G, expected.B)
|
||||||
|
image.Set(4, 2, encoded)
|
||||||
|
actual := image.Get(4, 2).RGBA()
|
||||||
|
switch {
|
||||||
|
case expected.R == 0 && expected.G == 0 && expected.B == 0:
|
||||||
|
// should be false eg black
|
||||||
|
if actual.R != 0 || actual.G != 0 || actual.B != 0 {
|
||||||
|
t.Errorf("failed to roundtrip color: expected %v but got %v", expected, actual)
|
||||||
|
}
|
||||||
|
case int(expected.R)+int(expected.G)+int(expected.B) > 128*3:
|
||||||
|
// should be true eg white
|
||||||
|
if actual.R == 0 || actual.G == 0 || actual.B == 0 {
|
||||||
|
t.Errorf("failed to roundtrip color: expected %v but got %v", expected, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test pixel formats by filling them with noise and checking whether they
|
||||||
|
// contain the same data afterwards.
|
||||||
|
func TestImageNoise(t *testing.T) {
|
||||||
|
t.Run("RGB888", func(t *testing.T) {
|
||||||
|
testImageNoise[pixel.RGB888](t)
|
||||||
|
})
|
||||||
|
t.Run("RGB565BE", func(t *testing.T) {
|
||||||
|
testImageNoise[pixel.RGB565BE](t)
|
||||||
|
})
|
||||||
|
t.Run("RGB555", func(t *testing.T) {
|
||||||
|
testImageNoise[pixel.RGB555](t)
|
||||||
|
})
|
||||||
|
t.Run("RGB444BE", func(t *testing.T) {
|
||||||
|
testImageNoise[pixel.RGB444BE](t)
|
||||||
|
})
|
||||||
|
t.Run("Monochrome", func(t *testing.T) {
|
||||||
|
testImageNoise[pixel.Monochrome](t)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func testImageNoise[T pixel.Color](t *testing.T) {
|
||||||
|
// Create an image of a random width/height for extra testing.
|
||||||
|
width := rand.Int()%500 + 10
|
||||||
|
height := rand.Int()%500 + 10
|
||||||
|
t.Log("image size:", width, height)
|
||||||
|
|
||||||
|
// Create two images: the to-be-tested image object and a reference image.
|
||||||
|
img := pixel.NewImage[T](width, height)
|
||||||
|
ref := goimage.NewRGBA(goimage.Rect(0, 0, width, height))
|
||||||
|
|
||||||
|
// Fill the two images with noise.
|
||||||
|
for y := 0; y < height; y++ {
|
||||||
|
for x := 0; x < width; x++ {
|
||||||
|
// Set a random color in both images.
|
||||||
|
c := pixel.NewColor[T](uint8(rand.Uint32()), uint8(rand.Uint32()), uint8(rand.Uint32()))
|
||||||
|
img.Set(x, y, c)
|
||||||
|
ref.Set(x, y, c.RGBA())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compare the two images. They should match.
|
||||||
|
mismatch := 0
|
||||||
|
firstX := 0
|
||||||
|
firstY := 0
|
||||||
|
var firstExpected, firstActual color.RGBA
|
||||||
|
for y := 0; y < height; y++ {
|
||||||
|
for x := 0; x < width; x++ {
|
||||||
|
c := img.Get(x, y).RGBA()
|
||||||
|
r2, g2, b2, _ := ref.At(x, y).RGBA()
|
||||||
|
c2 := color.RGBA{R: uint8(r2 >> 8), G: uint8(g2 >> 8), B: uint8(b2 >> 8), A: 255}
|
||||||
|
if c != c2 {
|
||||||
|
mismatch++
|
||||||
|
if mismatch == 1 {
|
||||||
|
firstX = x
|
||||||
|
firstY = y
|
||||||
|
firstExpected = c
|
||||||
|
firstActual = c2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if mismatch != 0 {
|
||||||
|
t.Errorf("mismatch found: %d pixels are different (first diff at (%d, %d), expected %v, actual %v)", mismatch, firstX, firstY, firstExpected, firstActual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+33
-2
@@ -14,9 +14,9 @@ import (
|
|||||||
|
|
||||||
// Pixel with a particular color, matching the underlying hardware of a
|
// Pixel with a particular color, matching the underlying hardware of a
|
||||||
// particular display. Each pixel is at least 1 byte in size.
|
// particular display. Each pixel is at least 1 byte in size.
|
||||||
// The color format is sRGB (or close to it) in all cases.
|
// The color format is sRGB (or close to it) in all cases except for 1-bit.
|
||||||
type Color interface {
|
type Color interface {
|
||||||
RGB888 | RGB565BE | RGB555 | RGB444BE
|
RGB888 | RGB565BE | RGB555 | RGB444BE | Monochrome
|
||||||
|
|
||||||
BaseColor
|
BaseColor
|
||||||
}
|
}
|
||||||
@@ -50,6 +50,8 @@ func NewColor[T Color](r, g, b uint8) T {
|
|||||||
return any(NewRGB555(r, g, b)).(T)
|
return any(NewRGB555(r, g, b)).(T)
|
||||||
case RGB444BE:
|
case RGB444BE:
|
||||||
return any(NewRGB444BE(r, g, b)).(T)
|
return any(NewRGB444BE(r, g, b)).(T)
|
||||||
|
case Monochrome:
|
||||||
|
return any(NewMonochrome(r, g, b)).(T)
|
||||||
default:
|
default:
|
||||||
panic("unknown color format")
|
panic("unknown color format")
|
||||||
}
|
}
|
||||||
@@ -202,6 +204,35 @@ func (c RGB444BE) RGBA() color.RGBA {
|
|||||||
return color
|
return color
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Monochrome bool
|
||||||
|
|
||||||
|
func NewMonochrome(r, g, b uint8) Monochrome {
|
||||||
|
// Very simple black/white split.
|
||||||
|
// This isn't very accurate (especially for sRGB colors) but is close enough.
|
||||||
|
if int(r)+int(g)+int(b) > 128*3 { // light, convert to white
|
||||||
|
return Monochrome(true)
|
||||||
|
}
|
||||||
|
// dark, convert to black
|
||||||
|
return Monochrome(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Monochrome) BitsPerPixel() int {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Monochrome) RGBA() color.RGBA {
|
||||||
|
value := uint8(0)
|
||||||
|
if c {
|
||||||
|
value = 255
|
||||||
|
}
|
||||||
|
return color.RGBA{
|
||||||
|
R: value,
|
||||||
|
G: value,
|
||||||
|
B: value,
|
||||||
|
A: 255,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Gamma brightness lookup table:
|
// Gamma brightness lookup table:
|
||||||
// https://victornpb.github.io/gamma-table-generator
|
// https://victornpb.github.io/gamma-table-generator
|
||||||
// gamma = 0.45 steps = 256 range = 0-255
|
// gamma = 0.45 steps = 256 range = 0-255
|
||||||
|
|||||||
+98
-17
@@ -17,6 +17,7 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"tinygo.org/x/drivers"
|
||||||
"tinygo.org/x/drivers/netdev"
|
"tinygo.org/x/drivers/netdev"
|
||||||
"tinygo.org/x/drivers/netlink"
|
"tinygo.org/x/drivers/netlink"
|
||||||
)
|
)
|
||||||
@@ -27,13 +28,13 @@ var _debug debug = debugBasic
|
|||||||
//var _debug debug = debugBasic | debugNetdev | debugRpc
|
//var _debug debug = debugBasic | debugNetdev | debugRpc
|
||||||
|
|
||||||
var (
|
var (
|
||||||
version = "0.0.1"
|
|
||||||
driverName = "Realtek rtl8720dn Wifi network device driver (rtl8720dn)"
|
driverName = "Realtek rtl8720dn Wifi network device driver (rtl8720dn)"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
O_NONBLOCK = 1 // note: different value than syscall.O_NONBLOCK (0x800)
|
O_NONBLOCK = 1 // note: different value than syscall.O_NONBLOCK (0x800)
|
||||||
RTW_MODE_STA = 0x00000001
|
RTW_MODE_STA = 0x00000001
|
||||||
|
defaultChannel = 6
|
||||||
)
|
)
|
||||||
|
|
||||||
type sock int32
|
type sock int32
|
||||||
@@ -102,14 +103,22 @@ func (r *rtl8720dn) connectToAP() error {
|
|||||||
return netlink.ErrMissingSSID
|
return netlink.ErrMissingSSID
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(r.params.Passphrase) != 0 && len(r.params.Passphrase) < 8 {
|
||||||
|
return netlink.ErrShortPassphrase
|
||||||
|
}
|
||||||
|
|
||||||
if debugging(debugBasic) {
|
if debugging(debugBasic) {
|
||||||
fmt.Printf("Connecting to Wifi SSID '%s'...", r.params.Ssid)
|
fmt.Printf("Connecting to Wifi SSID '%s'...", r.params.Ssid)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start the connection process
|
// Start the connection process
|
||||||
securityType := uint32(0x00400004)
|
securityType := uint32(0) // RTW_SECURITY_OPEN
|
||||||
|
if len(r.params.Passphrase) != 0 {
|
||||||
|
securityType = 0x00400004 // RTW_SECURITY_WPA2_AES_PSK
|
||||||
|
}
|
||||||
|
|
||||||
result := r.rpc_wifi_connect(r.params.Ssid, r.params.Passphrase, securityType, -1, 0)
|
result := r.rpc_wifi_connect(r.params.Ssid, r.params.Passphrase, securityType, -1, 0)
|
||||||
if result == -1 {
|
if result != 0 {
|
||||||
if debugging(debugBasic) {
|
if debugging(debugBasic) {
|
||||||
fmt.Printf("FAILED\r\n")
|
fmt.Printf("FAILED\r\n")
|
||||||
}
|
}
|
||||||
@@ -127,6 +136,51 @@ func (r *rtl8720dn) connectToAP() error {
|
|||||||
return r.startDhcpc()
|
return r.startDhcpc()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *rtl8720dn) startDhcps() error {
|
||||||
|
if result := r.rpc_tcpip_adapter_dhcps_start(0); result == -1 {
|
||||||
|
return netdev.ErrStartingDHCPServer
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *rtl8720dn) startAP() error {
|
||||||
|
if len(r.params.Ssid) == 0 {
|
||||||
|
return netlink.ErrMissingSSID
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(r.params.Passphrase) != 0 && len(r.params.Passphrase) < 8 {
|
||||||
|
return netlink.ErrShortPassphrase
|
||||||
|
}
|
||||||
|
|
||||||
|
if debugging(debugBasic) {
|
||||||
|
fmt.Printf("Starting Wifi AP as SSID '%s'...", r.params.Ssid)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start the connection process
|
||||||
|
securityType := uint32(0) // RTW_SECURITY_OPEN
|
||||||
|
if len(r.params.Passphrase) != 0 {
|
||||||
|
securityType = 0x00400004 // RTW_SECURITY_WPA2_AES_PSK
|
||||||
|
}
|
||||||
|
|
||||||
|
result := r.rpc_wifi_start_ap(r.params.Ssid, r.params.Passphrase, securityType, defaultChannel)
|
||||||
|
if result != 0 {
|
||||||
|
if debugging(debugBasic) {
|
||||||
|
fmt.Printf("FAILED\r\n")
|
||||||
|
}
|
||||||
|
return netlink.ErrConnectFailed
|
||||||
|
}
|
||||||
|
|
||||||
|
if debugging(debugBasic) {
|
||||||
|
fmt.Printf("LISTENING\r\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.notifyCb != nil {
|
||||||
|
r.notifyCb(netlink.EventNetUp)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r.startDhcps()
|
||||||
|
}
|
||||||
|
|
||||||
func (r *rtl8720dn) showDriver() {
|
func (r *rtl8720dn) showDriver() {
|
||||||
if r.driverShown {
|
if r.driverShown {
|
||||||
return
|
return
|
||||||
@@ -134,7 +188,7 @@ func (r *rtl8720dn) showDriver() {
|
|||||||
if debugging(debugBasic) {
|
if debugging(debugBasic) {
|
||||||
fmt.Printf("\r\n")
|
fmt.Printf("\r\n")
|
||||||
fmt.Printf("%s\r\n\r\n", driverName)
|
fmt.Printf("%s\r\n\r\n", driverName)
|
||||||
fmt.Printf("Driver version : %s\r\n", version)
|
fmt.Printf("Driver version : %s\r\n", drivers.Version)
|
||||||
}
|
}
|
||||||
r.driverShown = true
|
r.driverShown = true
|
||||||
}
|
}
|
||||||
@@ -238,14 +292,27 @@ func (r *rtl8720dn) netConnect(reset bool) error {
|
|||||||
}
|
}
|
||||||
r.showDevice()
|
r.showDevice()
|
||||||
|
|
||||||
|
retry:
|
||||||
for i := 0; r.params.Retries == 0 || i < r.params.Retries; i++ {
|
for i := 0; r.params.Retries == 0 || i < r.params.Retries; i++ {
|
||||||
if err := r.connectToAP(); err != nil {
|
switch r.params.ConnectMode {
|
||||||
if err == netlink.ErrConnectFailed {
|
case netlink.ConnectModeAP:
|
||||||
continue
|
if err := r.startAP(); err != nil {
|
||||||
|
if err == netlink.ErrConnectFailed {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
return err
|
break retry
|
||||||
|
|
||||||
|
default:
|
||||||
|
if err := r.connectToAP(); err != nil {
|
||||||
|
if err == netlink.ErrConnectFailed {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
break retry
|
||||||
}
|
}
|
||||||
break
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if r.networkDown() {
|
if r.networkDown() {
|
||||||
@@ -437,6 +504,12 @@ func ipToName(ip netip.AddrPort) []byte {
|
|||||||
return name
|
return name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func nameToIp(name []byte) netip.AddrPort {
|
||||||
|
port := uint16(name[2])<<8 | uint16(name[3])
|
||||||
|
addr, _ := netip.AddrFromSlice(name[4:8])
|
||||||
|
return netip.AddrPortFrom(addr, port)
|
||||||
|
}
|
||||||
|
|
||||||
func (r *rtl8720dn) Bind(sockfd int, ip netip.AddrPort) error {
|
func (r *rtl8720dn) Bind(sockfd int, ip netip.AddrPort) error {
|
||||||
|
|
||||||
if debugging(debugNetdev) {
|
if debugging(debugNetdev) {
|
||||||
@@ -534,10 +607,10 @@ func (r *rtl8720dn) Listen(sockfd int, backlog int) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *rtl8720dn) Accept(sockfd int, ip netip.AddrPort) (int, error) {
|
func (r *rtl8720dn) Accept(sockfd int) (int, netip.AddrPort, error) {
|
||||||
|
|
||||||
if debugging(debugNetdev) {
|
if debugging(debugNetdev) {
|
||||||
fmt.Printf("[Accept] sockfd: %d, peer: %s\r\n", sockfd, ip)
|
fmt.Printf("[Accept] sockfd: %d\r\n", sockfd)
|
||||||
}
|
}
|
||||||
|
|
||||||
r.mu.Lock()
|
r.mu.Lock()
|
||||||
@@ -546,12 +619,12 @@ func (r *rtl8720dn) Accept(sockfd int, ip netip.AddrPort) (int, error) {
|
|||||||
var newSock int32
|
var newSock int32
|
||||||
var lsock = sock(sockfd)
|
var lsock = sock(sockfd)
|
||||||
var socket = r.sockets[lsock]
|
var socket = r.sockets[lsock]
|
||||||
var name = ipToName(ip)
|
var name = ipToName(netip.AddrPort{})
|
||||||
|
|
||||||
switch socket.protocol {
|
switch socket.protocol {
|
||||||
case netdev.IPPROTO_TCP:
|
case netdev.IPPROTO_TCP:
|
||||||
default:
|
default:
|
||||||
return -1, netdev.ErrProtocolNotSupported
|
return -1, netip.AddrPort{}, netdev.ErrProtocolNotSupported
|
||||||
}
|
}
|
||||||
|
|
||||||
for {
|
for {
|
||||||
@@ -570,6 +643,14 @@ func (r *rtl8720dn) Accept(sockfd int, ip netip.AddrPort) (int, error) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get remote peer ip:port
|
||||||
|
namelen = uint32(len(name))
|
||||||
|
result := r.rpc_lwip_getpeername(int32(newSock), name, &namelen)
|
||||||
|
if result == -1 {
|
||||||
|
return -1, netip.AddrPort{}, fmt.Errorf("Getpeername failed")
|
||||||
|
}
|
||||||
|
raddr := nameToIp(name)
|
||||||
|
|
||||||
// If we've already seen this socket, we can re-use
|
// If we've already seen this socket, we can re-use
|
||||||
// the socket and return it. But, only if the socket
|
// the socket and return it. But, only if the socket
|
||||||
// is closed. If it's not closed, we'll just come back
|
// is closed. If it's not closed, we'll just come back
|
||||||
@@ -582,12 +663,12 @@ func (r *rtl8720dn) Accept(sockfd int, ip netip.AddrPort) (int, error) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// Reuse client socket
|
// Reuse client socket
|
||||||
return int(newSock), nil
|
return int(newSock), raddr, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create new socket for client and return fd
|
// Create new socket for client and return fd
|
||||||
r.sockets[sock(newSock)] = newSocket(socket.protocol)
|
r.sockets[sock(newSock)] = newSocket(socket.protocol)
|
||||||
return int(newSock), nil
|
return int(newSock), raddr, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+23
-1
@@ -1,6 +1,12 @@
|
|||||||
package servo
|
package servo
|
||||||
|
|
||||||
import "machine"
|
import (
|
||||||
|
"machine"
|
||||||
|
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
var ErrInvalidAngle = errors.New("servo: invalid angle")
|
||||||
|
|
||||||
// PWM is the interface necessary for controlling typical servo motors.
|
// PWM is the interface necessary for controlling typical servo motors.
|
||||||
type PWM interface {
|
type PWM interface {
|
||||||
@@ -80,3 +86,19 @@ func (s Servo) SetMicroseconds(microseconds int16) {
|
|||||||
value := uint64(s.pwm.Top()) * uint64(microseconds) / (pwmPeriod / 1000)
|
value := uint64(s.pwm.Top()) * uint64(microseconds) / (pwmPeriod / 1000)
|
||||||
s.pwm.Set(s.channel, uint32(value))
|
s.pwm.Set(s.channel, uint32(value))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetAngle sets the angle of the servo in degrees. The angle should be between
|
||||||
|
// 0 and 180, where 0 is the minimum angle and 180 is the maximum angle.
|
||||||
|
// This function should work for most servos, but if it doesn't work for yours
|
||||||
|
// you can use SetMicroseconds directly instead.
|
||||||
|
func (s Servo) SetAngle(angle int) error {
|
||||||
|
if angle < 0 || angle > 180 {
|
||||||
|
return ErrInvalidAngle
|
||||||
|
}
|
||||||
|
|
||||||
|
// 0° is 1000µs, 180° is 2000µs. See explanation in SetMicroseconds.
|
||||||
|
microseconds := angle*1000/180 + 1000
|
||||||
|
s.SetMicroseconds(int16(microseconds))
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -80,6 +80,7 @@ tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/vl6
|
|||||||
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/waveshare-epd/epd2in13/main.go
|
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/waveshare-epd/epd2in13/main.go
|
||||||
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/waveshare-epd/epd2in13x/main.go
|
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/waveshare-epd/epd2in13x/main.go
|
||||||
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/waveshare-epd/epd4in2/main.go
|
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/waveshare-epd/epd4in2/main.go
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=pico ./examples/waveshare-epd/epd2in66b/main.go
|
||||||
tinygo build -size short -o ./build/test.hex -target=circuitplay-express ./examples/ws2812
|
tinygo build -size short -o ./build/test.hex -target=circuitplay-express ./examples/ws2812
|
||||||
tinygo build -size short -o ./build/test.bin -target=m5stamp-c3 ./examples/ws2812
|
tinygo build -size short -o ./build/test.bin -target=m5stamp-c3 ./examples/ws2812
|
||||||
tinygo build -size short -o ./build/test.hex -target=feather-nrf52840 ./examples/is31fl3731/main.go
|
tinygo build -size short -o ./build/test.hex -target=feather-nrf52840 ./examples/is31fl3731/main.go
|
||||||
@@ -98,6 +99,7 @@ tinygo build -size short -o ./build/test.hex -target=hifive1b ./examples/ssd1351
|
|||||||
tinygo build -size short -o ./build/test.hex -target=circuitplay-express ./examples/lis2mdl/main.go
|
tinygo build -size short -o ./build/test.hex -target=circuitplay-express ./examples/lis2mdl/main.go
|
||||||
tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/max72xx/main.go
|
tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/max72xx/main.go
|
||||||
tinygo build -size short -o ./build/test.hex -target=feather-m0 ./examples/dht/main.go
|
tinygo build -size short -o ./build/test.hex -target=feather-m0 ./examples/dht/main.go
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=feather-m0 ./examples/dht20/main.go
|
||||||
# tinygo build -size short -o ./build/test.hex -target=arduino ./examples/keypad4x4/main.go
|
# tinygo build -size short -o ./build/test.hex -target=arduino ./examples/keypad4x4/main.go
|
||||||
tinygo build -size short -o ./build/test.hex -target=feather-rp2040 ./examples/pcf8523/
|
tinygo build -size short -o ./build/test.hex -target=feather-rp2040 ./examples/pcf8523/
|
||||||
tinygo build -size short -o ./build/test.hex -target=xiao ./examples/pcf8563/alarm/
|
tinygo build -size short -o ./build/test.hex -target=xiao ./examples/pcf8563/alarm/
|
||||||
@@ -105,6 +107,7 @@ tinygo build -size short -o ./build/test.hex -target=xiao ./examples/pcf8563/clk
|
|||||||
tinygo build -size short -o ./build/test.hex -target=xiao ./examples/pcf8563/time/
|
tinygo build -size short -o ./build/test.hex -target=xiao ./examples/pcf8563/time/
|
||||||
tinygo build -size short -o ./build/test.hex -target=xiao ./examples/pcf8563/timer/
|
tinygo build -size short -o ./build/test.hex -target=xiao ./examples/pcf8563/timer/
|
||||||
tinygo build -size short -o ./build/test.hex -target=pico ./examples/qmi8658c/main.go
|
tinygo build -size short -o ./build/test.hex -target=pico ./examples/qmi8658c/main.go
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=feather-rp2040 ./examples/pcf8591/
|
||||||
tinygo build -size short -o ./build/test.hex -target=feather-m0 ./examples/ina260/main.go
|
tinygo build -size short -o ./build/test.hex -target=feather-m0 ./examples/ina260/main.go
|
||||||
tinygo build -size short -o ./build/test.hex -target=nucleo-l432kc ./examples/aht20/main.go
|
tinygo build -size short -o ./build/test.hex -target=nucleo-l432kc ./examples/aht20/main.go
|
||||||
tinygo build -size short -o ./build/test.hex -target=feather-m4 ./examples/sdcard/console/
|
tinygo build -size short -o ./build/test.hex -target=feather-m4 ./examples/sdcard/console/
|
||||||
@@ -129,6 +132,8 @@ tinygo build -size short -o ./build/test.hex -target=microbit ./examples/ndir/ma
|
|||||||
tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/ndir/main_ndir.go
|
tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/ndir/main_ndir.go
|
||||||
tinygo build -size short -o ./build/test.uf2 -target=pico ./examples/mpu9150/main.go
|
tinygo build -size short -o ./build/test.uf2 -target=pico ./examples/mpu9150/main.go
|
||||||
tinygo build -size short -o ./build/test.hex -target=macropad-rp2040 ./examples/sh1106/macropad_spi
|
tinygo build -size short -o ./build/test.hex -target=macropad-rp2040 ./examples/sh1106/macropad_spi
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=macropad-rp2040 ./examples/encoders/quadrature-interrupt
|
||||||
|
tinygo build -size short -o ./build/test.uf2 -target=pico ./examples/mcp9808/main.go
|
||||||
# network examples (espat)
|
# network examples (espat)
|
||||||
tinygo build -size short -o ./build/test.hex -target=challenger-rp2040 ./examples/net/ntpclient/
|
tinygo build -size short -o ./build/test.hex -target=challenger-rp2040 ./examples/net/ntpclient/
|
||||||
# network examples (wifinina)
|
# network examples (wifinina)
|
||||||
|
|||||||
+73
-6
@@ -11,8 +11,17 @@ import (
|
|||||||
|
|
||||||
"tinygo.org/x/drivers"
|
"tinygo.org/x/drivers"
|
||||||
"tinygo.org/x/drivers/internal/legacy"
|
"tinygo.org/x/drivers/internal/legacy"
|
||||||
|
"tinygo.org/x/drivers/pixel"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
errBufferSize = errors.New("invalid size buffer")
|
||||||
|
errOutOfRange = errors.New("out of screen range")
|
||||||
|
errNotImplemented = errors.New("not implemented")
|
||||||
|
)
|
||||||
|
|
||||||
|
type ResetValue [2]byte
|
||||||
|
|
||||||
// Device wraps I2C or SPI connection.
|
// Device wraps I2C or SPI connection.
|
||||||
type Device struct {
|
type Device struct {
|
||||||
bus Buser
|
bus Buser
|
||||||
@@ -22,6 +31,8 @@ type Device struct {
|
|||||||
bufferSize int16
|
bufferSize int16
|
||||||
vccState VccMode
|
vccState VccMode
|
||||||
canReset bool
|
canReset bool
|
||||||
|
resetCol ResetValue
|
||||||
|
resetPage ResetValue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Config is the configuration for the display
|
// Config is the configuration for the display
|
||||||
@@ -30,6 +41,13 @@ type Config struct {
|
|||||||
Height int16
|
Height int16
|
||||||
VccState VccMode
|
VccState VccMode
|
||||||
Address uint16
|
Address uint16
|
||||||
|
// ResetCol and ResetPage are used to reset the screen to 0x0
|
||||||
|
// This is useful for some screens that have a different size than 128x64
|
||||||
|
// For example, the Thumby's screen is 72x40
|
||||||
|
// The default values are normally set automatically based on the size.
|
||||||
|
// If you're using a different size, you might need to set these values manually.
|
||||||
|
ResetCol ResetValue
|
||||||
|
ResetPage ResetValue
|
||||||
}
|
}
|
||||||
|
|
||||||
type I2CBus struct {
|
type I2CBus struct {
|
||||||
@@ -79,6 +97,7 @@ func NewSPI(bus drivers.SPI, dcPin, resetPin, csPin machine.Pin) Device {
|
|||||||
|
|
||||||
// Configure initializes the display with default configuration
|
// Configure initializes the display with default configuration
|
||||||
func (d *Device) Configure(cfg Config) {
|
func (d *Device) Configure(cfg Config) {
|
||||||
|
var zeroReset ResetValue
|
||||||
if cfg.Width != 0 {
|
if cfg.Width != 0 {
|
||||||
d.width = cfg.Width
|
d.width = cfg.Width
|
||||||
} else {
|
} else {
|
||||||
@@ -97,6 +116,16 @@ func (d *Device) Configure(cfg Config) {
|
|||||||
} else {
|
} else {
|
||||||
d.vccState = SWITCHCAPVCC
|
d.vccState = SWITCHCAPVCC
|
||||||
}
|
}
|
||||||
|
if cfg.ResetCol != zeroReset {
|
||||||
|
d.resetCol = cfg.ResetCol
|
||||||
|
} else {
|
||||||
|
d.resetCol = ResetValue{0, uint8(d.width - 1)}
|
||||||
|
}
|
||||||
|
if cfg.ResetPage != zeroReset {
|
||||||
|
d.resetPage = cfg.ResetPage
|
||||||
|
} else {
|
||||||
|
d.resetPage = ResetValue{0, uint8(d.height/8) - 1}
|
||||||
|
}
|
||||||
d.bufferSize = d.width * d.height / 8
|
d.bufferSize = d.width * d.height / 8
|
||||||
d.buffer = make([]byte, d.bufferSize)
|
d.buffer = make([]byte, d.bufferSize)
|
||||||
d.canReset = cfg.Address != 0 || d.width != 128 || d.height != 64 // I2C or not 128x64
|
d.canReset = cfg.Address != 0 || d.width != 128 || d.height != 64 // I2C or not 128x64
|
||||||
@@ -186,11 +215,11 @@ func (d *Device) Display() error {
|
|||||||
// Since we're printing the whole buffer, avoid resetting it in this case
|
// Since we're printing the whole buffer, avoid resetting it in this case
|
||||||
if d.canReset {
|
if d.canReset {
|
||||||
d.Command(COLUMNADDR)
|
d.Command(COLUMNADDR)
|
||||||
d.Command(0)
|
d.Command(d.resetCol[0])
|
||||||
d.Command(uint8(d.width - 1))
|
d.Command(d.resetCol[1])
|
||||||
d.Command(PAGEADDR)
|
d.Command(PAGEADDR)
|
||||||
d.Command(0)
|
d.Command(d.resetPage[0])
|
||||||
d.Command(uint8(d.height/8) - 1)
|
d.Command(d.resetPage[1])
|
||||||
}
|
}
|
||||||
|
|
||||||
return d.Tx(d.buffer, false)
|
return d.Tx(d.buffer, false)
|
||||||
@@ -223,8 +252,7 @@ func (d *Device) GetPixel(x int16, y int16) bool {
|
|||||||
// SetBuffer changes the whole buffer at once
|
// SetBuffer changes the whole buffer at once
|
||||||
func (d *Device) SetBuffer(buffer []byte) error {
|
func (d *Device) SetBuffer(buffer []byte) error {
|
||||||
if int16(len(buffer)) != d.bufferSize {
|
if int16(len(buffer)) != d.bufferSize {
|
||||||
//return ErrBuffer
|
return errBufferSize
|
||||||
return errors.New("wrong size buffer")
|
|
||||||
}
|
}
|
||||||
for i := int16(0); i < d.bufferSize; i++ {
|
for i := int16(0); i < d.bufferSize; i++ {
|
||||||
d.buffer[i] = buffer[i]
|
d.buffer[i] = buffer[i]
|
||||||
@@ -316,3 +344,42 @@ func (b *SPIBus) tx(data []byte, isCommand bool) error {
|
|||||||
func (d *Device) Size() (w, h int16) {
|
func (d *Device) Size() (w, h int16) {
|
||||||
return d.width, d.height
|
return d.width, d.height
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DrawBitmap copies the bitmap to the screen at the given coordinates.
|
||||||
|
func (d *Device) DrawBitmap(x, y int16, bitmap pixel.Image[pixel.Monochrome]) error {
|
||||||
|
width, height := bitmap.Size()
|
||||||
|
if x < 0 || x+int16(width) > d.width || y < 0 || y+int16(height) > d.height {
|
||||||
|
return errOutOfRange
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < width; i++ {
|
||||||
|
for j := 0; j < height; j++ {
|
||||||
|
d.SetPixel(x+int16(i), y+int16(j), bitmap.Get(i, j).RGBA())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rotation returns the currently configured rotation.
|
||||||
|
func (d *Device) Rotation() drivers.Rotation {
|
||||||
|
return drivers.Rotation0
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetRotation changes the rotation of the device (clock-wise).
|
||||||
|
// Would have to be implemented in software for this device.
|
||||||
|
func (d *Device) SetRotation(rotation drivers.Rotation) error {
|
||||||
|
return errNotImplemented
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the sleep mode for this display. When sleeping, the panel uses a lot
|
||||||
|
// less power. The display won't show an image anymore, but the memory contents
|
||||||
|
// should be kept.
|
||||||
|
func (d *Device) Sleep(sleepEnabled bool) error {
|
||||||
|
if sleepEnabled {
|
||||||
|
d.Command(DISPLAYOFF)
|
||||||
|
} else {
|
||||||
|
d.Command(DISPLAYON)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
package uc8151
|
||||||
|
|
||||||
|
// LUTType is the look-up table for the display
|
||||||
|
type LUTType [42]uint8
|
||||||
|
|
||||||
|
type LUTSet struct {
|
||||||
|
VCOM LUTType
|
||||||
|
WW LUTType
|
||||||
|
BW LUTType
|
||||||
|
WB LUTType
|
||||||
|
BB LUTType
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lut *LUTType) Clear() {
|
||||||
|
for i := range lut {
|
||||||
|
lut[i] = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lut *LUTType) SetRow(row int, pat uint8, dur [4]uint8, rep uint8) error {
|
||||||
|
index := row * 6
|
||||||
|
lut[index] = pat
|
||||||
|
lut[index+1] = dur[0]
|
||||||
|
lut[index+2] = dur[1]
|
||||||
|
lut[index+3] = dur[2]
|
||||||
|
lut[index+4] = dur[3]
|
||||||
|
lut[index+5] = rep
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
+12
-7
@@ -1,5 +1,7 @@
|
|||||||
package uc8151
|
package uc8151
|
||||||
|
|
||||||
|
import "tinygo.org/x/drivers"
|
||||||
|
|
||||||
// Registers
|
// Registers
|
||||||
const (
|
const (
|
||||||
// Display resolution
|
// Display resolution
|
||||||
@@ -141,13 +143,16 @@ const (
|
|||||||
HZ_100 = 0b00111010
|
HZ_100 = 0b00111010
|
||||||
HZ_200 = 0b00111001
|
HZ_200 = 0b00111001
|
||||||
|
|
||||||
NO_ROTATION Rotation = 0
|
// deprecated constants, just here for backward compatibility.
|
||||||
ROTATION_90 Rotation = 1 // 90 degrees clock-wise rotation
|
NO_ROTATION = drivers.Rotation0
|
||||||
ROTATION_180 Rotation = 2
|
ROTATION_90 = drivers.Rotation90
|
||||||
ROTATION_270 Rotation = 3
|
ROTATION_180 = drivers.Rotation180
|
||||||
|
ROTATION_270 = drivers.Rotation270
|
||||||
|
|
||||||
DEFAULT Speed = 0
|
DEFAULT Speed = 0
|
||||||
MEDIUM Speed = 1
|
SLOW Speed = 1
|
||||||
FAST Speed = 2
|
MEDIUM Speed = 2
|
||||||
TURBO Speed = 3
|
FAST Speed = 3
|
||||||
|
FASTER Speed = 4
|
||||||
|
TURBO Speed = 5
|
||||||
)
|
)
|
||||||
|
|||||||
+196
-349
@@ -1,6 +1,7 @@
|
|||||||
// Package uc8151 implements a driver for e-ink displays controlled by UC8151
|
// Package uc8151 implements a driver for e-ink displays controlled by UC8151
|
||||||
//
|
//
|
||||||
// Inspired by https://github.com/pimoroni/pimoroni-pico/blob/main/drivers/uc8151/uc8151.cpp
|
// Inspired by https://github.com/pimoroni/pimoroni-pico/blob/main/drivers/uc8151/uc8151.cpp
|
||||||
|
// Additional inspiration from https://github.com/antirez/uc8151_micropython
|
||||||
// Datasheet: https://www.buydisplay.com/download/ic/UC8151C.pdf
|
// Datasheet: https://www.buydisplay.com/download/ic/UC8151C.pdf
|
||||||
package uc8151 // import "tinygo.org/x/drivers/uc8151"
|
package uc8151 // import "tinygo.org/x/drivers/uc8151"
|
||||||
|
|
||||||
@@ -11,35 +12,43 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"tinygo.org/x/drivers"
|
"tinygo.org/x/drivers"
|
||||||
|
"tinygo.org/x/drivers/pixel"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
errOutOfRange = errors.New("out of screen range")
|
||||||
)
|
)
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Width int16
|
Width int16
|
||||||
Height int16
|
Height int16
|
||||||
Rotation Rotation // Rotation is clock-wise
|
Rotation drivers.Rotation // Rotation is clock-wise
|
||||||
Speed Speed // Value from DEFAULT, MEDIUM, FAST, TURBO
|
Speed Speed // Value from DEFAULT, SLOW, MEDIUM, FAST, FASTER, TURBO
|
||||||
Blocking bool
|
Blocking bool // block on calls to display or return immediately
|
||||||
|
FlickerFree bool // if we should avoid flickering
|
||||||
|
UpdateAfter int // if we are using flicker-free mode, how often we should update the screen
|
||||||
}
|
}
|
||||||
|
|
||||||
type Device struct {
|
type Device struct {
|
||||||
bus drivers.SPI
|
bus drivers.SPI
|
||||||
cs machine.Pin
|
cs machine.Pin
|
||||||
dc machine.Pin
|
dc machine.Pin
|
||||||
rst machine.Pin
|
rst machine.Pin
|
||||||
busy machine.Pin
|
busy machine.Pin
|
||||||
width int16
|
width int16
|
||||||
height int16
|
height int16
|
||||||
buffer []uint8
|
buffer []uint8
|
||||||
bufferLength uint32
|
bufferLength uint32
|
||||||
rotation Rotation
|
rotation drivers.Rotation
|
||||||
speed Speed
|
speed Speed
|
||||||
blocking bool
|
blocking bool
|
||||||
|
flickerFree bool
|
||||||
|
updateCount, updateAfter int
|
||||||
}
|
}
|
||||||
|
|
||||||
type Rotation uint8
|
|
||||||
type Speed uint8
|
type Speed uint8
|
||||||
|
|
||||||
// New returns a new epd2in13x driver. Pass in a fully configured SPI bus.
|
// New returns a new uc8151 driver. Pass in a fully configured SPI bus.
|
||||||
func New(bus drivers.SPI, csPin, dcPin, rstPin, busyPin machine.Pin) Device {
|
func New(bus drivers.SPI, csPin, dcPin, rstPin, busyPin machine.Pin) Device {
|
||||||
csPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
csPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||||
dcPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
dcPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||||
@@ -69,6 +78,8 @@ func (d *Device) Configure(cfg Config) {
|
|||||||
d.rotation = cfg.Rotation
|
d.rotation = cfg.Rotation
|
||||||
d.speed = cfg.Speed
|
d.speed = cfg.Speed
|
||||||
d.blocking = cfg.Blocking
|
d.blocking = cfg.Blocking
|
||||||
|
d.flickerFree = cfg.FlickerFree
|
||||||
|
d.updateAfter = cfg.UpdateAfter
|
||||||
d.bufferLength = (uint32(d.width) * uint32(d.height)) / 8
|
d.bufferLength = (uint32(d.width) * uint32(d.height)) / 8
|
||||||
d.buffer = make([]uint8, d.bufferLength)
|
d.buffer = make([]uint8, d.bufferLength)
|
||||||
for i := uint32(0); i < d.bufferLength; i++ {
|
for i := uint32(0); i < d.bufferLength; i++ {
|
||||||
@@ -84,14 +95,14 @@ func (d *Device) Configure(cfg Config) {
|
|||||||
d.SendData(RES_128x296 | LUT_REG | FORMAT_BW | SHIFT_RIGHT | BOOSTER_ON | RESET_NONE | SCAN_UP)
|
d.SendData(RES_128x296 | LUT_REG | FORMAT_BW | SHIFT_RIGHT | BOOSTER_ON | RESET_NONE | SCAN_UP)
|
||||||
}
|
}
|
||||||
|
|
||||||
d.SetLUT(d.speed)
|
d.SetLUT(d.speed, d.flickerFree)
|
||||||
|
|
||||||
d.SendCommand(PWR)
|
d.SendCommand(PWR)
|
||||||
d.SendData(VDS_INTERNAL | VDG_INTERNAL)
|
d.SendData(VDS_INTERNAL | VDG_INTERNAL)
|
||||||
d.SendData(VCOM_VG | VGHL_16V)
|
d.SendData(VCOM_VD | VGHL_16V)
|
||||||
d.SendData(0x2B)
|
d.SendData(0b100110) // +10v VDH
|
||||||
d.SendData(0x2B)
|
d.SendData(0b100110) // -10v VDL
|
||||||
d.SendData(0x2B)
|
d.SendData(0b000011) // VDHR default (For red pixels, not used here)
|
||||||
|
|
||||||
d.SendCommand(PON)
|
d.SendCommand(PON)
|
||||||
d.WaitUntilIdle()
|
d.WaitUntilIdle()
|
||||||
@@ -102,7 +113,7 @@ func (d *Device) Configure(cfg Config) {
|
|||||||
d.SendData(START_10MS | STRENGTH_3 | OFF_6_58US)
|
d.SendData(START_10MS | STRENGTH_3 | OFF_6_58US)
|
||||||
|
|
||||||
d.SendCommand(PFS)
|
d.SendCommand(PFS)
|
||||||
d.SendData(FRAMES_1)
|
d.SendData(FRAMES_4)
|
||||||
|
|
||||||
d.SendCommand(TSE)
|
d.SendCommand(TSE)
|
||||||
d.SendData(TEMP_INTERNAL | OFFSET_0)
|
d.SendData(TEMP_INTERNAL | OFFSET_0)
|
||||||
@@ -111,14 +122,13 @@ func (d *Device) Configure(cfg Config) {
|
|||||||
d.SendData(0x22)
|
d.SendData(0x22)
|
||||||
|
|
||||||
d.SendCommand(CDI)
|
d.SendCommand(CDI)
|
||||||
d.SendData(0x4C) // 4C //5C
|
d.SendData(0b11_00_1100)
|
||||||
|
|
||||||
d.SendCommand(PLL)
|
d.SendCommand(PLL)
|
||||||
d.SendData(HZ_100)
|
d.SendData(HZ_100)
|
||||||
|
|
||||||
d.SendCommand(POF)
|
d.SendCommand(POF)
|
||||||
d.WaitUntilIdle()
|
d.WaitUntilIdle()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset resets the device
|
// Reset resets the device
|
||||||
@@ -135,31 +145,30 @@ func (d *Device) PowerOff() {
|
|||||||
d.SendCommand(POF)
|
d.SendCommand(POF)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PowerOn power on the device
|
||||||
|
func (d *Device) PowerOn() {
|
||||||
|
d.SendCommand(PON)
|
||||||
|
}
|
||||||
|
|
||||||
// SendCommand sends a command to the display
|
// SendCommand sends a command to the display
|
||||||
func (d *Device) SendCommand(command uint8) {
|
func (d *Device) SendCommand(command uint8) {
|
||||||
d.sendDataCommand(true, command)
|
d.dc.Low()
|
||||||
|
d.cs.Low()
|
||||||
|
d.bus.Transfer(command)
|
||||||
|
d.cs.High()
|
||||||
}
|
}
|
||||||
|
|
||||||
// SendData sends a data byte to the display
|
// SendData sends a data byte to the display
|
||||||
func (d *Device) SendData(data uint8) {
|
func (d *Device) SendData(data ...uint8) {
|
||||||
d.sendDataCommand(false, data)
|
d.dc.High()
|
||||||
}
|
|
||||||
|
|
||||||
// sendDataCommand sends image data or a command to the screen
|
|
||||||
func (d *Device) sendDataCommand(isCommand bool, data uint8) {
|
|
||||||
if isCommand {
|
|
||||||
d.dc.Low()
|
|
||||||
} else {
|
|
||||||
d.dc.High()
|
|
||||||
}
|
|
||||||
d.cs.Low()
|
d.cs.Low()
|
||||||
d.bus.Transfer(data)
|
d.bus.Tx(data, nil)
|
||||||
d.cs.High()
|
d.cs.High()
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetPixel modifies the internal buffer in a single pixel.
|
// SetPixel modifies the internal buffer in a single pixel.
|
||||||
// The display have 2 colors: black and white
|
// The display have 2 colors: black and white
|
||||||
// We use RGBA(0,0,0, 255) as white (transparent)
|
// We use RGBA(0, 0, 0) as white (transparent)
|
||||||
// Anything else as black
|
// Anything else as black
|
||||||
func (d *Device) SetPixel(x int16, y int16, c color.RGBA) {
|
func (d *Device) SetPixel(x int16, y int16, c color.RGBA) {
|
||||||
x, y = d.xy(x, y)
|
x, y = d.xy(x, y)
|
||||||
@@ -168,31 +177,60 @@ func (d *Device) SetPixel(x int16, y int16, c color.RGBA) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
byteIndex := x/8 + y*(d.width/8)
|
byteIndex := x/8 + y*(d.width/8)
|
||||||
if c.R == 0 && c.G == 0 && c.B == 0 { // TRANSPARENT / WHITE
|
if c.R != 0 || c.G != 0 || c.B != 0 {
|
||||||
d.buffer[byteIndex] &^= 0x80 >> uint8(x%8)
|
|
||||||
} else { // WHITE / EMPTY
|
|
||||||
d.buffer[byteIndex] |= 0x80 >> uint8(x%8)
|
d.buffer[byteIndex] |= 0x80 >> uint8(x%8)
|
||||||
|
} else {
|
||||||
|
d.buffer[byteIndex] &^= 0x80 >> uint8(x%8)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DrawBitmap copies the bitmap to the screen at the given coordinates.
|
||||||
|
func (d *Device) DrawBitmap(x, y int16, bitmap pixel.Image[pixel.Monochrome]) error {
|
||||||
|
dw, dh := d.Size()
|
||||||
|
bw, bh := bitmap.Size()
|
||||||
|
if x < 0 || x+int16(bw) > dw || y < 0 || y+int16(bh) > dh {
|
||||||
|
return errOutOfRange
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < bw; i++ {
|
||||||
|
for j := 0; j < bh; j++ {
|
||||||
|
d.SetPixel(x+int16(i), y+int16(j), bitmap.Get(i, j).RGBA())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Display sends the buffer to the screen.
|
// Display sends the buffer to the screen.
|
||||||
func (d *Device) Display() error {
|
func (d *Device) Display() error {
|
||||||
if d.blocking {
|
if d.blocking {
|
||||||
d.WaitUntilIdle()
|
d.WaitUntilIdle()
|
||||||
}
|
}
|
||||||
d.SendCommand(PON)
|
|
||||||
|
if d.flickerFree && d.updateAfter != 0 && d.updateCount%d.updateAfter == 0 {
|
||||||
|
// we need full refresh here
|
||||||
|
d.SetLUT(MEDIUM, false)
|
||||||
|
} else {
|
||||||
|
d.SetLUT(d.speed, d.flickerFree)
|
||||||
|
}
|
||||||
|
d.updateCount++
|
||||||
|
|
||||||
|
d.PowerOn()
|
||||||
|
|
||||||
d.SendCommand(PTOU)
|
d.SendCommand(PTOU)
|
||||||
d.SendCommand(DTM2)
|
d.SendCommand(DTM2)
|
||||||
for i := uint32(0); i < d.bufferLength; i++ {
|
d.SendData(d.buffer...)
|
||||||
d.SendData(d.buffer[i])
|
|
||||||
}
|
|
||||||
|
|
||||||
d.SendCommand(DSP)
|
d.SendCommand(DSP)
|
||||||
d.SendCommand(DRF)
|
d.SendCommand(DRF)
|
||||||
|
|
||||||
|
d.SetLUT(d.speed, d.flickerFree)
|
||||||
|
|
||||||
if d.blocking {
|
if d.blocking {
|
||||||
d.WaitUntilIdle()
|
d.WaitUntilIdle()
|
||||||
d.PowerOff()
|
d.PowerOff()
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -208,13 +246,14 @@ func (d *Device) DisplayRect(x int16, y int16, width int16, height int16) error
|
|||||||
if x < 0 || y < 0 || x >= d.width || y >= d.height || width < 0 || height < 0 {
|
if x < 0 || y < 0 || x >= d.width || y >= d.height || width < 0 || height < 0 {
|
||||||
return errors.New("wrong rectangle")
|
return errors.New("wrong rectangle")
|
||||||
}
|
}
|
||||||
if d.rotation == ROTATION_90 {
|
switch d.rotation {
|
||||||
|
case drivers.Rotation0:
|
||||||
width, height = height, width
|
width, height = height, width
|
||||||
x -= width
|
x -= width
|
||||||
} else if d.rotation == ROTATION_180 {
|
case drivers.Rotation90:
|
||||||
x -= width - 1
|
x -= width - 1
|
||||||
y -= height - 1
|
y -= height - 1
|
||||||
} else if d.rotation == ROTATION_270 {
|
case drivers.Rotation180:
|
||||||
width, height = height, width
|
width, height = height, width
|
||||||
y -= height
|
y -= height
|
||||||
}
|
}
|
||||||
@@ -262,6 +301,12 @@ func (d *Device) DisplayRect(x int16, y int16, width int16, height int16) error
|
|||||||
|
|
||||||
// ClearDisplay erases the device SRAM
|
// ClearDisplay erases the device SRAM
|
||||||
func (d *Device) ClearDisplay() {
|
func (d *Device) ClearDisplay() {
|
||||||
|
ff := d.flickerFree
|
||||||
|
d.flickerFree = false
|
||||||
|
defer func() {
|
||||||
|
d.flickerFree = ff
|
||||||
|
}()
|
||||||
|
|
||||||
d.ClearBuffer()
|
d.ClearBuffer()
|
||||||
d.Display()
|
d.Display()
|
||||||
}
|
}
|
||||||
@@ -269,7 +314,7 @@ func (d *Device) ClearDisplay() {
|
|||||||
// WaitUntilIdle waits until the display is ready
|
// WaitUntilIdle waits until the display is ready
|
||||||
func (d *Device) WaitUntilIdle() {
|
func (d *Device) WaitUntilIdle() {
|
||||||
for !d.busy.Get() {
|
for !d.busy.Get() {
|
||||||
time.Sleep(100 * time.Millisecond)
|
time.Sleep(10 * time.Millisecond)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -287,15 +332,32 @@ func (d *Device) ClearBuffer() {
|
|||||||
|
|
||||||
// Size returns the current size of the display.
|
// Size returns the current size of the display.
|
||||||
func (d *Device) Size() (w, h int16) {
|
func (d *Device) Size() (w, h int16) {
|
||||||
if d.rotation == ROTATION_90 || d.rotation == ROTATION_270 {
|
if d.rotation == drivers.Rotation90 || d.rotation == drivers.Rotation270 {
|
||||||
return d.height, d.width
|
return d.height, d.width
|
||||||
}
|
}
|
||||||
return d.width, d.height
|
return d.width, d.height
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Rotation returns the currently configured rotation.
|
||||||
|
func (d *Device) Rotation() drivers.Rotation {
|
||||||
|
return d.rotation
|
||||||
|
}
|
||||||
|
|
||||||
// SetRotation changes the rotation (clock-wise) of the device
|
// SetRotation changes the rotation (clock-wise) of the device
|
||||||
func (d *Device) SetRotation(rotation Rotation) {
|
func (d *Device) SetRotation(rotation drivers.Rotation) error {
|
||||||
d.rotation = rotation
|
d.rotation = rotation
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the sleep mode for this display.
|
||||||
|
func (d *Device) Sleep(sleepEnabled bool) error {
|
||||||
|
if sleepEnabled {
|
||||||
|
d.PowerOff()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
d.PowerOn()
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetBlocking changes the blocking flag of the device
|
// SetBlocking changes the blocking flag of the device
|
||||||
@@ -303,16 +365,16 @@ func (d *Device) SetBlocking(blocking bool) {
|
|||||||
d.blocking = blocking
|
d.blocking = blocking
|
||||||
}
|
}
|
||||||
|
|
||||||
// xy chages the coordinates according to the rotation
|
// xy changes the coordinates according to the rotation
|
||||||
func (d *Device) xy(x, y int16) (int16, int16) {
|
func (d *Device) xy(x, y int16) (int16, int16) {
|
||||||
switch d.rotation {
|
switch d.rotation {
|
||||||
case NO_ROTATION:
|
case drivers.Rotation0:
|
||||||
return x, y
|
return x, y
|
||||||
case ROTATION_90:
|
case drivers.Rotation90:
|
||||||
return d.width - y - 1, x
|
return d.width - y - 1, x
|
||||||
case ROTATION_180:
|
case drivers.Rotation180:
|
||||||
return d.width - x - 1, d.height - y - 1
|
return d.width - x - 1, d.height - y - 1
|
||||||
case ROTATION_270:
|
case drivers.Rotation270:
|
||||||
return y, d.height - x - 1
|
return y, d.height - x - 1
|
||||||
}
|
}
|
||||||
return x, y
|
return x, y
|
||||||
@@ -338,296 +400,81 @@ func (d *Device) Invert(invert bool) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetLUT sets the look up tables for full or partial updates
|
// SetLUT sets the look up tables for full or partial updates based on
|
||||||
func (d *Device) SetLUT(speed Speed) {
|
// the speed and flicker-free mode.
|
||||||
switch speed {
|
// Based on code from https://github.com/antirez/uc8151_micropython
|
||||||
case MEDIUM:
|
func (d *Device) SetLUT(speed Speed, flickerFree bool) error {
|
||||||
var lut = [44]uint8{
|
var lut LUTSet
|
||||||
0x00, 0x16, 0x16, 0x0d, 0x00, 0x01,
|
|
||||||
0x00, 0x23, 0x23, 0x00, 0x00, 0x02,
|
|
||||||
0x00, 0x16, 0x16, 0x0d, 0x00, 0x01,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00,
|
|
||||||
}
|
|
||||||
d.SendCommand(LUT_VCOM)
|
|
||||||
for i := 0; i < 44; i++ {
|
|
||||||
d.SendData(lut[i])
|
|
||||||
}
|
|
||||||
lut = [44]uint8{
|
|
||||||
0x54, 0x16, 0x16, 0x0d, 0x00, 0x01,
|
|
||||||
0x60, 0x23, 0x23, 0x00, 0x00, 0x02,
|
|
||||||
0xa8, 0x16, 0x16, 0x0d, 0x00, 0x01,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
|
|
||||||
d.SendCommand(LUT_WW)
|
|
||||||
// do not send last two bytes
|
|
||||||
for i := 0; i < 42; i++ {
|
|
||||||
d.SendData(lut[i])
|
|
||||||
}
|
|
||||||
|
|
||||||
lut = [44]uint8{
|
// Num. of frames for single direction change.
|
||||||
0x54, 0x16, 0x16, 0x0d, 0x00, 0x01,
|
period := 64
|
||||||
0x60, 0x23, 0x23, 0x00, 0x00, 0x02,
|
p := uint8(period / (2 ^ (int(speed) - 1)))
|
||||||
0xa8, 0x16, 0x16, 0x0d, 0x00, 0x01,
|
if p < 1 {
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
p = 1
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
|
|
||||||
d.SendCommand(LUT_BW)
|
|
||||||
// do not send last two bytes
|
|
||||||
for i := 0; i < 42; i++ {
|
|
||||||
d.SendData(lut[i])
|
|
||||||
}
|
|
||||||
|
|
||||||
lut = [44]uint8{
|
|
||||||
0xa8, 0x16, 0x16, 0x0d, 0x00, 0x01,
|
|
||||||
0x60, 0x23, 0x23, 0x00, 0x00, 0x02,
|
|
||||||
0x54, 0x16, 0x16, 0x0d, 0x00, 0x01,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
|
|
||||||
d.SendCommand(LUT_WB)
|
|
||||||
// do not send last two bytes
|
|
||||||
for i := 0; i < 42; i++ {
|
|
||||||
d.SendData(lut[i])
|
|
||||||
}
|
|
||||||
|
|
||||||
lut = [44]uint8{
|
|
||||||
0xa8, 0x16, 0x16, 0x0d, 0x00, 0x01,
|
|
||||||
0x60, 0x23, 0x23, 0x00, 0x00, 0x02,
|
|
||||||
0x54, 0x16, 0x16, 0x0d, 0x00, 0x01,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
|
|
||||||
d.SendCommand(LUT_BB)
|
|
||||||
// do not send last two bytes
|
|
||||||
for i := 0; i < 42; i++ {
|
|
||||||
d.SendData(lut[i])
|
|
||||||
}
|
|
||||||
|
|
||||||
break
|
|
||||||
case FAST:
|
|
||||||
var lut = [44]uint8{
|
|
||||||
0x00, 0x04, 0x04, 0x07, 0x00, 0x01,
|
|
||||||
0x00, 0x0c, 0x0c, 0x00, 0x00, 0x02,
|
|
||||||
0x00, 0x04, 0x04, 0x07, 0x00, 0x02,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00,
|
|
||||||
}
|
|
||||||
d.SendCommand(LUT_VCOM)
|
|
||||||
for i := 0; i < 44; i++ {
|
|
||||||
d.SendData(lut[i])
|
|
||||||
}
|
|
||||||
lut = [44]uint8{
|
|
||||||
0x54, 0x04, 0x04, 0x07, 0x00, 0x01,
|
|
||||||
0x60, 0x0c, 0x0c, 0x00, 0x00, 0x02,
|
|
||||||
0xa8, 0x04, 0x04, 0x07, 0x00, 0x02,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
|
|
||||||
d.SendCommand(LUT_WW)
|
|
||||||
// do not send last two bytes
|
|
||||||
for i := 0; i < 42; i++ {
|
|
||||||
d.SendData(lut[i])
|
|
||||||
}
|
|
||||||
|
|
||||||
lut = [44]uint8{
|
|
||||||
0x54, 0x04, 0x04, 0x07, 0x00, 0x01,
|
|
||||||
0x60, 0x0c, 0x0c, 0x00, 0x00, 0x02,
|
|
||||||
0xa8, 0x04, 0x04, 0x07, 0x00, 0x02,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
|
|
||||||
d.SendCommand(LUT_BW)
|
|
||||||
// do not send last two bytes
|
|
||||||
for i := 0; i < 42; i++ {
|
|
||||||
d.SendData(lut[i])
|
|
||||||
}
|
|
||||||
|
|
||||||
lut = [44]uint8{
|
|
||||||
0xa8, 0x04, 0x04, 0x07, 0x00, 0x01,
|
|
||||||
0x60, 0x0c, 0x0c, 0x00, 0x00, 0x02,
|
|
||||||
0x54, 0x04, 0x04, 0x07, 0x00, 0x02,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
|
|
||||||
d.SendCommand(LUT_WB)
|
|
||||||
// do not send last two bytes
|
|
||||||
for i := 0; i < 42; i++ {
|
|
||||||
d.SendData(lut[i])
|
|
||||||
}
|
|
||||||
|
|
||||||
lut = [44]uint8{
|
|
||||||
0xa8, 0x04, 0x04, 0x07, 0x00, 0x01,
|
|
||||||
0x60, 0x0c, 0x0c, 0x00, 0x00, 0x02,
|
|
||||||
0x54, 0x04, 0x04, 0x07, 0x00, 0x02,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
|
|
||||||
d.SendCommand(LUT_BB)
|
|
||||||
// do not send last two bytes
|
|
||||||
for i := 0; i < 42; i++ {
|
|
||||||
d.SendData(lut[i])
|
|
||||||
}
|
|
||||||
|
|
||||||
break
|
|
||||||
case TURBO:
|
|
||||||
var lut = [44]uint8{
|
|
||||||
0x00, 0x01, 0x01, 0x02, 0x00, 0x01,
|
|
||||||
0x00, 0x02, 0x02, 0x00, 0x00, 0x02,
|
|
||||||
0x00, 0x02, 0x02, 0x03, 0x00, 0x02,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00,
|
|
||||||
}
|
|
||||||
d.SendCommand(LUT_VCOM)
|
|
||||||
for i := 0; i < 44; i++ {
|
|
||||||
d.SendData(lut[i])
|
|
||||||
}
|
|
||||||
lut = [44]uint8{
|
|
||||||
0x54, 0x01, 0x01, 0x02, 0x00, 0x01,
|
|
||||||
0x60, 0x02, 0x02, 0x00, 0x00, 0x02,
|
|
||||||
0xa8, 0x02, 0x02, 0x03, 0x00, 0x02,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
|
|
||||||
d.SendCommand(LUT_WW)
|
|
||||||
// do not send last two bytes
|
|
||||||
for i := 0; i < 42; i++ {
|
|
||||||
d.SendData(lut[i])
|
|
||||||
}
|
|
||||||
|
|
||||||
lut = [44]uint8{
|
|
||||||
0x54, 0x01, 0x01, 0x02, 0x00, 0x01,
|
|
||||||
0x60, 0x02, 0x02, 0x00, 0x00, 0x02,
|
|
||||||
0xa8, 0x02, 0x02, 0x03, 0x00, 0x02,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
|
|
||||||
d.SendCommand(LUT_BW)
|
|
||||||
// do not send last two bytes
|
|
||||||
for i := 0; i < 42; i++ {
|
|
||||||
d.SendData(lut[i])
|
|
||||||
}
|
|
||||||
|
|
||||||
lut = [44]uint8{
|
|
||||||
0xa8, 0x01, 0x01, 0x02, 0x00, 0x01,
|
|
||||||
0x60, 0x02, 0x02, 0x00, 0x00, 0x02,
|
|
||||||
0x54, 0x02, 0x02, 0x03, 0x00, 0x02,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
|
|
||||||
d.SendCommand(LUT_WB)
|
|
||||||
// do not send last two bytes
|
|
||||||
for i := 0; i < 42; i++ {
|
|
||||||
d.SendData(lut[i])
|
|
||||||
}
|
|
||||||
|
|
||||||
lut = [44]uint8{
|
|
||||||
0xa8, 0x01, 0x01, 0x02, 0x00, 0x01,
|
|
||||||
0x60, 0x02, 0x02, 0x00, 0x00, 0x02,
|
|
||||||
0x54, 0x02, 0x02, 0x03, 0x00, 0x02,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
|
|
||||||
d.SendCommand(LUT_BB)
|
|
||||||
// do not send last two bytes
|
|
||||||
for i := 0; i < 42; i++ {
|
|
||||||
d.SendData(lut[i])
|
|
||||||
}
|
|
||||||
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
var lut = [44]uint8{
|
|
||||||
0x00, 0x64, 0x64, 0x37, 0x00, 0x01,
|
|
||||||
0x00, 0x8c, 0x8c, 0x00, 0x00, 0x04,
|
|
||||||
0x00, 0x64, 0x64, 0x37, 0x00, 0x01,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00,
|
|
||||||
}
|
|
||||||
d.SendCommand(LUT_VCOM)
|
|
||||||
for i := 0; i < 44; i++ {
|
|
||||||
d.SendData(lut[i])
|
|
||||||
}
|
|
||||||
lut = [44]uint8{
|
|
||||||
0x54, 0x64, 0x64, 0x37, 0x00, 0x01,
|
|
||||||
0x60, 0x8c, 0x8c, 0x00, 0x00, 0x04,
|
|
||||||
0xa8, 0x64, 0x64, 0x37, 0x00, 0x01,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
|
|
||||||
d.SendCommand(LUT_WW)
|
|
||||||
// do not send last two bytes
|
|
||||||
for i := 0; i < 42; i++ {
|
|
||||||
d.SendData(lut[i])
|
|
||||||
}
|
|
||||||
|
|
||||||
lut = [44]uint8{
|
|
||||||
0x54, 0x64, 0x64, 0x37, 0x00, 0x01,
|
|
||||||
0x60, 0x8c, 0x8c, 0x00, 0x00, 0x04,
|
|
||||||
0xa8, 0x64, 0x64, 0x37, 0x00, 0x01,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
|
|
||||||
d.SendCommand(LUT_BW)
|
|
||||||
// do not send last two bytes
|
|
||||||
for i := 0; i < 42; i++ {
|
|
||||||
d.SendData(lut[i])
|
|
||||||
}
|
|
||||||
|
|
||||||
lut = [44]uint8{
|
|
||||||
0xa8, 0x64, 0x64, 0x37, 0x00, 0x01,
|
|
||||||
0x60, 0x8c, 0x8c, 0x00, 0x00, 0x04,
|
|
||||||
0x54, 0x64, 0x64, 0x37, 0x00, 0x01,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
|
|
||||||
d.SendCommand(LUT_WB)
|
|
||||||
// do not send last two bytes
|
|
||||||
for i := 0; i < 42; i++ {
|
|
||||||
d.SendData(lut[i])
|
|
||||||
}
|
|
||||||
|
|
||||||
lut = [44]uint8{
|
|
||||||
0xa8, 0x64, 0x64, 0x37, 0x00, 0x01,
|
|
||||||
0x60, 0x8c, 0x8c, 0x00, 0x00, 0x04,
|
|
||||||
0x54, 0x64, 0x64, 0x37, 0x00, 0x01,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
|
|
||||||
d.SendCommand(LUT_BB)
|
|
||||||
// do not send last two bytes
|
|
||||||
for i := 0; i < 42; i++ {
|
|
||||||
d.SendData(lut[i])
|
|
||||||
}
|
|
||||||
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Num. of frames for back-and-forth change.
|
||||||
|
hperiod := period % 2
|
||||||
|
hp := uint8(hperiod / (2 ^ (int(speed) - 1)))
|
||||||
|
if hp < 1 {
|
||||||
|
hp = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if speed < FAST && !flickerFree {
|
||||||
|
// For low speed everything is charge-neutral, even WB/BW.
|
||||||
|
|
||||||
|
// Phase 1: long go-inverted-color.
|
||||||
|
lut.VCOM.SetRow(0, 0x00, [4]uint8{p, 0x00, 0x00, 0x00}, 0x02)
|
||||||
|
lut.BW.SetRow(0, 0b01_000000, [4]uint8{p, 0x00, 0x00, 0x00}, 0x02)
|
||||||
|
lut.WB.SetRow(0, 0b10_000000, [4]uint8{p, 0x00, 0x00, 0x00}, 0x02)
|
||||||
|
|
||||||
|
// Phase 2: short ping/pong.
|
||||||
|
lut.VCOM.SetRow(1, 0x00, [4]uint8{hp, hp, 0x00, 0x00}, 0x02)
|
||||||
|
lut.BW.SetRow(1, 0b10_01_0000, [4]uint8{hp, hp, 0x00, 0x00}, 0x01)
|
||||||
|
lut.WB.SetRow(1, 0b01_10_0000, [4]uint8{hp, hp, 0x00, 0x00}, 0x01)
|
||||||
|
|
||||||
|
// Phase 3: long go-target-color.
|
||||||
|
lut.VCOM.SetRow(2, 0x00, [4]uint8{p, 0x00, 0x00, 0x00}, 0x02)
|
||||||
|
lut.BW.SetRow(2, 0b10_000000, [4]uint8{p, 0x00, 0x00, 0x00}, 0x02)
|
||||||
|
lut.WB.SetRow(2, 0b01_000000, [4]uint8{p, 0x00, 0x00, 0x00}, 0x02)
|
||||||
|
|
||||||
|
// For this speed, we use the same LUTs for WW/BB as well.
|
||||||
|
copy(lut.WW[:], lut.BW[:])
|
||||||
|
copy(lut.BB[:], lut.WB[:])
|
||||||
|
} else {
|
||||||
|
// Speed >= FAST
|
||||||
|
// For greater than 3 we use non charge-neutral LUTs for WB/BW
|
||||||
|
// since the inpulse is short and it gets reversed when the
|
||||||
|
// pixel changes color, so that's not a problem for the display,
|
||||||
|
// however we still need to use charge-neutral LUTs for WW/BB.
|
||||||
|
lut.VCOM.SetRow(0, 0x00, [4]uint8{p, p, p, p}, 0x01)
|
||||||
|
lut.BW.SetRow(0, 0b10_00_00_00, [4]uint8{p * 4, 0x00, 0x00, 0x00}, 0x01)
|
||||||
|
lut.WB.SetRow(0, 0b01_00_00_00, [4]uint8{p * 4, 0x00, 0x00, 0x00}, 0x01)
|
||||||
|
lut.WW.SetRow(0, 0b01_10_00_00, [4]uint8{p * 2, p * 2, 0x00, 0x00}, 0x01)
|
||||||
|
lut.BB.SetRow(0, 0b10_01_00_00, [4]uint8{p * 2, p * 2, 0x00, 0x00}, 0x01)
|
||||||
|
}
|
||||||
|
|
||||||
|
if flickerFree {
|
||||||
|
// If no flickering mode is enabled, we use an empty
|
||||||
|
// waveform BB and WW. The screen will need to be periodically fully refreshed.
|
||||||
|
lut.WW.Clear()
|
||||||
|
lut.BB.Clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
d.SendCommand(LUT_VCOM)
|
||||||
|
d.SendData(append(lut.VCOM[:], []uint8{0, 0}...)...)
|
||||||
|
|
||||||
|
d.SendCommand(LUT_BW)
|
||||||
|
d.SendData(lut.BW[:]...)
|
||||||
|
|
||||||
|
d.SendCommand(LUT_WB)
|
||||||
|
d.SendData(lut.WB[:]...)
|
||||||
|
|
||||||
|
d.SendCommand(LUT_WW)
|
||||||
|
d.SendData(lut.WW[:]...)
|
||||||
|
|
||||||
|
d.SendCommand(LUT_BB)
|
||||||
|
d.SendData(lut.BB[:]...)
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -2,4 +2,4 @@ package drivers
|
|||||||
|
|
||||||
// Version returns a user-readable string showing the version of the drivers package for support purposes.
|
// 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.
|
// Update this value before release of new version of software.
|
||||||
const Version = "0.26.0"
|
const Version = "0.28.0"
|
||||||
|
|||||||
@@ -0,0 +1,287 @@
|
|||||||
|
// Package epd2in66b implements a driver for the Waveshare 2.66inch E-Paper E-Ink Display Module (B)
|
||||||
|
// for Raspberry Pi Pico, 296×152, Red / Black / White
|
||||||
|
// Datasheet: https://files.waveshare.com/upload/e/ec/2.66inch-e-paper-b-specification.pdf
|
||||||
|
package epd2in66b
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image/color"
|
||||||
|
"machine"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"tinygo.org/x/drivers"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
displayWidth = 152
|
||||||
|
displayHeight = 296
|
||||||
|
)
|
||||||
|
|
||||||
|
const Baudrate = 4_000_000 // 4 MHz
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
ResetPin machine.Pin
|
||||||
|
DataPin machine.Pin
|
||||||
|
ChipSelectPin machine.Pin
|
||||||
|
BusyPin machine.Pin
|
||||||
|
}
|
||||||
|
|
||||||
|
type Device struct {
|
||||||
|
bus drivers.SPI
|
||||||
|
cs machine.Pin
|
||||||
|
dc machine.Pin
|
||||||
|
rst machine.Pin
|
||||||
|
busy machine.Pin
|
||||||
|
|
||||||
|
blackBuffer []byte
|
||||||
|
redBuffer []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// New allocates a new device.
|
||||||
|
// The bus is expected to be configured and ready for use.
|
||||||
|
func New(bus drivers.SPI) Device {
|
||||||
|
pixelCount := displayWidth * displayHeight
|
||||||
|
|
||||||
|
bufLen := pixelCount / 8
|
||||||
|
|
||||||
|
return Device{
|
||||||
|
bus: bus,
|
||||||
|
blackBuffer: make([]byte, bufLen),
|
||||||
|
redBuffer: make([]byte, bufLen),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure configures the device and its pins.
|
||||||
|
func (d *Device) Configure(c Config) error {
|
||||||
|
d.cs = c.ChipSelectPin
|
||||||
|
d.dc = c.DataPin
|
||||||
|
d.rst = c.ResetPin
|
||||||
|
d.busy = c.BusyPin
|
||||||
|
|
||||||
|
d.cs.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||||
|
d.dc.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||||
|
d.rst.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||||
|
d.busy.Configure(machine.PinConfig{Mode: machine.PinInput})
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Device) Size() (x, y int16) {
|
||||||
|
return displayWidth, displayHeight
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetPixel modifies the internal buffer in a single pixel.
|
||||||
|
// The display has 3 colors: red, black and white
|
||||||
|
//
|
||||||
|
// - white = RGBA(255,255,255, 1-255)
|
||||||
|
// - red = RGBA(1-255,0,0,1-255)
|
||||||
|
// - Anything else as black
|
||||||
|
func (d *Device) SetPixel(x int16, y int16, c color.RGBA) {
|
||||||
|
if x < 0 || x >= displayWidth || y < 0 || y >= displayHeight {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
bytePos, bitPos := pos(x, y, displayWidth)
|
||||||
|
|
||||||
|
if c.R == 0xff && c.G == 0xff && c.B == 0xff && c.A > 0 { // white
|
||||||
|
set(d.blackBuffer, bytePos, bitPos)
|
||||||
|
unset(d.redBuffer, bytePos, bitPos)
|
||||||
|
} else if c.R != 0 && c.G == 0 && c.B == 0 && c.A > 0 { // red-ish
|
||||||
|
set(d.blackBuffer, bytePos, bitPos)
|
||||||
|
set(d.redBuffer, bytePos, bitPos)
|
||||||
|
} else { // black or other
|
||||||
|
unset(d.blackBuffer, bytePos, bitPos)
|
||||||
|
unset(d.redBuffer, bytePos, bitPos)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func set(buf []byte, bytePos, bitPos int) {
|
||||||
|
buf[bytePos] |= 0x1 << bitPos
|
||||||
|
}
|
||||||
|
|
||||||
|
func unset(buf []byte, bytePos, bitPos int) {
|
||||||
|
buf[bytePos] &^= 0x1 << bitPos
|
||||||
|
}
|
||||||
|
|
||||||
|
func pos(x, y, stride int16) (bytePos int, bitPos int) {
|
||||||
|
p := int(x) + int(y)*int(stride)
|
||||||
|
bytePos = p / 8
|
||||||
|
|
||||||
|
// reverse bit position as it is reversed on the device's buffer
|
||||||
|
bitPos = 7 - p%8
|
||||||
|
|
||||||
|
return bytePos, bitPos
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Device) Display() error {
|
||||||
|
// Write RAM (Black White) / RAM 0x24
|
||||||
|
// 1 == white, 0 == black
|
||||||
|
if err := d.sendCommandByte(0x24); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.sendData(d.blackBuffer); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write RAM (RED) / RAM 0x26)
|
||||||
|
// 0 == blank, 1 == red
|
||||||
|
if err := d.sendCommandByte(0x26); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.sendData(d.redBuffer); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return d.turnOnDisplay()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Device) ClearBuffer() {
|
||||||
|
fill(d.redBuffer, 0x00)
|
||||||
|
fill(d.blackBuffer, 0xff)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Device) turnOnDisplay() error {
|
||||||
|
// also documented as 'Master Activation'
|
||||||
|
if err := d.sendCommandByte(0x20); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
d.WaitUntilIdle()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Device) Reset() error {
|
||||||
|
d.hwReset()
|
||||||
|
d.WaitUntilIdle()
|
||||||
|
|
||||||
|
// soft reset & set defaults
|
||||||
|
if err := d.sendCommandByte(0x12); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
d.WaitUntilIdle()
|
||||||
|
|
||||||
|
// data entry mode setting
|
||||||
|
if err := d.sendCommandSequence([]byte{0x11, 0x03}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.setWindow(0, displayWidth-1, 0, displayHeight-1); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// display update control 1 - resolution setting
|
||||||
|
if err := d.sendCommandSequence([]byte{0x21, 0x00, 0x80}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.setCursor(0, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
d.WaitUntilIdle()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Device) setCursor(x, y uint16) error {
|
||||||
|
// Set RAM X address counter
|
||||||
|
if err := d.sendCommandSequence([]byte{0x4e, byte(x & 0x1f)}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set RAM Y address counter
|
||||||
|
yLo := byte(y)
|
||||||
|
yHi := byte(y>>8) & 0x1
|
||||||
|
if err := d.sendCommandSequence([]byte{0x4f, yLo, yHi}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Device) hwReset() {
|
||||||
|
d.rst.High()
|
||||||
|
time.Sleep(50 * time.Millisecond)
|
||||||
|
d.rst.Low()
|
||||||
|
time.Sleep(2 * time.Millisecond)
|
||||||
|
d.rst.High()
|
||||||
|
time.Sleep(50 * time.Millisecond)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Device) setWindow(xstart, xend, ystart, yend int16) error {
|
||||||
|
// set RAM X-address start / end position
|
||||||
|
d1 := byte((xstart >> 3) & 0x1f)
|
||||||
|
d2 := byte((xend >> 3) & 0x1f)
|
||||||
|
if err := d.sendCommandSequence([]byte{0x44, d1, d2}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// set RAM Y-address start / end position
|
||||||
|
ystartLo := byte(ystart)
|
||||||
|
ystartHi := byte(ystart>>8) & 0x1
|
||||||
|
|
||||||
|
yendLo := byte(yend)
|
||||||
|
yendHi := byte(yend>>8) & 0x1
|
||||||
|
|
||||||
|
return d.sendCommandSequence([]byte{0x45, ystartLo, ystartHi, yendLo, yendHi})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Device) WaitUntilIdle() {
|
||||||
|
// give it some time to get busy
|
||||||
|
time.Sleep(50 * time.Millisecond)
|
||||||
|
|
||||||
|
for d.busy.Get() { // high = busy
|
||||||
|
time.Sleep(10 * time.Millisecond)
|
||||||
|
}
|
||||||
|
|
||||||
|
// give it some extra time
|
||||||
|
time.Sleep(50 * time.Millisecond)
|
||||||
|
}
|
||||||
|
|
||||||
|
// sendCommandSequence sends the first byte in the buffer as a 'command' and all following bytes as data
|
||||||
|
func (d *Device) sendCommandSequence(seq []byte) error {
|
||||||
|
err := d.sendCommandByte(seq[0])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 1; i < len(seq); i++ {
|
||||||
|
err = d.sendDataByte(seq[i])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Device) sendCommandByte(b byte) error {
|
||||||
|
d.dc.Low()
|
||||||
|
d.cs.Low()
|
||||||
|
_, err := d.bus.Transfer(b)
|
||||||
|
d.cs.High()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Device) sendDataByte(b byte) error {
|
||||||
|
d.dc.High()
|
||||||
|
d.cs.Low()
|
||||||
|
_, err := d.bus.Transfer(b)
|
||||||
|
d.cs.High()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Device) sendData(b []byte) error {
|
||||||
|
d.dc.High()
|
||||||
|
d.cs.Low()
|
||||||
|
err := d.bus.Tx(b, nil)
|
||||||
|
d.cs.High()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// fill quickly fills a slice with a given value
|
||||||
|
func fill(s []byte, b byte) {
|
||||||
|
s[0] = b
|
||||||
|
for j := 1; j < len(s); j *= 2 {
|
||||||
|
copy(s[j:], s[:j])
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
//go:build pico
|
||||||
|
|
||||||
|
package epd2in66b
|
||||||
|
|
||||||
|
import (
|
||||||
|
"machine"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DefaultConfig contains the default config for the https://www.waveshare.com/wiki/Pico-ePaper-2.66 module
|
||||||
|
var DefaultConfig = Config{
|
||||||
|
DataPin: machine.GP8,
|
||||||
|
ChipSelectPin: machine.GP9,
|
||||||
|
ResetPin: machine.GP12,
|
||||||
|
BusyPin: machine.GP13,
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewPicoModule allocates a new device backed by the https://www.waveshare.com/wiki/Pico-ePaper-2.66 module
|
||||||
|
// This will also configure the SPI1 bus and configure the device with the DefaultConfig
|
||||||
|
func NewPicoModule() (Device, error) {
|
||||||
|
spi := machine.SPI1
|
||||||
|
|
||||||
|
if err := spi.Configure(machine.SPIConfig{
|
||||||
|
Frequency: Baudrate,
|
||||||
|
}); err != nil {
|
||||||
|
return Device{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
dev := New(spi)
|
||||||
|
if err := dev.Configure(DefaultConfig); err != nil {
|
||||||
|
return dev, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return dev, nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
package epd2in66b
|
||||||
|
|
||||||
|
import (
|
||||||
|
_ "embed"
|
||||||
|
"fmt"
|
||||||
|
"image"
|
||||||
|
"image/color"
|
||||||
|
"image/draw"
|
||||||
|
"image/png"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"tinygo.org/x/drivers"
|
||||||
|
"tinygo.org/x/tinyfont"
|
||||||
|
"tinygo.org/x/tinyfont/freemono"
|
||||||
|
)
|
||||||
|
|
||||||
|
type mockBus struct{}
|
||||||
|
|
||||||
|
func (m *mockBus) Tx(w, r []byte) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockBus) Transfer(b byte) (byte, error) {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBufferDrawing(t *testing.T) {
|
||||||
|
dev := New(&mockBus{})
|
||||||
|
|
||||||
|
tinyfont.WriteLine(&dev, &freemono.Bold9pt7b, 10, 40, "Hello World!", color.RGBA{0xff, 0xff, 0xff, 0xff})
|
||||||
|
|
||||||
|
red := color.RGBA{0xff, 0, 0, 0xff}
|
||||||
|
black := color.RGBA{0xff, 0xff, 0xff, 0xff}
|
||||||
|
showRect(&dev, 10, 10, 10, 10, black)
|
||||||
|
showRect(&dev, 10, 20, 10, 10, red)
|
||||||
|
|
||||||
|
img := toImage(&dev)
|
||||||
|
writeImage(img)
|
||||||
|
}
|
||||||
|
|
||||||
|
func toImage(dev *Device) *image.RGBA {
|
||||||
|
red := color.RGBA{0xff, 0, 0, 0xff}
|
||||||
|
|
||||||
|
xMax, yMax := dev.Size()
|
||||||
|
r := image.Rect(0, 0, int(xMax), int(yMax))
|
||||||
|
container := image.NewRGBA(r)
|
||||||
|
draw.Draw(container, container.Bounds(), image.NewUniform(color.White), image.Point{}, draw.Over)
|
||||||
|
|
||||||
|
for x := 0; x < int(xMax); x++ {
|
||||||
|
for y := 0; y < int(yMax); y++ {
|
||||||
|
|
||||||
|
bytePos, bitPos := pos(int16(x), int16(y), displayWidth)
|
||||||
|
|
||||||
|
if isSet(dev.redBuffer, bytePos, bitPos) {
|
||||||
|
container.Set(x, y, red)
|
||||||
|
} else if isSet(dev.blackBuffer, bytePos, bitPos) {
|
||||||
|
container.Set(x, y, color.Black)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return container
|
||||||
|
}
|
||||||
|
|
||||||
|
func isSet(buf []byte, bytePos, bitPos int) bool {
|
||||||
|
return (buf[bytePos])&(0x1<<bitPos) != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func showRect(display drivers.Displayer, x int16, y int16, w int16, h int16, c color.RGBA) {
|
||||||
|
for i := x; i < x+w; i++ {
|
||||||
|
for j := y; j < y+h; j++ {
|
||||||
|
display.SetPixel(i, j, c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeImage(img image.Image) string {
|
||||||
|
fn := fmt.Sprintf("%d.png", time.Now().Unix())
|
||||||
|
f, err := os.OpenFile(fn, os.O_RDWR|os.O_CREATE, 0o644)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
err = png.Encode(f, img)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return fn
|
||||||
|
}
|
||||||
+244
-106
@@ -33,7 +33,6 @@ var _debug debug = debugBasic
|
|||||||
//var _debug debug = debugBasic | debugNetdev | debugCmd | debugDetail
|
//var _debug debug = debugBasic | debugNetdev | debugCmd | debugDetail
|
||||||
|
|
||||||
var (
|
var (
|
||||||
version = "0.0.1"
|
|
||||||
driverName = "Tinygo ESP32 Wifi network device driver (WiFiNINA)"
|
driverName = "Tinygo ESP32 Wifi network device driver (WiFiNINA)"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -48,6 +47,9 @@ const (
|
|||||||
statusConnectFailed connectionStatus = 4
|
statusConnectFailed connectionStatus = 4
|
||||||
statusConnectionLost connectionStatus = 5
|
statusConnectionLost connectionStatus = 5
|
||||||
statusDisconnected connectionStatus = 6
|
statusDisconnected connectionStatus = 6
|
||||||
|
statusAPListening connectionStatus = 7
|
||||||
|
statusAPConnected connectionStatus = 8
|
||||||
|
statusAPFailed connectionStatus = 9
|
||||||
|
|
||||||
encTypeTKIP encryptionType = 2
|
encTypeTKIP encryptionType = 2
|
||||||
encTypeCCMP encryptionType = 4
|
encTypeCCMP encryptionType = 4
|
||||||
@@ -163,10 +165,12 @@ type encryptionType uint8
|
|||||||
type sock uint8
|
type sock uint8
|
||||||
type hwerr uint8
|
type hwerr uint8
|
||||||
|
|
||||||
type socket struct {
|
type Socket struct {
|
||||||
protocol int
|
protocol int
|
||||||
ip netip.AddrPort
|
clientConnected bool
|
||||||
inuse bool
|
laddr netip.AddrPort // Set in Bind()
|
||||||
|
raddr netip.AddrPort // Set in Connect()
|
||||||
|
sock // Device socket, as returned from w.getSocket()
|
||||||
}
|
}
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
@@ -213,17 +217,13 @@ type wifinina struct {
|
|||||||
killWatchdog chan bool
|
killWatchdog chan bool
|
||||||
fault error
|
fault error
|
||||||
|
|
||||||
sockets map[sock]*socket // keyed by sock as returned by getSocket()
|
sockets map[int]*Socket // keyed by sockfd
|
||||||
}
|
|
||||||
|
|
||||||
func newSocket(protocol int) *socket {
|
|
||||||
return &socket{protocol: protocol, inuse: true}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(cfg *Config) *wifinina {
|
func New(cfg *Config) *wifinina {
|
||||||
w := wifinina{
|
w := wifinina{
|
||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
sockets: make(map[sock]*socket),
|
sockets: make(map[int]*Socket),
|
||||||
killWatchdog: make(chan bool),
|
killWatchdog: make(chan bool),
|
||||||
cs: cfg.Cs,
|
cs: cfg.Cs,
|
||||||
ack: cfg.Ack,
|
ack: cfg.Ack,
|
||||||
@@ -302,6 +302,61 @@ func (w *wifinina) connectToAP() error {
|
|||||||
return netlink.ErrConnectTimeout
|
return netlink.ErrConnectTimeout
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w *wifinina) startAP() error {
|
||||||
|
timeout := w.params.ConnectTimeout
|
||||||
|
if timeout == 0 {
|
||||||
|
timeout = netlink.DefaultConnectTimeout
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(w.params.Ssid) == 0 {
|
||||||
|
return netlink.ErrMissingSSID
|
||||||
|
}
|
||||||
|
|
||||||
|
if debugging(debugBasic) {
|
||||||
|
fmt.Printf("Starting Wifi AP as SSID '%s'...", w.params.Ssid)
|
||||||
|
}
|
||||||
|
|
||||||
|
start := time.Now()
|
||||||
|
|
||||||
|
// Start the connection process
|
||||||
|
switch {
|
||||||
|
case w.params.Passphrase != "":
|
||||||
|
w.setPassphraseForAP(w.params.Ssid, w.params.Passphrase)
|
||||||
|
default:
|
||||||
|
w.setNetworkForAP(w.params.Ssid)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if we are listening
|
||||||
|
for {
|
||||||
|
status := w.getConnectionStatus()
|
||||||
|
switch status {
|
||||||
|
case statusAPListening:
|
||||||
|
if debugging(debugBasic) {
|
||||||
|
fmt.Printf("LISTENING\r\n")
|
||||||
|
}
|
||||||
|
if w.notifyCb != nil {
|
||||||
|
w.notifyCb(netlink.EventNetUp)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
case statusAPFailed:
|
||||||
|
if debugging(debugBasic) {
|
||||||
|
fmt.Printf("FAILED (%s)\r\n", w.reason())
|
||||||
|
}
|
||||||
|
return netlink.ErrConnectFailed
|
||||||
|
}
|
||||||
|
if time.Since(start) > timeout {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
}
|
||||||
|
|
||||||
|
if debugging(debugBasic) {
|
||||||
|
fmt.Printf("FAILED (timed out)\r\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
return netlink.ErrConnectTimeout
|
||||||
|
}
|
||||||
|
|
||||||
func (w *wifinina) netDisconnect() {
|
func (w *wifinina) netDisconnect() {
|
||||||
w.disconnect()
|
w.disconnect()
|
||||||
}
|
}
|
||||||
@@ -313,7 +368,7 @@ func (w *wifinina) showDriver() {
|
|||||||
if debugging(debugBasic) {
|
if debugging(debugBasic) {
|
||||||
fmt.Printf("\r\n")
|
fmt.Printf("\r\n")
|
||||||
fmt.Printf("%s\r\n\r\n", driverName)
|
fmt.Printf("%s\r\n\r\n", driverName)
|
||||||
fmt.Printf("Driver version : %s\r\n", version)
|
fmt.Printf("Driver version : %s\r\n", drivers.Version)
|
||||||
}
|
}
|
||||||
w.driverShown = true
|
w.driverShown = true
|
||||||
}
|
}
|
||||||
@@ -383,7 +438,12 @@ func (w *wifinina) showIP() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (w *wifinina) networkDown() bool {
|
func (w *wifinina) networkDown() bool {
|
||||||
return w.getConnectionStatus() != statusConnected
|
switch w.getConnectionStatus() {
|
||||||
|
case statusConnected, statusAPListening, statusAPConnected:
|
||||||
|
return false
|
||||||
|
default:
|
||||||
|
return true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *wifinina) watchdog() {
|
func (w *wifinina) watchdog() {
|
||||||
@@ -421,15 +481,28 @@ func (w *wifinina) netConnect(reset bool) error {
|
|||||||
}
|
}
|
||||||
w.showDevice()
|
w.showDevice()
|
||||||
|
|
||||||
|
retry:
|
||||||
for i := 0; w.params.Retries == 0 || i < w.params.Retries; i++ {
|
for i := 0; w.params.Retries == 0 || i < w.params.Retries; i++ {
|
||||||
if err := w.connectToAP(); err != nil {
|
switch w.params.ConnectMode {
|
||||||
switch err {
|
case netlink.ConnectModeAP:
|
||||||
case netlink.ErrConnectTimeout, netlink.ErrConnectFailed:
|
if err := w.startAP(); err != nil {
|
||||||
continue
|
switch err {
|
||||||
|
case netlink.ErrConnectTimeout, netlink.ErrConnectFailed:
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
return err
|
break retry
|
||||||
|
default:
|
||||||
|
if err := w.connectToAP(); err != nil {
|
||||||
|
switch err {
|
||||||
|
case netlink.ErrConnectTimeout, netlink.ErrConnectFailed:
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
break retry
|
||||||
}
|
}
|
||||||
break
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if w.networkDown() {
|
if w.networkDown() {
|
||||||
@@ -504,6 +577,12 @@ func (w *wifinina) GetHostByName(name string) (netip.Addr, error) {
|
|||||||
fmt.Printf("[GetHostByName] name: %s\r\n", name)
|
fmt.Printf("[GetHostByName] name: %s\r\n", name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If it's already in dotted-decimal notation, return a copy
|
||||||
|
// per gethostbyname(3).
|
||||||
|
if ip, err := netip.ParseAddr(name); err == nil {
|
||||||
|
return ip, nil
|
||||||
|
}
|
||||||
|
|
||||||
w.mu.Lock()
|
w.mu.Lock()
|
||||||
defer w.mu.Unlock()
|
defer w.mu.Unlock()
|
||||||
|
|
||||||
@@ -546,6 +625,20 @@ func (w *wifinina) Addr() (netip.Addr, error) {
|
|||||||
return ip, nil
|
return ip, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// newSockfd returns the next available sockfd, or -1 if none available
|
||||||
|
func (w *wifinina) newSockfd() int {
|
||||||
|
if len(w.sockets) >= maxNetworks {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
// Search for the next available sockfd starting at 0
|
||||||
|
for sockfd := 0; ; sockfd++ {
|
||||||
|
if _, ok := w.sockets[sockfd]; !ok {
|
||||||
|
return sockfd
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
// See man socket(2) for standard Berkely sockets for Socket, Bind, etc.
|
// See man socket(2) for standard Berkely sockets for Socket, Bind, etc.
|
||||||
// The driver strives to meet the function and semantics of socket(2).
|
// The driver strives to meet the function and semantics of socket(2).
|
||||||
|
|
||||||
@@ -573,37 +666,49 @@ func (w *wifinina) Socket(domain int, stype int, protocol int) (int, error) {
|
|||||||
w.mu.Lock()
|
w.mu.Lock()
|
||||||
defer w.mu.Unlock()
|
defer w.mu.Unlock()
|
||||||
|
|
||||||
sock := w.getSocket()
|
sockfd := w.newSockfd()
|
||||||
if sock == noSocketAvail {
|
if sockfd == -1 {
|
||||||
return -1, netdev.ErrNoMoreSockets
|
return -1, netdev.ErrNoMoreSockets
|
||||||
}
|
}
|
||||||
|
|
||||||
socket := newSocket(protocol)
|
w.sockets[sockfd] = &Socket{
|
||||||
w.sockets[sock] = socket
|
protocol: protocol,
|
||||||
|
sock: noSocketAvail,
|
||||||
|
}
|
||||||
|
|
||||||
return int(sock), nil
|
if debugging(debugNetdev) {
|
||||||
|
fmt.Printf("[Socket] <-- sockfd %d\r\n", sockfd)
|
||||||
|
}
|
||||||
|
|
||||||
|
return sockfd, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *wifinina) Bind(sockfd int, ip netip.AddrPort) error {
|
func (w *wifinina) Bind(sockfd int, ip netip.AddrPort) error {
|
||||||
|
|
||||||
if debugging(debugNetdev) {
|
if debugging(debugNetdev) {
|
||||||
fmt.Printf("[Bind] sockfd: %d, addr: %s\r\n", sockfd, ip)
|
fmt.Printf("[Bind] sockfd: %d, addr: %s:%d\r\n", sockfd, ip.Addr(), ip.Port())
|
||||||
}
|
}
|
||||||
|
|
||||||
w.mu.Lock()
|
w.mu.Lock()
|
||||||
defer w.mu.Unlock()
|
defer w.mu.Unlock()
|
||||||
|
|
||||||
var sock = sock(sockfd)
|
socket, ok := w.sockets[sockfd]
|
||||||
var socket = w.sockets[sock]
|
if !ok {
|
||||||
|
return netdev.ErrInvalidSocketFd
|
||||||
|
}
|
||||||
|
|
||||||
switch socket.protocol {
|
switch socket.protocol {
|
||||||
case netdev.IPPROTO_TCP:
|
case netdev.IPPROTO_TCP:
|
||||||
case netdev.IPPROTO_TLS:
|
case netdev.IPPROTO_TLS:
|
||||||
case netdev.IPPROTO_UDP:
|
case netdev.IPPROTO_UDP:
|
||||||
w.startServer(sock, ip.Port(), protoModeUDP)
|
socket.sock = w.getSocket()
|
||||||
|
if socket.sock == noSocketAvail {
|
||||||
|
return netdev.ErrNoMoreSockets
|
||||||
|
}
|
||||||
|
w.startServer(socket.sock, ip.Port(), protoModeUDP)
|
||||||
}
|
}
|
||||||
|
|
||||||
socket.ip = ip
|
socket.laddr = ip
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -628,21 +733,40 @@ func (w *wifinina) Connect(sockfd int, host string, ip netip.AddrPort) error {
|
|||||||
w.mu.Lock()
|
w.mu.Lock()
|
||||||
defer w.mu.Unlock()
|
defer w.mu.Unlock()
|
||||||
|
|
||||||
var sock = sock(sockfd)
|
socket, ok := w.sockets[sockfd]
|
||||||
var socket = w.sockets[sock]
|
if !ok {
|
||||||
|
return netdev.ErrInvalidSocketFd
|
||||||
|
}
|
||||||
|
|
||||||
// Start the connection
|
// Start the connection
|
||||||
switch socket.protocol {
|
switch socket.protocol {
|
||||||
|
|
||||||
case netdev.IPPROTO_TCP:
|
case netdev.IPPROTO_TCP:
|
||||||
w.startClient(sock, "", toUint32(ip.Addr().As4()), ip.Port(), protoModeTCP)
|
socket.sock = w.getSocket()
|
||||||
|
if socket.sock == noSocketAvail {
|
||||||
|
return netdev.ErrNoMoreSockets
|
||||||
|
}
|
||||||
|
w.startClient(socket.sock, "", toUint32(ip.Addr().As4()), ip.Port(), protoModeTCP)
|
||||||
|
|
||||||
case netdev.IPPROTO_TLS:
|
case netdev.IPPROTO_TLS:
|
||||||
w.startClient(sock, host, 0, ip.Port(), protoModeTLS)
|
socket.sock = w.getSocket()
|
||||||
|
if socket.sock == noSocketAvail {
|
||||||
|
return netdev.ErrNoMoreSockets
|
||||||
|
}
|
||||||
|
w.startClient(socket.sock, host, 0, ip.Port(), protoModeTLS)
|
||||||
|
|
||||||
case netdev.IPPROTO_UDP:
|
case netdev.IPPROTO_UDP:
|
||||||
w.startClient(sock, "", toUint32(ip.Addr().As4()), ip.Port(), protoModeUDP)
|
if socket.sock == noSocketAvail {
|
||||||
|
return fmt.Errorf("Must Bind before Connecting")
|
||||||
|
}
|
||||||
|
// See start in sendUDP()
|
||||||
|
socket.raddr = ip
|
||||||
|
socket.clientConnected = true
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if w.getClientState(sock) == tcpStateEstablished {
|
if w.getClientState(socket.sock) == tcpStateEstablished {
|
||||||
|
socket.clientConnected = true
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -662,12 +786,18 @@ func (w *wifinina) Listen(sockfd int, backlog int) error {
|
|||||||
w.mu.Lock()
|
w.mu.Lock()
|
||||||
defer w.mu.Unlock()
|
defer w.mu.Unlock()
|
||||||
|
|
||||||
var sock = sock(sockfd)
|
socket, ok := w.sockets[sockfd]
|
||||||
var socket = w.sockets[sock]
|
if !ok {
|
||||||
|
return netdev.ErrInvalidSocketFd
|
||||||
|
}
|
||||||
|
|
||||||
switch socket.protocol {
|
switch socket.protocol {
|
||||||
case netdev.IPPROTO_TCP:
|
case netdev.IPPROTO_TCP:
|
||||||
w.startServer(sock, socket.ip.Port(), protoModeTCP)
|
socket.sock = w.getSocket()
|
||||||
|
if socket.sock == noSocketAvail {
|
||||||
|
return netdev.ErrNoMoreSockets
|
||||||
|
}
|
||||||
|
w.startServer(socket.sock, socket.laddr.Port(), protoModeTCP)
|
||||||
case netdev.IPPROTO_UDP:
|
case netdev.IPPROTO_UDP:
|
||||||
default:
|
default:
|
||||||
return netdev.ErrProtocolNotSupported
|
return netdev.ErrProtocolNotSupported
|
||||||
@@ -676,27 +806,29 @@ func (w *wifinina) Listen(sockfd int, backlog int) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *wifinina) Accept(sockfd int, ip netip.AddrPort) (int, error) {
|
func (w *wifinina) Accept(sockfd int) (int, netip.AddrPort, error) {
|
||||||
|
|
||||||
if debugging(debugNetdev) {
|
if debugging(debugNetdev) {
|
||||||
fmt.Printf("[Accept] sockfd: %d, peer: %s\r\n", sockfd, ip)
|
fmt.Printf("[Accept] sockfd: %d\r\n", sockfd)
|
||||||
}
|
}
|
||||||
|
|
||||||
w.mu.Lock()
|
w.mu.Lock()
|
||||||
defer w.mu.Unlock()
|
defer w.mu.Unlock()
|
||||||
|
|
||||||
var client sock
|
socket, ok := w.sockets[sockfd]
|
||||||
var sock = sock(sockfd)
|
if !ok {
|
||||||
var socket = w.sockets[sock]
|
return -1, netip.AddrPort{}, netdev.ErrInvalidSocketFd
|
||||||
|
}
|
||||||
|
|
||||||
switch socket.protocol {
|
switch socket.protocol {
|
||||||
case netdev.IPPROTO_TCP:
|
case netdev.IPPROTO_TCP:
|
||||||
default:
|
default:
|
||||||
return -1, netdev.ErrProtocolNotSupported
|
return -1, netip.AddrPort{}, netdev.ErrProtocolNotSupported
|
||||||
}
|
}
|
||||||
|
|
||||||
|
skip:
|
||||||
for {
|
for {
|
||||||
// Accept() will be sleeping most of the time, checking for a
|
// Accept() will be sleeping most of the time, checking for
|
||||||
// new clients every 1/10 sec.
|
// new clients every 1/10 sec.
|
||||||
w.mu.Unlock()
|
w.mu.Unlock()
|
||||||
time.Sleep(100 * time.Millisecond)
|
time.Sleep(100 * time.Millisecond)
|
||||||
@@ -704,49 +836,46 @@ func (w *wifinina) Accept(sockfd int, ip netip.AddrPort) (int, error) {
|
|||||||
|
|
||||||
// Check if we've faulted
|
// Check if we've faulted
|
||||||
if w.fault != nil {
|
if w.fault != nil {
|
||||||
return -1, w.fault
|
return -1, netip.AddrPort{}, w.fault
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: BUG: Currently, a sock that is 100% busy will always be
|
|
||||||
// TODO: returned by w.accept(sock), starving other socks
|
|
||||||
// TODO: from begin serviced. Need to figure out how to
|
|
||||||
// TODO: service socks fairly (round-robin?) so no one sock
|
|
||||||
// TODO: can dominate.
|
|
||||||
|
|
||||||
// Check if a client has data
|
// Check if a client has data
|
||||||
client = w.accept(sock)
|
var client sock = w.accept(socket.sock)
|
||||||
if client == noSocketAvail {
|
if client == noSocketAvail {
|
||||||
// None ready
|
// None ready
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we've already seen this socket, we can reuse
|
// If we already have a socket for the client, skip
|
||||||
// the socket and return it. But, only if the socket
|
for _, s := range w.sockets {
|
||||||
// is closed. If it's not closed, we'll just come back
|
if s.sock == client {
|
||||||
// later to reuse it.
|
continue skip
|
||||||
|
|
||||||
clientSocket, ok := w.sockets[client]
|
|
||||||
if ok {
|
|
||||||
// Wait for client to Close
|
|
||||||
if clientSocket.inuse {
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
// Reuse client socket
|
|
||||||
return int(client), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create new socket for client and return fd
|
// Otherwise, create a new socket
|
||||||
w.sockets[client] = newSocket(socket.protocol)
|
clientfd := w.newSockfd()
|
||||||
return int(client), nil
|
if clientfd == -1 {
|
||||||
|
return -1, netip.AddrPort{}, netdev.ErrNoMoreSockets
|
||||||
|
}
|
||||||
|
|
||||||
|
w.sockets[clientfd] = &Socket{
|
||||||
|
protocol: netdev.IPPROTO_TCP,
|
||||||
|
sock: client,
|
||||||
|
clientConnected: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
raddr := w.getRemoteData(client)
|
||||||
|
|
||||||
|
return clientfd, raddr, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *wifinina) sockDown(sock sock) bool {
|
func (w *wifinina) sockDown(socket *Socket) bool {
|
||||||
var socket = w.sockets[sock]
|
|
||||||
if socket.protocol == netdev.IPPROTO_UDP {
|
if socket.protocol == netdev.IPPROTO_UDP {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return w.getClientState(sock) != tcpStateEstablished
|
return w.getClientState(socket.sock) != tcpStateEstablished
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *wifinina) sendTCP(sock sock, buf []byte, deadline time.Time) (int, error) {
|
func (w *wifinina) sendTCP(sock sock, buf []byte, deadline time.Time) (int, error) {
|
||||||
@@ -774,7 +903,7 @@ func (w *wifinina) sendTCP(sock sock, buf []byte, deadline time.Time) (int, erro
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check if socket went down
|
// Check if socket went down
|
||||||
if w.sockDown(sock) {
|
if w.getClientState(sock) != tcpStateEstablished {
|
||||||
return -1, io.EOF
|
return -1, io.EOF
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -792,7 +921,10 @@ func (w *wifinina) sendTCP(sock sock, buf []byte, deadline time.Time) (int, erro
|
|||||||
return -1, netdev.ErrTimeout
|
return -1, netdev.ErrTimeout
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *wifinina) sendUDP(sock sock, buf []byte, deadline time.Time) (int, error) {
|
func (w *wifinina) sendUDP(sock sock, raddr netip.AddrPort, buf []byte, deadline time.Time) (int, error) {
|
||||||
|
|
||||||
|
// Start a client for each send
|
||||||
|
w.startClient(sock, "", toUint32(raddr.Addr().As4()), raddr.Port(), protoModeUDP)
|
||||||
|
|
||||||
// Queue it
|
// Queue it
|
||||||
ok := w.insertDataBuf(sock, buf)
|
ok := w.insertDataBuf(sock, buf)
|
||||||
@@ -810,8 +942,10 @@ func (w *wifinina) sendUDP(sock sock, buf []byte, deadline time.Time) (int, erro
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (w *wifinina) sendChunk(sockfd int, buf []byte, deadline time.Time) (int, error) {
|
func (w *wifinina) sendChunk(sockfd int, buf []byte, deadline time.Time) (int, error) {
|
||||||
var sock = sock(sockfd)
|
socket, ok := w.sockets[sockfd]
|
||||||
var socket = w.sockets[sock]
|
if !ok {
|
||||||
|
return -1, netdev.ErrInvalidSocketFd
|
||||||
|
}
|
||||||
|
|
||||||
// Check if we've timed out
|
// Check if we've timed out
|
||||||
if !deadline.IsZero() {
|
if !deadline.IsZero() {
|
||||||
@@ -822,9 +956,9 @@ func (w *wifinina) sendChunk(sockfd int, buf []byte, deadline time.Time) (int, e
|
|||||||
|
|
||||||
switch socket.protocol {
|
switch socket.protocol {
|
||||||
case netdev.IPPROTO_TCP, netdev.IPPROTO_TLS:
|
case netdev.IPPROTO_TCP, netdev.IPPROTO_TLS:
|
||||||
return w.sendTCP(sock, buf, deadline)
|
return w.sendTCP(socket.sock, buf, deadline)
|
||||||
case netdev.IPPROTO_UDP:
|
case netdev.IPPROTO_UDP:
|
||||||
return w.sendUDP(sock, buf, deadline)
|
return w.sendUDP(socket.sock, socket.raddr, buf, deadline)
|
||||||
}
|
}
|
||||||
|
|
||||||
return -1, netdev.ErrProtocolNotSupported
|
return -1, netdev.ErrProtocolNotSupported
|
||||||
@@ -869,7 +1003,10 @@ func (w *wifinina) Recv(sockfd int, buf []byte, flags int,
|
|||||||
w.mu.Lock()
|
w.mu.Lock()
|
||||||
defer w.mu.Unlock()
|
defer w.mu.Unlock()
|
||||||
|
|
||||||
var sock = sock(sockfd)
|
socket, ok := w.sockets[sockfd]
|
||||||
|
if !ok {
|
||||||
|
return -1, netdev.ErrInvalidSocketFd
|
||||||
|
}
|
||||||
|
|
||||||
// Limit max read size to chunk large read requests
|
// Limit max read size to chunk large read requests
|
||||||
var max = len(buf)
|
var max = len(buf)
|
||||||
@@ -890,22 +1027,22 @@ func (w *wifinina) Recv(sockfd int, buf []byte, flags int,
|
|||||||
// doesn't return unless there is data, even a single byte, or
|
// doesn't return unless there is data, even a single byte, or
|
||||||
// on error such as timeout or EOF.
|
// on error such as timeout or EOF.
|
||||||
|
|
||||||
n := int(w.getDataBuf(sock, buf[:max]))
|
n := int(w.getDataBuf(socket.sock, buf[:max]))
|
||||||
if n > 0 {
|
if n > 0 {
|
||||||
if debugging(debugNetdev) {
|
if debugging(debugNetdev) {
|
||||||
fmt.Printf("[<--Recv] sockfd: %d, n: %d\r\n",
|
fmt.Printf("[<--Recv] sockfd: %d, n: %d\r\n",
|
||||||
sock, n)
|
sockfd, n)
|
||||||
}
|
}
|
||||||
return n, nil
|
return n, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if socket went down
|
// Check if socket went down
|
||||||
if w.sockDown(sock) {
|
if w.sockDown(socket) {
|
||||||
// Get any last bytes
|
// Get any last bytes
|
||||||
n = int(w.getDataBuf(sock, buf[:max]))
|
n = int(w.getDataBuf(socket.sock, buf[:max]))
|
||||||
if debugging(debugNetdev) {
|
if debugging(debugNetdev) {
|
||||||
fmt.Printf("[<--Recv] sockfd: %d, n: %d, EOF\r\n",
|
fmt.Printf("[<--Recv] sockfd: %d, n: %d, EOF\r\n",
|
||||||
sock, n)
|
sockfd, n)
|
||||||
}
|
}
|
||||||
if n > 0 {
|
if n > 0 {
|
||||||
return n, io.EOF
|
return n, io.EOF
|
||||||
@@ -934,34 +1071,18 @@ func (w *wifinina) Close(sockfd int) error {
|
|||||||
w.mu.Lock()
|
w.mu.Lock()
|
||||||
defer w.mu.Unlock()
|
defer w.mu.Unlock()
|
||||||
|
|
||||||
var sock = sock(sockfd)
|
socket, ok := w.sockets[sockfd]
|
||||||
var socket = w.sockets[sock]
|
if !ok {
|
||||||
|
return netdev.ErrInvalidSocketFd
|
||||||
if !socket.inuse {
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
w.stopClient(sock)
|
if socket.clientConnected {
|
||||||
|
w.stopClient(socket.sock)
|
||||||
if socket.protocol == netdev.IPPROTO_UDP {
|
|
||||||
socket.inuse = false
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
start := time.Now()
|
delete(w.sockets, sockfd)
|
||||||
for time.Since(start) < 5*time.Second {
|
|
||||||
|
|
||||||
if w.getClientState(sock) == tcpStateClosed {
|
return nil
|
||||||
socket.inuse = false
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
w.mu.Unlock()
|
|
||||||
time.Sleep(100 * time.Millisecond)
|
|
||||||
w.mu.Lock()
|
|
||||||
}
|
|
||||||
|
|
||||||
return netdev.ErrClosingSocket
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *wifinina) SetSockOpt(sockfd int, level int, opt int, value interface{}) error {
|
func (w *wifinina) SetSockOpt(sockfd int, level int, opt int, value interface{}) error {
|
||||||
@@ -1123,6 +1244,23 @@ func (w *wifinina) accept(s sock) sock {
|
|||||||
return newsock
|
return newsock
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w *wifinina) getRemoteData(s sock) netip.AddrPort {
|
||||||
|
|
||||||
|
if debugging(debugCmd) {
|
||||||
|
fmt.Printf(" [cmdGetRemoteData] sock: %d\r\n", s)
|
||||||
|
}
|
||||||
|
|
||||||
|
sl := make([]string, 2)
|
||||||
|
l := w.reqRspStr1(cmdGetRemoteData, uint8(s), sl)
|
||||||
|
if l != 2 {
|
||||||
|
w.faultf("getRemoteData wanted l=2, got l=%d", l)
|
||||||
|
return netip.AddrPort{}
|
||||||
|
}
|
||||||
|
ip, _ := netip.AddrFromSlice([]byte(sl[0])[:4])
|
||||||
|
port := binary.BigEndian.Uint16([]byte(sl[1]))
|
||||||
|
return netip.AddrPortFrom(ip, port)
|
||||||
|
}
|
||||||
|
|
||||||
// insertDataBuf adds data to the buffer used for sending UDP data
|
// insertDataBuf adds data to the buffer used for sending UDP data
|
||||||
func (w *wifinina) insertDataBuf(sock sock, buf []byte) bool {
|
func (w *wifinina) insertDataBuf(sock sock, buf []byte) bool {
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user