mirror of
https://github.com/tinygo-org/drivers.git
synced 2026-08-02 05:57:47 +00:00
Compare commits
49 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 90a13a697b | |||
| d632eb888f | |||
| f6761a9079 | |||
| 51c3aa271b | |||
| 520234e9f8 | |||
| 334ed57373 | |||
| 3637033fec | |||
| 233866443c | |||
| 65fd7aa1a1 | |||
| 82e9e4a7f3 | |||
| 76ff632134 | |||
| cd2694e85f | |||
| 734adb6df8 | |||
| 2492fcb322 | |||
| 4f82c231cb | |||
| 2c9a05a413 | |||
| a89a26112e | |||
| bd42656644 | |||
| 94729a4f7d | |||
| 050cf4dbbc | |||
| 49c8810432 | |||
| 4b38841f71 | |||
| 3fe3fc64e4 | |||
| 0565a15b17 | |||
| fad9d12e0b | |||
| f2e5278965 | |||
| 259593e608 | |||
| 7a2b1b8c90 | |||
| cad03a7d4e | |||
| 98f5396625 | |||
| 3b16972ed8 | |||
| b35496dd1b | |||
| ad6be40966 | |||
| b3af3f594e | |||
| 9a3bfd4826 | |||
| 1ba9ba3143 | |||
| feb1c63656 | |||
| 0691ed5205 | |||
| e70beede1c | |||
| 92673d996d | |||
| 374e2dfe1d | |||
| b35aab6d61 | |||
| 358501fc47 | |||
| a0119ffc6f | |||
| 58106b5942 | |||
| 2537c69b0f | |||
| 633f19c4ac | |||
| 6a4cd9d8e7 | |||
| 8a4a0ff017 |
@@ -1,3 +1,35 @@
|
||||
0.18.0
|
||||
---
|
||||
- **new devices**
|
||||
- apds9960: add support for APDS-9960 Digital Proximity sensor
|
||||
- axp192: add support for AXP192 single Cell Li-Battery and power system management IC
|
||||
- hts221: add support for HTS221 capacitive digital sensor for relative humidity and temperature
|
||||
- i2csoft: add support for software I2C
|
||||
- image: add support for image/jpeg and image/png
|
||||
- lps22hb: add support for LPS22HB MEMS nano pressure sensor
|
||||
- lsm6dox: add support for lsm6dox accelerometer
|
||||
- lsm9ds1: add support for lsm9ds1 accelerometer
|
||||
- **enhancements**
|
||||
- ili9341: change to use drivers.SPI interface
|
||||
- **ws2812**
|
||||
- generate assembly instead of handwriting it
|
||||
- improve timings to be compatible with the WS2811
|
||||
- add support for 168MHz (e.g. Adafruit Feather STM32F405)
|
||||
- add support for RISC-V
|
||||
- wifinina: control nina pins, for example leds
|
||||
- **docs**
|
||||
- rtl8720dn: examples for tcpclient, udpstation, mqtt, and webserver
|
||||
- **wifinina**
|
||||
- nina-fw update docs
|
||||
- examples/wifinina/http-get
|
||||
- ili9341: refactor examples
|
||||
- Fix broken link for SHT3x datasheet
|
||||
- **core**
|
||||
- all: use build directives for both Go1.17 and earlier versions
|
||||
- **bugfixes**
|
||||
- net: fix raddr of tcp conn
|
||||
- mcp3008: fix bitshift bug
|
||||
|
||||
0.17.1
|
||||
---
|
||||
- To correct an error in the release process. Same as 0.17.0.
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
clean:
|
||||
@rm -rf build
|
||||
|
||||
FMT_PATHS = ./*.go ./examples/**/*.go
|
||||
FMT_PATHS = ./
|
||||
|
||||
fmt-check:
|
||||
@unformatted=$$(gofmt -l $(FMT_PATHS)); [ -z "$$unformatted" ] && exit 0; echo "Unformatted:"; for fn in $$unformatted; do echo " $$fn"; done; exit 1
|
||||
@@ -17,6 +17,8 @@ smoke-test:
|
||||
@md5sum ./build/test.hex
|
||||
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/apa102/main.go
|
||||
@md5sum ./build/test.hex
|
||||
tinygo build -size short -o ./build/test.hex -target=nano-33-ble ./examples/apds9960/proximity/main.go
|
||||
@md5sum ./build/test.hex
|
||||
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/apa102/itsybitsy-m0/main.go
|
||||
@md5sum ./build/test.hex
|
||||
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/at24cx/main.go
|
||||
@@ -63,6 +65,8 @@ smoke-test:
|
||||
@md5sum ./build/test.hex
|
||||
tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/hd44780i2c/main.go
|
||||
@md5sum ./build/test.hex
|
||||
tinygo build -size short -o ./build/test.hex -target=nano-33-ble ./examples/hts221/main.go
|
||||
@md5sum ./build/test.hex
|
||||
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/hub75/main.go
|
||||
@md5sum ./build/test.hex
|
||||
tinygo build -size short -o ./build/test.hex -target=pyportal ./examples/ili9341/basic
|
||||
@@ -75,8 +79,12 @@ smoke-test:
|
||||
@md5sum ./build/test.hex
|
||||
tinygo build -size short -o ./build/test.hex -target=xiao ./examples/ili9341/scroll
|
||||
@md5sum ./build/test.hex
|
||||
tinygo build -size short -o ./build/test.hex -target=pyportal ./examples/ili9341/slideshow
|
||||
@md5sum ./build/test.hex
|
||||
tinygo build -size short -o ./build/test.hex -target=circuitplay-express ./examples/lis3dh/main.go
|
||||
@md5sum ./build/test.hex
|
||||
tinygo build -size short -o ./build/test.hex -target=nano-33-ble ./examples/lps22hb/main.go
|
||||
@md5sum ./build/test.hex
|
||||
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/lsm303agr/main.go
|
||||
@md5sum ./build/test.hex
|
||||
tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/lsm6ds3/main.go
|
||||
@@ -199,12 +207,20 @@ endif
|
||||
@md5sum ./build/test.hex
|
||||
tinygo build -size short -o ./build/test.hex -target=wioterminal ./examples/rtl8720dn/webclient/
|
||||
@md5sum ./build/test.hex
|
||||
tinygo build -size short -o ./build/test.hex -target=wioterminal ./examples/rtl8720dn/webserver/
|
||||
@md5sum ./build/test.hex
|
||||
tinygo build -size short -o ./build/test.hex -target=wioterminal ./examples/rtl8720dn/mqttsub/
|
||||
@md5sum ./build/test.hex
|
||||
tinygo build -size short -o ./build/test.hex -target=feather-m4 ./examples/i2csoft/adt7410/
|
||||
@md5sum ./build/test.hex
|
||||
tinygo build -size short -o ./build/test.elf -target=wioterminal ./examples/axp192/m5stack-core2-blinky/
|
||||
@md5sum ./build/test.hex
|
||||
|
||||
DRIVERS = $(wildcard */)
|
||||
NOTESTS = build examples flash semihosting pcd8544 shiftregister st7789 microphone mcp3008 gps microbitmatrix \
|
||||
hcsr04 ssd1331 ws2812 thermistor apa102 easystepper ssd1351 ili9341 wifinina shifter hub75 \
|
||||
hd44780 buzzer ssd1306 espat l9110x st7735 bmi160 l293x dht keypad4x4 max72xx p1am tone tm1637 \
|
||||
pcf8563 mcp2515 servo sdcard rtl8720dn
|
||||
pcf8563 mcp2515 servo sdcard rtl8720dn image cmd i2csoft hts221 lps22hb apds9960 axp192
|
||||
TESTS = $(filter-out $(addsuffix /%,$(NOTESTS)),$(DRIVERS))
|
||||
|
||||
unit-test:
|
||||
|
||||
@@ -52,7 +52,7 @@ func main() {
|
||||
|
||||
## Currently supported devices
|
||||
|
||||
The following 67 devices are supported.
|
||||
The following 74 devices are supported.
|
||||
|
||||
| Device Name | Interface Type |
|
||||
|----------|-------------|
|
||||
@@ -61,7 +61,9 @@ The following 67 devices are supported.
|
||||
| [AHT20 I2C Temperature and Humidity Sensor](http://www.aosong.com/userfiles/files/media/AHT20%20%E8%8B%B1%E6%96%87%E7%89%88%E8%AF%B4%E6%98%8E%E4%B9%A6%20A0%2020201222.pdf) | I2C |
|
||||
| [AMG88xx 8x8 Thermal camera sensor](https://cdn-learn.adafruit.com/assets/assets/000/043/261/original/Grid-EYE_SPECIFICATIONS%28Reference%29.pdf) | I2C |
|
||||
| [APA102 RGB LED](https://cdn-shop.adafruit.com/product-files/2343/APA102C.pdf) | SPI |
|
||||
| [APDS9960 Digital proximity, ambient light, RGB and gesture sensor](https://cdn.sparkfun.com/assets/learn_tutorials/3/2/1/Avago-APDS-9960-datasheet.pdf) | I2C |
|
||||
| [AT24CX 2-wire serial EEPROM](https://www.openimpulse.com/blog/wp-content/uploads/wpsc/downloadables/24C32-Datasheet.pdf) | I2C |
|
||||
| [AXP192 single Cell Li-Battery and Power System Management](https://github.com/m5stack/M5-Schematic/blob/master/Core/AXP192%20Datasheet_v1.1_en_draft_2211.pdf) | I2C |
|
||||
| [BBC micro:bit LED matrix](https://github.com/bbcmicrobit/hardware/blob/master/SCH_BBC-Microbit_V1.3B.pdf) | GPIO |
|
||||
| [BH1750 ambient light sensor](https://www.mouser.com/ds/2/348/bh1750fvi-e-186247.pdf) | I2C |
|
||||
| [BlinkM RGB LED](http://thingm.com/fileadmin/thingm/downloads/BlinkM_datasheet.pdf) | I2C |
|
||||
@@ -79,7 +81,9 @@ The following 67 devices are supported.
|
||||
| [GPS module](https://www.u-blox.com/en/product/neo-6-series) | I2C/UART |
|
||||
| [HC-SR04 Ultrasonic distance sensor](https://cdn.sparkfun.com/datasheets/Sensors/Proximity/HCSR04.pdf) | GPIO |
|
||||
| [HD44780 LCD controller](https://www.sparkfun.com/datasheets/LCD/HD44780.pdf) | GPIO/I2C |
|
||||
| [HTS221 digital humidity and temperature sensor](https://www.st.com/resource/en/datasheet/hts221.pdf) | I2C |
|
||||
| [HUB75 RGB led matrix](https://cdn-learn.adafruit.com/downloads/pdf/32x16-32x32-rgb-led-matrix.pdf) | SPI |
|
||||
| [software I2C driver](https://www.ti.com/lit/an/slva704/slva704.pdf) | GPIO |
|
||||
| [ILI9341 TFT color display](https://cdn-shop.adafruit.com/datasheets/ILI9341.pdf) | SPI |
|
||||
| [INA260 Volt/Amp/Power meter](https://www.ti.com/lit/ds/symlink/ina260.pdf) | I2C |
|
||||
| [4x4 Membrane Keypad](https://cdn.sparkfun.com/assets/f/f/a/5/0/DS-16038.pdf) | GPIO |
|
||||
@@ -87,12 +91,16 @@ The following 67 devices are supported.
|
||||
| [L9110x motor driver](https://www.elecrow.com/download/datasheet-l9110.pdf) | GPIO/PWM |
|
||||
| [LIS2MDL magnetometer](https://www.st.com/resource/en/datasheet/lis2mdl.pdf) | I2C |
|
||||
| [LIS3DH accelerometer](https://www.st.com/resource/en/datasheet/lis3dh.pdf) | I2C |
|
||||
| [LPS22HB MEMS nano pressure sensor](https://www.st.com/resource/en/datasheet/dm00140895.pdf) | I2C |
|
||||
| [LSM6DS3 accelerometer](https://www.st.com/resource/en/datasheet/lsm6ds3.pdf) | I2C |
|
||||
| [LSM6DSOX accelerometer](https://www.st.com/resource/en/datasheet/lsm6dsox.pdf) | I2C |
|
||||
| [LSM303AGR accelerometer](https://www.st.com/resource/en/datasheet/lsm303agr.pdf) | I2C |
|
||||
| [LSM9DS1 accelerometer](https://www.st.com/resource/en/datasheet/lsm9ds1.pdf) | I2C |
|
||||
| [MAG3110 magnetometer](https://www.nxp.com/docs/en/data-sheet/MAG3110.pdf) | I2C |
|
||||
| [MAX7219 & MAX7221 display driver](https://datasheets.maximintegrated.com/en/ds/MAX7219-MAX7221.pdf) | SPI |
|
||||
| [MCP23017 port expander](https://ww1.microchip.com/downloads/en/DeviceDoc/20001952C.pdf) | I2C |
|
||||
| [MCP3008 analog to digital converter (ADC)](http://ww1.microchip.com/downloads/en/DeviceDoc/21295d.pdf) | SPI |
|
||||
| [MCP2515 Stand-Alone CAN Controller with SPI Interface](https://ww1.microchip.com/downloads/en/DeviceDoc/MCP2515-Family-Data-Sheet-DS20001801K.pdf) | SPI |
|
||||
| [MCP3008 analog to digital converter (ADC)](http://ww1.microchip.com/downloads/en/DeviceDoc/21295d.pdf) | SPI |
|
||||
| [MCP23017 port expander](https://ww1.microchip.com/downloads/en/DeviceDoc/20001952C.pdf) | I2C |
|
||||
| [Microphone - PDM](https://cdn-learn.adafruit.com/assets/assets/000/049/977/original/MP34DT01-M.pdf) | I2S/PDM |
|
||||
| [MMA8653 accelerometer](https://www.nxp.com/docs/en/data-sheet/MMA8653FC.pdf) | I2C |
|
||||
| [MPU6050 accelerometer/gyroscope](https://store.invensense.com/datasheets/invensense/MPU-6050_DataSheet_V3%204.pdf) | I2C |
|
||||
@@ -105,7 +113,7 @@ The following 67 devices are supported.
|
||||
| [Servo](https://learn.sparkfun.com/tutorials/hobby-servo-tutorial/all) | PWM |
|
||||
| [Shift register (PISO)](https://en.wikipedia.org/wiki/Shift_register#Parallel-in_serial-out_\(PISO\)) | GPIO |
|
||||
| [Shift registers (SIPO)](https://en.wikipedia.org/wiki/Shift_register#Serial-in_parallel-out_(SIPO)) | GPIO |
|
||||
| [SHT3x Digital Humidity Sensor](https://www.sensirion.com/fileadmin/user_upload/customers/sensirion/Dokumente/0_Datasheets/Humidity/Sensirion_Humidity_Sensors_SHT3x_Datasheet_digital.pdf) | I2C |
|
||||
| [SHT3x Digital Humidity Sensor](https://www.sensirion.com/fileadmin/user_upload/customers/sensirion/Dokumente/2_Humidity_Sensors/Datasheets/Sensirion_Humidity_Sensors_SHT3x_Datasheet_digital.pdf) | I2C |
|
||||
| [SPI NOR Flash Memory](https://en.wikipedia.org/wiki/Flash_memory#NOR_flash) | SPI/QSPI |
|
||||
| [SPI SDCARD/MMC](https://en.wikipedia.org/wiki/SD_card) | SPI |
|
||||
| [SSD1306 OLED display](https://cdn-shop.adafruit.com/datasheets/SSD1306.pdf) | I2C / SPI |
|
||||
|
||||
@@ -0,0 +1,461 @@
|
||||
// Package apds9960 implements a driver for APDS-9960,
|
||||
// a digital proximity, ambient light, RGB and gesture sensor.
|
||||
//
|
||||
// Datasheet: https://cdn.sparkfun.com/assets/learn_tutorials/3/2/1/Avago-APDS-9960-datasheet.pdf
|
||||
//
|
||||
package apds9960
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
)
|
||||
|
||||
// Device wraps an I2C connection to a APDS-9960 device.
|
||||
type Device struct {
|
||||
bus drivers.I2C
|
||||
Address uint8
|
||||
mode uint8
|
||||
gesture gestureData
|
||||
}
|
||||
|
||||
// Configuration for APDS-9960 device.
|
||||
type Configuration struct {
|
||||
ProximityPulseLength uint8
|
||||
ProximityPulseCount uint8
|
||||
GesturePulseLength uint8
|
||||
GesturePulseCount uint8
|
||||
ProximityGain uint8
|
||||
GestureGain uint8
|
||||
ColorGain uint8
|
||||
ADCIntegrationCycles uint16
|
||||
LEDBoost uint16
|
||||
threshold uint8
|
||||
sensitivity uint8
|
||||
}
|
||||
|
||||
// for gesture-related data
|
||||
type gestureData struct {
|
||||
detected uint8
|
||||
threshold uint8
|
||||
sensitivity uint8
|
||||
gXDelta int16
|
||||
gYDelta int16
|
||||
gXPrevDelta int16
|
||||
gYPrevDelta int16
|
||||
received bool
|
||||
}
|
||||
|
||||
// for enabling various device function
|
||||
type enableConfig struct {
|
||||
GEN bool
|
||||
PIEN bool
|
||||
AIEN bool
|
||||
WEN bool
|
||||
PEN bool
|
||||
AEN bool
|
||||
PON bool
|
||||
}
|
||||
|
||||
// Connected returns whether APDS-9960 has been found.
|
||||
// It does a "who am I" request and checks the response.
|
||||
func (d *Device) Connected() bool {
|
||||
data := []byte{0}
|
||||
d.bus.ReadRegister(d.Address, APDS9960_ID_REG, data)
|
||||
return data[0] == 0xAB
|
||||
}
|
||||
|
||||
// Configure sets up the APDS-9960 device.
|
||||
func (d *Device) Configure(cfg Configuration) {
|
||||
d.DisableAll() // turn off everything
|
||||
|
||||
// "default" settings
|
||||
if cfg.ProximityPulseLength == 0 {
|
||||
cfg.ProximityPulseLength = 16
|
||||
}
|
||||
if cfg.ProximityPulseCount == 0 {
|
||||
cfg.ProximityPulseCount = 64
|
||||
}
|
||||
if cfg.GesturePulseLength == 0 {
|
||||
cfg.GesturePulseLength = 16
|
||||
}
|
||||
if cfg.GesturePulseCount == 0 {
|
||||
cfg.GesturePulseCount = 64
|
||||
}
|
||||
if cfg.ProximityGain == 0 {
|
||||
cfg.ProximityGain = 1
|
||||
}
|
||||
if cfg.GestureGain == 0 {
|
||||
cfg.GestureGain = 1
|
||||
}
|
||||
if cfg.ColorGain == 0 {
|
||||
cfg.ColorGain = 4
|
||||
}
|
||||
if cfg.ADCIntegrationCycles == 0 {
|
||||
cfg.ADCIntegrationCycles = 4
|
||||
}
|
||||
if cfg.threshold == 0 {
|
||||
d.gesture.threshold = 30
|
||||
}
|
||||
if cfg.sensitivity == 0 {
|
||||
d.gesture.sensitivity = 20
|
||||
}
|
||||
|
||||
d.SetProximityPulse(cfg.ProximityPulseLength, cfg.ProximityPulseCount)
|
||||
d.SetGesturePulse(cfg.GesturePulseLength, cfg.GesturePulseCount)
|
||||
d.SetGains(cfg.ProximityGain, cfg.GestureGain, cfg.ColorGain)
|
||||
d.SetADCIntegrationCycles(cfg.ADCIntegrationCycles)
|
||||
|
||||
if cfg.LEDBoost > 0 {
|
||||
d.LEDBoost(cfg.LEDBoost)
|
||||
}
|
||||
}
|
||||
|
||||
// GetMode returns current engine mode
|
||||
func (d *Device) GetMode() uint8 {
|
||||
return d.mode
|
||||
}
|
||||
|
||||
// DisableAll turns off the device and all functions
|
||||
func (d *Device) DisableAll() {
|
||||
d.enable(enableConfig{})
|
||||
d.bus.WriteRegister(d.Address, APDS9960_GCONF4_REG, []byte{0x00})
|
||||
d.mode = MODE_NONE
|
||||
d.gesture.detected = GESTURE_NONE
|
||||
}
|
||||
|
||||
// SetProximityPulse sets proximity pulse length (4, 8, 16, 32) and count (1~64)
|
||||
// default: 16, 64
|
||||
func (d *Device) SetProximityPulse(length, count uint8) {
|
||||
d.bus.WriteRegister(d.Address, APDS9960_PPULSE_REG, []byte{getPulseLength(length)<<6 | getPulseCount(count)})
|
||||
}
|
||||
|
||||
// SetGesturePulse sets gesture pulse length (4, 8, 16, 32) and count (1~64)
|
||||
// default: 16, 64
|
||||
func (d *Device) SetGesturePulse(length, count uint8) {
|
||||
d.bus.WriteRegister(d.Address, APDS9960_GPULSE_REG, []byte{getPulseLength(length)<<6 | getPulseCount(count)})
|
||||
}
|
||||
|
||||
// SetADCIntegrationCycles sets ALS/color ADC internal integration cycles (1~256, 1 cycle = 2.78 ms)
|
||||
// default: 4 (~10 ms)
|
||||
func (d *Device) SetADCIntegrationCycles(cycles uint16) {
|
||||
if cycles > 256 {
|
||||
cycles = 256
|
||||
}
|
||||
d.bus.WriteRegister(d.Address, APDS9960_ATIME_REG, []byte{uint8(256 - cycles)})
|
||||
}
|
||||
|
||||
// SetGains sets proximity/gesture gain (1, 2, 4, 8x) and ALS/color gain (1, 4, 16, 64x)
|
||||
// default: 1, 1, 4
|
||||
func (d *Device) SetGains(proximityGain, gestureGain, colorGain uint8) {
|
||||
d.bus.WriteRegister(d.Address, APDS9960_CONTROL_REG, []byte{getProximityGain(proximityGain)<<2 | getALSGain(colorGain)})
|
||||
d.bus.WriteRegister(d.Address, APDS9960_GCONF2_REG, []byte{getProximityGain(gestureGain) << 5})
|
||||
}
|
||||
|
||||
// LEDBoost sets proximity and gesture LED current level (100, 150, 200, 300 (%))
|
||||
// default: 100
|
||||
func (d *Device) LEDBoost(percent uint16) {
|
||||
var v uint8
|
||||
switch percent {
|
||||
case 100:
|
||||
v = 0
|
||||
case 150:
|
||||
v = 1
|
||||
case 200:
|
||||
v = 2
|
||||
case 300:
|
||||
v = 3
|
||||
}
|
||||
d.bus.WriteRegister(d.Address, APDS9960_CONFIG2_REG, []byte{0x01 | v<<4})
|
||||
}
|
||||
|
||||
// Setthreshold sets threshold (0~255) for detecting gestures
|
||||
// default: 30
|
||||
func (d *Device) Setthreshold(t uint8) {
|
||||
d.gesture.threshold = t
|
||||
}
|
||||
|
||||
// Setsensitivity sets sensivity (0~100) for detecting gestures
|
||||
// default: 20
|
||||
func (d *Device) Setsensitivity(s uint8) {
|
||||
if s > 100 {
|
||||
s = 100
|
||||
}
|
||||
d.gesture.sensitivity = 100 - s
|
||||
}
|
||||
|
||||
// EnableProximity starts the proximity engine
|
||||
func (d *Device) EnableProximity() {
|
||||
if d.mode != MODE_NONE {
|
||||
d.DisableAll()
|
||||
}
|
||||
d.enable(enableConfig{PON: true, PEN: true, WEN: true})
|
||||
d.mode = MODE_PROXIMITY
|
||||
}
|
||||
|
||||
// ProximityAvailable reports if proximity data is available
|
||||
func (d *Device) ProximityAvailable() bool {
|
||||
if d.mode == MODE_PROXIMITY && d.readStatus("PVALID") {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ReadProximity reads proximity data (0~255)
|
||||
func (d *Device) ReadProximity() (proximity int32) {
|
||||
if d.mode != MODE_PROXIMITY {
|
||||
return 0
|
||||
}
|
||||
data := []byte{0}
|
||||
d.bus.ReadRegister(d.Address, APDS9960_PDATA_REG, data)
|
||||
return 255 - int32(data[0])
|
||||
}
|
||||
|
||||
// EnableColor starts the color engine
|
||||
func (d *Device) EnableColor() {
|
||||
if d.mode != MODE_NONE {
|
||||
d.DisableAll()
|
||||
}
|
||||
d.enable(enableConfig{PON: true, AEN: true, WEN: true})
|
||||
d.mode = MODE_COLOR
|
||||
}
|
||||
|
||||
// ColorAvailable reports if color data is available
|
||||
func (d *Device) ColorAvailable() bool {
|
||||
if d.mode == MODE_COLOR && d.readStatus("AVALID") {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ReadColor reads color data (red, green, blue, clear color/brightness)
|
||||
func (d *Device) ReadColor() (r int32, g int32, b int32, clear int32) {
|
||||
if d.mode != MODE_COLOR {
|
||||
return
|
||||
}
|
||||
data := []byte{0, 0, 0, 0, 0, 0, 0, 0}
|
||||
d.bus.ReadRegister(d.Address, APDS9960_CDATAL_REG, data[:1])
|
||||
d.bus.ReadRegister(d.Address, APDS9960_CDATAH_REG, data[1:2])
|
||||
d.bus.ReadRegister(d.Address, APDS9960_RDATAL_REG, data[2:3])
|
||||
d.bus.ReadRegister(d.Address, APDS9960_RDATAH_REG, data[3:4])
|
||||
d.bus.ReadRegister(d.Address, APDS9960_GDATAL_REG, data[4:5])
|
||||
d.bus.ReadRegister(d.Address, APDS9960_GDATAH_REG, data[5:6])
|
||||
d.bus.ReadRegister(d.Address, APDS9960_BDATAL_REG, data[6:7])
|
||||
d.bus.ReadRegister(d.Address, APDS9960_BDATAH_REG, data[7:])
|
||||
clear = int32(uint16(data[1])<<8 | uint16(data[0]))
|
||||
r = int32(uint16(data[3])<<8 | uint16(data[2]))
|
||||
g = int32(uint16(data[5])<<8 | uint16(data[4]))
|
||||
b = int32(uint16(data[7])<<8 | uint16(data[6]))
|
||||
return
|
||||
}
|
||||
|
||||
// EnableGesture starts the gesture engine
|
||||
func (d *Device) EnableGesture() {
|
||||
if d.mode != MODE_NONE {
|
||||
d.DisableAll()
|
||||
}
|
||||
d.enable(enableConfig{PON: true, PEN: true, GEN: true, WEN: true})
|
||||
d.mode = MODE_GESTURE
|
||||
d.gesture.detected = GESTURE_NONE
|
||||
d.gesture.gXDelta = 0
|
||||
d.gesture.gYDelta = 0
|
||||
d.gesture.gXPrevDelta = 0
|
||||
d.gesture.gYPrevDelta = 0
|
||||
d.gesture.received = false
|
||||
}
|
||||
|
||||
// GestureAvailable reports if gesture data is available
|
||||
func (d *Device) GestureAvailable() bool {
|
||||
if d.mode != MODE_GESTURE {
|
||||
return false
|
||||
}
|
||||
|
||||
data := []byte{0, 0, 0, 0}
|
||||
|
||||
// check GVALID
|
||||
d.bus.ReadRegister(d.Address, APDS9960_GSTATUS_REG, data[:1])
|
||||
if data[0]&0x01 == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
// get number of data sets available in FIFO
|
||||
d.bus.ReadRegister(d.Address, APDS9960_GFLVL_REG, data[:1])
|
||||
availableDataSets := data[0]
|
||||
if availableDataSets == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
// read up, down, left and right proximity data from FIFO
|
||||
var dataSets [32][4]uint8
|
||||
for i := uint8(0); i < availableDataSets; i++ {
|
||||
d.bus.ReadRegister(d.Address, APDS9960_GFIFO_U_REG, data[:1])
|
||||
d.bus.ReadRegister(d.Address, APDS9960_GFIFO_D_REG, data[1:2])
|
||||
d.bus.ReadRegister(d.Address, APDS9960_GFIFO_L_REG, data[2:3])
|
||||
d.bus.ReadRegister(d.Address, APDS9960_GFIFO_R_REG, data[3:4])
|
||||
for j := uint8(0); j < 4; j++ {
|
||||
dataSets[i][j] = data[j]
|
||||
}
|
||||
}
|
||||
|
||||
// gesture detection process
|
||||
d.gesture.detected = GESTURE_NONE
|
||||
for i := uint8(0); i < availableDataSets; i++ {
|
||||
U := dataSets[i][0]
|
||||
D := dataSets[i][1]
|
||||
L := dataSets[i][2]
|
||||
R := dataSets[i][3]
|
||||
|
||||
// if all readings fall below threshold, it's possible that
|
||||
// a movement's just been made
|
||||
if U < d.gesture.threshold && D < d.gesture.threshold && L < d.gesture.threshold && R < d.gesture.threshold {
|
||||
d.gesture.received = true
|
||||
// if there were movement in the previous step (including the last data sets)
|
||||
if d.gesture.gXPrevDelta != 0 && d.gesture.gYPrevDelta != 0 {
|
||||
totalX := d.gesture.gXPrevDelta - d.gesture.gXDelta
|
||||
totalY := d.gesture.gYPrevDelta - d.gesture.gYDelta
|
||||
// if previous and current movement are in opposite directions (pass through one led then next)
|
||||
// and the difference is big enough, the gesture is recorded
|
||||
switch {
|
||||
case totalX < -int16(d.gesture.sensitivity):
|
||||
d.gesture.detected = GESTURE_LEFT
|
||||
case totalX > int16(d.gesture.sensitivity):
|
||||
d.gesture.detected = GESTURE_RIGHT
|
||||
case totalY > int16(d.gesture.sensitivity):
|
||||
d.gesture.detected = GESTURE_DOWN
|
||||
case totalY < -int16(d.gesture.sensitivity):
|
||||
d.gesture.detected = GESTURE_UP
|
||||
}
|
||||
d.gesture.gXDelta = 0
|
||||
d.gesture.gYDelta = 0
|
||||
d.gesture.gXPrevDelta = 0
|
||||
d.gesture.gYPrevDelta = 0
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// recording current movement
|
||||
d.gesture.gXDelta = int16(R) - int16(L)
|
||||
d.gesture.gYDelta = int16(D) - int16(U)
|
||||
if d.gesture.received {
|
||||
d.gesture.received = false
|
||||
d.gesture.gXPrevDelta = d.gesture.gXDelta
|
||||
d.gesture.gYPrevDelta = d.gesture.gYDelta
|
||||
}
|
||||
}
|
||||
|
||||
return d.gesture.detected != GESTURE_NONE
|
||||
}
|
||||
|
||||
// ReadGesture reads last gesture data
|
||||
func (d *Device) ReadGesture() (gesture int32) {
|
||||
return int32(d.gesture.detected)
|
||||
}
|
||||
|
||||
// private functions
|
||||
|
||||
func (d *Device) enable(cfg enableConfig) {
|
||||
var gen, pien, aien, wen, pen, aen, pon uint8
|
||||
|
||||
if cfg.GEN {
|
||||
gen = 1
|
||||
}
|
||||
if cfg.PIEN {
|
||||
pien = 1
|
||||
}
|
||||
if cfg.AIEN {
|
||||
aien = 1
|
||||
}
|
||||
if cfg.WEN {
|
||||
wen = 1
|
||||
}
|
||||
if cfg.PEN {
|
||||
pen = 1
|
||||
}
|
||||
if cfg.AEN {
|
||||
aen = 1
|
||||
}
|
||||
if cfg.PON {
|
||||
pon = 1
|
||||
}
|
||||
|
||||
data := []byte{gen<<6 | pien<<5 | aien<<4 | wen<<3 | pen<<2 | aen<<1 | pon}
|
||||
d.bus.WriteRegister(d.Address, APDS9960_ENABLE_REG, data)
|
||||
|
||||
if cfg.PON {
|
||||
time.Sleep(time.Millisecond * 10)
|
||||
}
|
||||
}
|
||||
|
||||
func (d *Device) readStatus(param string) bool {
|
||||
data := []byte{0}
|
||||
d.bus.ReadRegister(d.Address, APDS9960_STATUS_REG, data)
|
||||
|
||||
switch param {
|
||||
case "CPSAT":
|
||||
return data[0]>>7&0x01 == 1
|
||||
case "PGSAT":
|
||||
return data[0]>>6&0x01 == 1
|
||||
case "PINT":
|
||||
return data[0]>>5&0x01 == 1
|
||||
case "AINT":
|
||||
return data[0]>>4&0x01 == 1
|
||||
case "PVALID":
|
||||
return data[0]>>1&0x01 == 1
|
||||
case "AVALID":
|
||||
return data[0]&0x01 == 1
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func getPulseLength(l uint8) uint8 {
|
||||
switch l {
|
||||
case 4:
|
||||
return 0
|
||||
case 8:
|
||||
return 1
|
||||
case 16:
|
||||
return 2
|
||||
case 32:
|
||||
return 3
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
func getPulseCount(c uint8) uint8 {
|
||||
if c < 1 && c > 64 {
|
||||
return 0
|
||||
}
|
||||
return c - 1
|
||||
}
|
||||
|
||||
func getProximityGain(g uint8) uint8 {
|
||||
switch g {
|
||||
case 1:
|
||||
return 0
|
||||
case 2:
|
||||
return 1
|
||||
case 4:
|
||||
return 2
|
||||
case 8:
|
||||
return 3
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
func getALSGain(g uint8) uint8 {
|
||||
switch g {
|
||||
case 1:
|
||||
return 0
|
||||
case 4:
|
||||
return 1
|
||||
case 16:
|
||||
return 2
|
||||
case 64:
|
||||
return 3
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
//go:build !nano_33_ble
|
||||
// +build !nano_33_ble
|
||||
|
||||
package apds9960
|
||||
|
||||
import "tinygo.org/x/drivers"
|
||||
|
||||
// New creates a new APDS-9960 connection. The I2C bus must already be
|
||||
// configured.
|
||||
//
|
||||
// This function only creates the Device object, it does not touch the device.
|
||||
func New(bus drivers.I2C) Device {
|
||||
return Device{bus: bus, Address: ADPS9960_ADDRESS, mode: MODE_NONE}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
//go:build nano_33_ble
|
||||
// +build nano_33_ble
|
||||
|
||||
package apds9960
|
||||
|
||||
import (
|
||||
"machine"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
)
|
||||
|
||||
// New creates a new APDS-9960 connection. The I2C bus must already be
|
||||
// configured.
|
||||
//
|
||||
// This function only creates the Device object, it does not touch the device.
|
||||
func New(bus drivers.I2C) Device {
|
||||
// turn on internal power pin (machine.P0_22) and I2C1 pullups power pin (machine.P1_00)
|
||||
// and wait a moment.
|
||||
ENV := machine.P0_22
|
||||
ENV.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
ENV.High()
|
||||
R := machine.P1_00
|
||||
R.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
R.High()
|
||||
time.Sleep(time.Millisecond * 10)
|
||||
|
||||
return Device{bus: bus, Address: ADPS9960_ADDRESS, mode: MODE_NONE}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package apds9960
|
||||
|
||||
const (
|
||||
|
||||
// I2C address
|
||||
ADPS9960_ADDRESS = 0x39
|
||||
|
||||
// control/status registers
|
||||
APDS9960_RAM_REG = 0x00
|
||||
APDS9960_ENABLE_REG = 0x80
|
||||
APDS9960_ATIME_REG = 0x81
|
||||
APDS9960_WTIME_REG = 0x83
|
||||
APDS9960_AILTIL_REG = 0x84
|
||||
APDS9960_AILTH_REG = 0x85
|
||||
APDS9960_AIHTL_REG = 0x86
|
||||
APDS9960_AIHTH_REG = 0x87
|
||||
APDS9960_PILT_REG = 0x89
|
||||
APDS9960_PIHT_REG = 0x8B
|
||||
APDS9960_PERS_REG = 0x8C
|
||||
APDS9960_CONFIG1_REG = 0x8D
|
||||
APDS9960_PPULSE_REG = 0x8E
|
||||
APDS9960_CONTROL_REG = 0x8F
|
||||
APDS9960_CONFIG2_REG = 0x90
|
||||
APDS9960_ID_REG = 0x92
|
||||
APDS9960_STATUS_REG = 0x93
|
||||
APDS9960_CDATAL_REG = 0x94
|
||||
APDS9960_CDATAH_REG = 0x95
|
||||
APDS9960_RDATAL_REG = 0x96
|
||||
APDS9960_RDATAH_REG = 0x97
|
||||
APDS9960_GDATAL_REG = 0x98
|
||||
APDS9960_GDATAH_REG = 0x99
|
||||
APDS9960_BDATAL_REG = 0x9A
|
||||
APDS9960_BDATAH_REG = 0x9B
|
||||
APDS9960_PDATA_REG = 0x9C
|
||||
APDS9960_POFFSET_UR_REG = 0x9D
|
||||
APDS9960_POFFSET_DL_REG = 0x9E
|
||||
APDS9960_CONFIG3_REG = 0x9F
|
||||
APDS9960_GPENTH_REG = 0xA0
|
||||
APDS9960_GEXTH_REG = 0xA1
|
||||
APDS9960_GCONF1_REG = 0xA2
|
||||
APDS9960_GCONF2_REG = 0xA3
|
||||
APDS9960_GOFFSET_U_REG = 0xA4
|
||||
APDS9960_GOFFSET_D_REG = 0xA5
|
||||
APDS9960_GOFFSET_L_REG = 0xA7
|
||||
APDS9960_GOFFSET_R_REG = 0xA9
|
||||
APDS9960_GPULSE_REG = 0xA6
|
||||
APDS9960_GCONF3_REG = 0xAA
|
||||
APDS9960_GCONF4_REG = 0xAB
|
||||
APDS9960_GFLVL_REG = 0xAE
|
||||
APDS9960_GSTATUS_REG = 0xAF
|
||||
APDS9960_IFORCE_REG = 0xE4
|
||||
APDS9960_PICLEAR_REG = 0xE5
|
||||
APDS9960_CICLEAR_REG = 0xE6
|
||||
APDS9960_AICLEAR_REG = 0xE7
|
||||
APDS9960_GFIFO_U_REG = 0xFC
|
||||
APDS9960_GFIFO_D_REG = 0xFD
|
||||
APDS9960_GFIFO_L_REG = 0xFE
|
||||
APDS9960_GFIFO_R_REG = 0xFF
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
// sensor modes
|
||||
MODE_NONE = iota
|
||||
MODE_PROXIMITY
|
||||
MODE_COLOR
|
||||
MODE_GESTURE
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
// detected gestures
|
||||
GESTURE_NONE = iota
|
||||
GESTURE_UP
|
||||
GESTURE_DOWN
|
||||
GESTURE_LEFT
|
||||
GESTURE_RIGHT
|
||||
)
|
||||
@@ -0,0 +1,258 @@
|
||||
// Package axp192 provides a driver for the axp192 I2C Enhanced single Cell
|
||||
// Li-Battery and Power System Management IC.
|
||||
//
|
||||
// http://www.x-powers.com/en.php/Info/product_detail/article_id/29
|
||||
// Datasheet: https://github.com/m5stack/M5-Schematic/blob/master/Core/AXP192%20Datasheet_v1.1_en_draft_2211.pdf
|
||||
//
|
||||
package axp192 // import "tinygo.org/x/drivers/axp192"
|
||||
|
||||
import (
|
||||
"tinygo.org/x/drivers"
|
||||
)
|
||||
|
||||
type Error uint8
|
||||
|
||||
const (
|
||||
ErrInvalidID Error = 0x1
|
||||
)
|
||||
|
||||
func (e Error) Error() string {
|
||||
switch e {
|
||||
case ErrInvalidID:
|
||||
return "Invalid chip ID"
|
||||
default:
|
||||
return "Unknown error"
|
||||
}
|
||||
}
|
||||
|
||||
type Device struct {
|
||||
bus drivers.I2C
|
||||
buf []byte
|
||||
Address uint8
|
||||
}
|
||||
|
||||
// New returns AXP192 device for the provided I2C bus using default address.
|
||||
func New(i2c drivers.I2C) *Device {
|
||||
return &Device{
|
||||
bus: i2c,
|
||||
buf: make([]byte, 2),
|
||||
Address: Address,
|
||||
}
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
}
|
||||
|
||||
// Configure the AXP192 device.
|
||||
func (d *Device) Configure(config Config) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReadPowerSupplyStatus reads power supply status.
|
||||
func (d *Device) ReadPowerSupplyStatus() uint8 {
|
||||
return d.read8bit(RegPowerSupplyStatus)
|
||||
}
|
||||
|
||||
// SetVbusIPSOutAccessManagement sets VBUS-IPSOUT access management.
|
||||
func (d *Device) SetVbusIPSOutAccessManagement(a uint8) {
|
||||
d.write1Byte(RegVbusIPSOutAccessManagement, a)
|
||||
}
|
||||
|
||||
// GetVbusIPSOutAccessManagement gets VBUS-IPSOUT access management.
|
||||
func (d *Device) GetVbusIPSOutAccessManagement() uint8 {
|
||||
return d.read8bit(RegVbusIPSOutAccessManagement)
|
||||
}
|
||||
|
||||
// SetGPIO1Control sets GPIO1 function.
|
||||
func (d *Device) SetGPIO1Control(a uint8) {
|
||||
d.write1Byte(RegGPIO1Control, a)
|
||||
}
|
||||
|
||||
// GetGPIO1Control gets GPIO1 function.
|
||||
func (d *Device) GetGPIO1Control() uint8 {
|
||||
return d.read8bit(RegGPIO1Control)
|
||||
}
|
||||
|
||||
// SetGPIO2Control sets GPIO2 function.
|
||||
func (d *Device) SetGPIO2Control(a uint8) {
|
||||
d.write1Byte(RegGPIO2Control, a)
|
||||
}
|
||||
|
||||
// GetGPIO2Control gets GPIO2 function.
|
||||
func (d *Device) GetGPIO2Control() uint8 {
|
||||
return d.read8bit(RegGPIO2Control)
|
||||
}
|
||||
|
||||
// SetGPIO20SignalStatus sets GPIO[2:0] signal status.
|
||||
func (d *Device) SetGPIO20SignalStatus(a uint8) {
|
||||
d.write1Byte(RegGPIO20SignalStatus, a)
|
||||
}
|
||||
|
||||
// GetGPIO20SignalStatus gets GPIO[2:0] signal status.
|
||||
func (d *Device) GetGPIO20SignalStatus() uint8 {
|
||||
return d.read8bit(RegGPIO20SignalStatus)
|
||||
}
|
||||
|
||||
// SetBackupBatteryChargingControl sets backup battery charge control.
|
||||
func (d *Device) SetBackupBatteryChargingControl(a uint8) {
|
||||
d.write1Byte(RegBackupBatteryChargingControl, a)
|
||||
}
|
||||
|
||||
// GetBackupBatteryChargingControl gets backup battery charge control.
|
||||
func (d *Device) GetBackupBatteryChargingControl() uint8 {
|
||||
return d.read8bit(RegBackupBatteryChargingControl)
|
||||
}
|
||||
|
||||
// SetDCDC1VoltageSet sets DC-DC1 output voltage.
|
||||
func (d *Device) SetDCDC1VoltageSet(a uint8) {
|
||||
d.write1Byte(RegDCDC1VoltageSet, a)
|
||||
}
|
||||
|
||||
// GetDCDC1VoltageSet gets DC-DC1 output voltage.
|
||||
func (d *Device) GetDCDC1VoltageSet() uint8 {
|
||||
return d.read8bit(RegDCDC1VoltageSet)
|
||||
}
|
||||
|
||||
// SetDCDC2VoltageSet sets DC-DC2 dynamic voltage parameter.
|
||||
func (d *Device) SetDCDC2VoltageSet(a uint8) {
|
||||
d.write1Byte(RegDCDC2VoltageSet, a)
|
||||
}
|
||||
|
||||
// GetDCDC2VoltageSet gets DC-DC2 dynamic voltage parameter.
|
||||
func (d *Device) GetDCDC2VoltageSet() uint8 {
|
||||
return d.read8bit(RegDCDC2VoltageSet)
|
||||
}
|
||||
|
||||
// SetDCDC3VoltageSet sets DC-DC3 output voltage.
|
||||
func (d *Device) SetDCDC3VoltageSet(a uint8) {
|
||||
d.write1Byte(RegDCDC3VoltageSet, a)
|
||||
}
|
||||
|
||||
// GetDCDC3VoltageSet gets DC-DC3 output voltage.
|
||||
func (d *Device) GetDCDC3VoltageSet() uint8 {
|
||||
return d.read8bit(RegDCDC3VoltageSet)
|
||||
}
|
||||
|
||||
// SetLDO23VoltageSet sets LDO2/3 output voltage.
|
||||
func (d *Device) SetLDO23VoltageSet(a uint8) {
|
||||
d.write1Byte(RegLDO23VoltageSet, a)
|
||||
}
|
||||
|
||||
// GetLDO23VoltageSet gets LDO2/3 output voltage.
|
||||
func (d *Device) GetLDO23VoltageSet() uint8 {
|
||||
return d.read8bit(RegLDO23VoltageSet)
|
||||
}
|
||||
|
||||
// SetDCDC13LDO23Switch sets DC-DC1/3 & LOD2/3 output control.
|
||||
func (d *Device) SetDCDC13LDO23Switch(a uint8) {
|
||||
d.write1Byte(RegDCDC13LDO23Switch, a)
|
||||
}
|
||||
|
||||
// GetDCDC13LDO23Switch gets DC-DC1/3 & LOD2/3 output control.
|
||||
func (d *Device) GetDCDC13LDO23Switch() uint8 {
|
||||
return d.read8bit(RegDCDC13LDO23Switch)
|
||||
}
|
||||
|
||||
// SetGPIO43FunctionControl sets GPIO[4:3] pin function.
|
||||
func (d *Device) SetGPIO43FunctionControl(a uint8) {
|
||||
d.write1Byte(RegGPIO43FunctionControl, a)
|
||||
}
|
||||
|
||||
// GetGPIO43FunctionControl gets GPIO[4:3] pin function.
|
||||
func (d *Device) GetGPIO43FunctionControl() uint8 {
|
||||
return d.read8bit(RegGPIO43FunctionControl)
|
||||
}
|
||||
|
||||
// SetPEKParameterSet sets PEK press key parameter.
|
||||
func (d *Device) SetPEKParameterSet(a uint8) {
|
||||
d.write1Byte(RegPEKParameterSet, a)
|
||||
}
|
||||
|
||||
// GetPEKParameterSet gets PEK press key parameter.
|
||||
func (d *Device) GetPEKParameterSet() uint8 {
|
||||
return d.read8bit(RegPEKParameterSet)
|
||||
}
|
||||
|
||||
// SetADCEnableSet sets ADC enable 1.
|
||||
func (d *Device) SetADCEnableSet(a uint8) {
|
||||
d.write1Byte(RegADCEnableSet, a)
|
||||
}
|
||||
|
||||
// GetADCEnableSet gets ADC enable 1.
|
||||
func (d *Device) GetADCEnableSet() uint8 {
|
||||
return d.read8bit(RegADCEnableSet)
|
||||
}
|
||||
|
||||
// SetGPIO43SignalStatus sets GPIO[4:3] signal status.
|
||||
func (d *Device) SetGPIO43SignalStatus(a uint8) {
|
||||
d.write1Byte(RegGPIO43SignalStatus, a)
|
||||
}
|
||||
|
||||
// GetGPIO43SignalStatus gets GPIO[4:3] signal status.
|
||||
func (d *Device) GetGPIO43SignalStatus() uint8 {
|
||||
return d.read8bit(RegGPIO43SignalStatus)
|
||||
}
|
||||
|
||||
// SetDCVoltage sets DC voltage.
|
||||
func (d *Device) SetDCVoltage(number uint8, voltage uint16) {
|
||||
if voltage < 700 {
|
||||
voltage = 0
|
||||
} else {
|
||||
voltage = (voltage - 700) / 25
|
||||
}
|
||||
|
||||
switch number {
|
||||
case 0:
|
||||
v := d.GetDCDC1VoltageSet()
|
||||
d.SetDCDC1VoltageSet((v & 0x80) | (uint8(voltage) & 0x7F))
|
||||
case 1:
|
||||
v := d.GetDCDC2VoltageSet()
|
||||
d.SetDCDC2VoltageSet((v & 0x80) | (uint8(voltage) & 0x7F))
|
||||
case 2:
|
||||
v := d.GetDCDC3VoltageSet()
|
||||
d.SetDCDC3VoltageSet((v & 0x80) | (uint8(voltage) & 0x7F))
|
||||
}
|
||||
}
|
||||
|
||||
// SetLDOVoltage sets LDO voltage.
|
||||
func (d *Device) SetLDOVoltage(number uint8, voltage uint16) {
|
||||
if voltage > 3300 {
|
||||
voltage = 15
|
||||
} else {
|
||||
voltage = (voltage / 100) - 18
|
||||
}
|
||||
|
||||
switch number {
|
||||
case 2:
|
||||
v := d.GetLDO23VoltageSet()
|
||||
d.SetLDO23VoltageSet((v & 0x0F) | (uint8(voltage) << 4))
|
||||
break
|
||||
case 3:
|
||||
v := d.GetLDO23VoltageSet()
|
||||
d.SetLDO23VoltageSet((v & 0xF0) | uint8(voltage))
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// SetLDOEnable enable LDO.
|
||||
func (d *Device) SetLDOEnable(number uint8, state bool) {
|
||||
mark := uint8(0x01)
|
||||
mark <<= number
|
||||
switch number {
|
||||
case 2:
|
||||
v := d.GetDCDC13LDO23Switch()
|
||||
d.SetDCDC13LDO23Switch(v | mark)
|
||||
case 3:
|
||||
v := d.GetDCDC13LDO23Switch()
|
||||
d.SetDCDC13LDO23Switch(v & (^mark))
|
||||
}
|
||||
}
|
||||
|
||||
func (d *Device) write1Byte(reg, data uint8) {
|
||||
d.bus.WriteRegister(d.Address, reg, []byte{data})
|
||||
}
|
||||
|
||||
func (d *Device) read8bit(reg uint8) uint8 {
|
||||
d.bus.ReadRegister(d.Address, reg, d.buf[:1])
|
||||
return d.buf[0]
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
package axp192
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
axp192orig "tinygo.org/x/drivers/axp192"
|
||||
)
|
||||
|
||||
// Device wraps an I2C connection to a AXP192 device.
|
||||
type Device struct {
|
||||
*axp192orig.Device
|
||||
LED Pin
|
||||
RST Pin
|
||||
SPK_EN Pin
|
||||
}
|
||||
|
||||
// New creates a new AXP192 connection. The I2C bus must already be
|
||||
// configured.
|
||||
//
|
||||
// This function only creates the Device object, it does not touch the device.
|
||||
func New(i2c drivers.I2C) *Device {
|
||||
d := axp192orig.New(i2c)
|
||||
|
||||
axp := &Device{
|
||||
Device: d,
|
||||
}
|
||||
axp.LED = Pin{pin: 1, axp: axp}
|
||||
axp.SPK_EN = Pin{pin: 2, axp: axp}
|
||||
axp.RST = Pin{pin: 4, axp: axp}
|
||||
|
||||
axp.begin()
|
||||
|
||||
return axp
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
}
|
||||
|
||||
// Configure sets up the device for communication
|
||||
func (d *Device) Configure(config Config) error {
|
||||
return d.Device.Configure(axp192orig.Config{})
|
||||
}
|
||||
|
||||
func (d *Device) begin() {
|
||||
d.SetVbusIPSOutAccessManagement((d.GetVbusIPSOutAccessManagement() & 0x04) | 0x02)
|
||||
d.SetGPIO1Control(d.GetGPIO1Control() & 0xF8)
|
||||
d.SetGPIO2Control(d.GetGPIO2Control() & 0xF8)
|
||||
d.SetBackupBatteryChargingControl((d.GetBackupBatteryChargingControl() & 0x1C) | 0xA2)
|
||||
d.SetESPVoltage(3350)
|
||||
d.SetLcdVoltage(3300)
|
||||
d.SetLDOVoltage(2, 3300) //Periph power voltage preset (LCD_logic, SD card)
|
||||
d.SetLDOVoltage(3, 2000) //Vibrator power voltage preset
|
||||
|
||||
d.SetLDOEnable(2, true)
|
||||
d.SetDCDC3(true) // LCD Backlight
|
||||
// GPIO4 : LCD Reset
|
||||
d.SetGPIO43FunctionControl((d.GetGPIO43FunctionControl() & 0x72) | 0x84)
|
||||
// Power On/Off Setting
|
||||
d.SetPEKParameterSet(0x4C)
|
||||
d.SetADCEnableSet(0xFF)
|
||||
|
||||
d.RST.Low()
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
d.RST.High()
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
}
|
||||
|
||||
// ToggleLED toggles LED connected to AXP192.
|
||||
func (d *Device) ToggleLED() {
|
||||
v := d.GetGPIO20SignalStatus()
|
||||
if (v & 0x02) > 0 {
|
||||
d.SetGPIO20SignalStatus(v & 0xFD)
|
||||
} else {
|
||||
d.SetGPIO20SignalStatus(v | 0x02)
|
||||
}
|
||||
}
|
||||
|
||||
// SetESPVoltage sets voltage of ESP32.
|
||||
func (d *Device) SetESPVoltage(voltage uint16) {
|
||||
if voltage >= 3000 && voltage <= 3400 {
|
||||
d.SetDCVoltage(0, voltage)
|
||||
}
|
||||
}
|
||||
|
||||
// SetLcdVoltage sets voltage of LCD.
|
||||
func (d *Device) SetLcdVoltage(voltage uint16) {
|
||||
if voltage >= 2500 && voltage <= 3300 {
|
||||
d.SetDCVoltage(2, voltage)
|
||||
}
|
||||
}
|
||||
|
||||
// SetDCDC3 enables or disables DCDC3.
|
||||
func (d *Device) SetDCDC3(State bool) {
|
||||
v := d.GetDCDC13LDO23Switch()
|
||||
if State == true {
|
||||
v = (1 << 1) | v
|
||||
} else {
|
||||
v = ^(uint8(1) << 1) & v
|
||||
}
|
||||
d.SetDCDC13LDO23Switch(v)
|
||||
}
|
||||
|
||||
// Pin is a single pin on AXP192.
|
||||
type Pin struct {
|
||||
pin uint8
|
||||
axp *Device
|
||||
}
|
||||
|
||||
// High sets this GPIO pin to high.
|
||||
func (p Pin) High() {
|
||||
switch p.pin {
|
||||
case 1: // LED
|
||||
v := p.axp.GetGPIO20SignalStatus()
|
||||
p.axp.SetGPIO20SignalStatus(v | 0x02)
|
||||
case 2: // SPK_EN
|
||||
case 4: // RST
|
||||
v := p.axp.GetGPIO43SignalStatus()
|
||||
v |= uint8(0x02)
|
||||
p.axp.SetGPIO43SignalStatus(v)
|
||||
}
|
||||
}
|
||||
|
||||
// Low sets this GPIO pin to low.
|
||||
func (p Pin) Low() {
|
||||
switch p.pin {
|
||||
case 1: // LED
|
||||
v := p.axp.GetGPIO20SignalStatus()
|
||||
p.axp.SetGPIO20SignalStatus(v & 0xFD)
|
||||
case 2: // SPK_EN
|
||||
case 4: // RST
|
||||
v := p.axp.GetGPIO43SignalStatus()
|
||||
v &= ^uint8(0x02)
|
||||
p.axp.SetGPIO43SignalStatus(v)
|
||||
}
|
||||
}
|
||||
|
||||
// Toggle switches an output pin from low to high or from high to low.
|
||||
func (p Pin) Toggle() {
|
||||
switch p.pin {
|
||||
case 1: // LED
|
||||
v := p.axp.GetGPIO20SignalStatus()
|
||||
if (v & 0x02) == 0 {
|
||||
p.axp.SetGPIO20SignalStatus(v | 0x02)
|
||||
} else {
|
||||
p.axp.SetGPIO20SignalStatus(v & 0xFD)
|
||||
}
|
||||
case 2: // SPK_EN
|
||||
case 4: // RST
|
||||
v := p.axp.GetGPIO43SignalStatus()
|
||||
if (v & 0x02) == 0 {
|
||||
v |= uint8(0x02)
|
||||
} else {
|
||||
v &= ^uint8(0x02)
|
||||
}
|
||||
p.axp.SetGPIO43SignalStatus(v)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
package axp192
|
||||
|
||||
// power supply control class
|
||||
// 0x00 Power supply status register
|
||||
// 0x01 Power supply mode/charging status register
|
||||
// 0x04 OTG VBUS status register
|
||||
// 0x06‐09 Data buffer register
|
||||
// 0x10 EXTEN & DC‐DC2 switch register
|
||||
// 0x12 DC‐DC1/3 & LDO2/3switch register
|
||||
// 0x23 DC‐DC2 voltage set register
|
||||
// 0x25 DC‐DC2 voltage slope set register
|
||||
// 0x26 DC‐DC1voltage set register
|
||||
// 0x27 DC‐DC3 voltage set register
|
||||
// 0x28 LDO2/3 voltage set register
|
||||
// 0x30 VBUS‐IPSOUT access set register
|
||||
// 0x31 VOFF power off voltage set register
|
||||
// 0x32 Power off、battery detect、CHGLED control register
|
||||
// 0x33 Charging control register1
|
||||
// 0x34 Charging control register2
|
||||
// 0x35 Backup battery charging control register
|
||||
// 0x36 PEK parameter set register
|
||||
// 0x37 DCDC switch frequency set register
|
||||
// 0x38 Battery charging under temperature warning set register
|
||||
// 0x39 Battery charging over temperature warning set register
|
||||
// 0x3A APS under voltage Level1 set register
|
||||
// 0x3B APS under voltage Level2 set register
|
||||
// 0x3C Battery discharging under temperature warning set register
|
||||
// 0x3D Battery discharging over temperature warning set register
|
||||
// 0x80 DCDC mode set register
|
||||
// 0x82 ADC enable set register 1
|
||||
// 0x83 ADC enable set register 2
|
||||
// 0x84 ADC sample frequency set, TS pin control register
|
||||
// 0x85 GPIO [3:0] input range set register
|
||||
// 0x8A Timer control register
|
||||
// 0x8B VBUS monitor set register
|
||||
// 0x8F Over temperature power off control register
|
||||
|
||||
// GPIO control class
|
||||
// 0x90 GPIO0 control register
|
||||
// 0x91 GPIO0 LDO mode output voltage set register
|
||||
// 0x92 GPIO1 control register
|
||||
// 0x93 GPIO2 control register
|
||||
// 0x94 GPIO[2:0] signal status register
|
||||
// 0x95 GPIO[4:3] function control register
|
||||
// 0x96 GPIO[4:3] signal status register
|
||||
// 0x97 GPIO[2:0] pull down control register
|
||||
// 0x98 PWM1 frequency set register
|
||||
// 0x99 PWM1 duty ratio set register 1
|
||||
// 0x9A PWM1 duty ratio set register 2
|
||||
// 0x9B PWM2 frequency set register
|
||||
// 0x9C PWM2 duty ratio set register 1
|
||||
// 0x9D PWM2 duty ratio set register 2
|
||||
// 0x9E GPIO5 control register
|
||||
|
||||
// IRQ control class
|
||||
// 0x40 IRQ enable control register 1
|
||||
// 0x41 IRQ enable control register 2
|
||||
// 0x42 IRQ enable control register 3
|
||||
// 0x43 IRQ enable control register 4
|
||||
// 0x44 IRQ status register 1
|
||||
// 0x45 IRQ status register 2
|
||||
// 0x46 IRQ status register 3
|
||||
// 0x47 IRQ status register 4
|
||||
|
||||
// ADC data class
|
||||
// 0x56 ACIN voltage ADC data high 8 bit
|
||||
// 0x57 ACIN voltage ADC data low 4 bit
|
||||
// 0x58 ACIN current ADC data high 8 bit
|
||||
// 0x59 ACIN current ADC data low 4 bit
|
||||
// 0x5A VBUS voltage ADC data high 8 bit
|
||||
// 0x5B VBUS voltage ADC data low 4 bit
|
||||
// 0x5C VBUS current ADC data high 8 bit
|
||||
// 0x5D VBUS current ADC data low 4 bit
|
||||
// 0x5E AXP192 internal temperature monitor ADC data High 8 bit
|
||||
// 0x5F AXP192 internal temperature monitor ADC data low 4 bit
|
||||
// 0x62 TS input ADC data High 8 bit,monitor battery temperature by default
|
||||
// 0x63 TS input ADC data low 4 bit,monitor battery temperature by default
|
||||
// 0x64 GPIO0 voltage ADC data high 8 bit
|
||||
// 0x65 GPIO0 voltage ADC data low 4 bit
|
||||
// 0x66 GPIO1 voltage ADC data high 8 bit
|
||||
// 0x67 GPIO1 voltage ADC data low 4 bit
|
||||
// 0x68 GPIO2 voltage ADC data high 8 bit
|
||||
// 0x69 GPIO2 voltage ADC data low 4 bit
|
||||
// 0x6A GPIO[3] voltage ADC data high 8 bit
|
||||
// 0x6B GPIO[3] voltage ADC data low 4 bit
|
||||
// 0x70 Battery instantaneous power high 8 bit
|
||||
// 0x71 Battery instantaneous power middle 8 bit
|
||||
// 0x72 Battery instantaneous power low 8 bit
|
||||
// 0x78 Battery voltage high 8 bit
|
||||
// 0x79 Battery voltage low 4 bit
|
||||
// 0x7A Battery charging current high 8 bit
|
||||
// 0x7B Battery charging current low 5 bit
|
||||
// 0x7C Battery discharging current high 8 bit
|
||||
// 0x7D Battery discharging current low 5 bit
|
||||
// 0x7E APS voltage high 8 bit
|
||||
// 0x7F APS voltage low 4 bit
|
||||
// 0xB0 Battery charging coulomb counter data register 3
|
||||
// 0xB1 Battery charging coulomb counter data register 2
|
||||
// 0xB2 Battery charging coulomb counter data register 1
|
||||
// 0xB3 Battery charging coulomb counter data register 0
|
||||
// 0xB4 Battery discharging coulomb counter data register 3
|
||||
// 0xB5 Battery discharging coulomb counter data register 2
|
||||
// 0xB6 Battery discharging coulomb counter data register 1
|
||||
// 0xB7 Battery discharging coulomb counter data register 0
|
||||
// 0xB8 Coulomb counter control register
|
||||
|
||||
const (
|
||||
// Address is default I2C address.
|
||||
Address = 0x34
|
||||
|
||||
RegPowerSupplyStatus = 0x00
|
||||
RegDCDC13LDO23Switch = 0x12
|
||||
RegVbusIPSOutAccessManagement = 0x30
|
||||
RegBackupBatteryChargingControl = 0x35
|
||||
RegDCDC2VoltageSet = 0x25
|
||||
RegDCDC1VoltageSet = 0x26
|
||||
RegDCDC3VoltageSet = 0x27
|
||||
RegLDO23VoltageSet = 0x28
|
||||
RegPEKParameterSet = 0x36
|
||||
RegADCEnableSet = 0x82
|
||||
|
||||
RegGPIO1Control = 0x92
|
||||
RegGPIO2Control = 0x93
|
||||
RegGPIO20SignalStatus = 0x94
|
||||
RegGPIO43FunctionControl = 0x95
|
||||
RegGPIO43SignalStatus = 0x96
|
||||
)
|
||||
@@ -0,0 +1,49 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// See ../../image/README.md for the usage.
|
||||
|
||||
func main() {
|
||||
err := run(os.Args)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func run(args []string) error {
|
||||
if len(args) < 2 {
|
||||
return fmt.Errorf("usage: %s FILE")
|
||||
}
|
||||
|
||||
b, err := ioutil.ReadFile(args[1])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("const %s = \"\" +\n", strings.Replace(args[1], ".", "_", -1))
|
||||
|
||||
i := 0
|
||||
max := 32
|
||||
for i = 0; i < len(b); i++ {
|
||||
bb := b[i]
|
||||
if (i % max) == 0 {
|
||||
fmt.Printf(" \"")
|
||||
}
|
||||
fmt.Printf("\\x%02X", bb)
|
||||
if (i%max) == max-1 && i != len(b)-1 {
|
||||
fmt.Printf("\" + \n")
|
||||
}
|
||||
}
|
||||
if (i % max) < max-1 {
|
||||
fmt.Printf("\"\n")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
+2
-1
@@ -1,4 +1,5 @@
|
||||
// +build mimxrt1062 stm32f405 atsamd51 stm32f103xx k210 stm32f407
|
||||
//go:build mimxrt1062 || stm32f405 || atsamd51 || stm32f103xx || k210 || stm32f407
|
||||
// +build mimxrt1062 stm32f405 atsamd51 stm32f103xx k210 stm32f407
|
||||
|
||||
package dht // import "tinygo.org/x/drivers/dht"
|
||||
|
||||
|
||||
+2
-1
@@ -1,4 +1,5 @@
|
||||
// +build arduino atmega1284p nrf52840 digispark nrf52 arduino_nano nrf51 atsamd21 fe310 arduino_nano33 circuitplay_express arduino_mega2560
|
||||
//go:build !mimxrt1062 && !stm32f405 && !atsamd51 && !stm32f103xx && !k210 && !stm32f407
|
||||
// +build !mimxrt1062,!stm32f405,!atsamd51,!stm32f103xx,!k210,!stm32f407
|
||||
|
||||
package dht // import "tinygo.org/x/drivers/dht"
|
||||
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/apds9960"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
machine.I2C1.Configure(machine.I2CConfig{
|
||||
SCL: machine.P0_15, // SCL1 on Nano 33 BLE Sense
|
||||
SDA: machine.P0_14, // SDA1 on Nano 33 BLE Sense
|
||||
Frequency: machine.TWI_FREQ_400KHZ,
|
||||
})
|
||||
|
||||
sensor := apds9960.New(machine.I2C1)
|
||||
|
||||
if !sensor.Connected() {
|
||||
println("APDS-9960 not connected!")
|
||||
return
|
||||
}
|
||||
|
||||
sensor.Configure(apds9960.Configuration{}) // use default settings
|
||||
|
||||
sensor.EnableColor() // enable color engine
|
||||
|
||||
for {
|
||||
|
||||
if sensor.ColorAvailable() {
|
||||
r, g, b, c := sensor.ReadColor()
|
||||
println("Red =", r, "\tGreen =", g, "\tBlue =", b, "\tClear =", c)
|
||||
}
|
||||
time.Sleep(time.Millisecond * 100)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/apds9960"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
machine.I2C1.Configure(machine.I2CConfig{
|
||||
SCL: machine.P0_15, // SCL1 on Nano 33 BLE Sense
|
||||
SDA: machine.P0_14, // SDA1 on Nano 33 BLE Sense
|
||||
Frequency: machine.TWI_FREQ_400KHZ,
|
||||
})
|
||||
|
||||
sensor := apds9960.New(machine.I2C1)
|
||||
|
||||
if !sensor.Connected() {
|
||||
println("APDS-9960 not connected!")
|
||||
return
|
||||
}
|
||||
|
||||
sensor.Configure(apds9960.Configuration{}) // use default settings
|
||||
|
||||
sensor.EnableGesture() // enable gesture engine
|
||||
|
||||
for {
|
||||
|
||||
// wave your hand (not too slow) about 10 cm above the sensor
|
||||
if sensor.GestureAvailable() {
|
||||
|
||||
gesture := sensor.ReadGesture()
|
||||
print("Detected gesture: ")
|
||||
switch gesture {
|
||||
case apds9960.GESTURE_UP:
|
||||
println("Up")
|
||||
case apds9960.GESTURE_DOWN:
|
||||
println("Down")
|
||||
case apds9960.GESTURE_LEFT:
|
||||
println("Left")
|
||||
case apds9960.GESTURE_RIGHT:
|
||||
println("Right")
|
||||
}
|
||||
}
|
||||
// note: the delay shouldn't be too long, otherwise new gesture data might be lost
|
||||
time.Sleep(time.Millisecond * 250)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/apds9960"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
machine.I2C1.Configure(machine.I2CConfig{
|
||||
SCL: machine.P0_15, // SCL1 on Nano 33 BLE Sense
|
||||
SDA: machine.P0_14, // SDA1 on Nano 33 BLE Sense
|
||||
Frequency: machine.TWI_FREQ_400KHZ,
|
||||
})
|
||||
|
||||
sensor := apds9960.New(machine.I2C1)
|
||||
|
||||
if !sensor.Connected() {
|
||||
println("APDS-9960 not connected!")
|
||||
return
|
||||
}
|
||||
|
||||
sensor.Configure(apds9960.Configuration{}) // use default settings
|
||||
|
||||
sensor.EnableProximity() // enable proximity engine
|
||||
|
||||
for {
|
||||
|
||||
if sensor.ProximityAvailable() {
|
||||
p := sensor.ReadProximity()
|
||||
println("Proximity:", p)
|
||||
}
|
||||
time.Sleep(time.Millisecond * 100)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
"time"
|
||||
|
||||
axp192 "tinygo.org/x/drivers/axp192/m5stack-core2-axp192"
|
||||
"tinygo.org/x/drivers/i2csoft"
|
||||
)
|
||||
|
||||
func main() {
|
||||
i2c := i2csoft.New(machine.SCL0_PIN, machine.SDA0_PIN)
|
||||
i2c.Configure(i2csoft.I2CConfig{Frequency: 100e3})
|
||||
|
||||
axp := axp192.New(i2c)
|
||||
led := axp.LED
|
||||
|
||||
for {
|
||||
led.Low()
|
||||
time.Sleep(time.Millisecond * 500)
|
||||
|
||||
led.High()
|
||||
time.Sleep(time.Millisecond * 500)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/hts221"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
machine.I2C1.Configure(machine.I2CConfig{
|
||||
SCL: machine.P0_15, // SCL1 on Nano 33 BLE Sense
|
||||
SDA: machine.P0_14, // SDA1 on Nano 33 BLE Sense
|
||||
Frequency: machine.TWI_FREQ_400KHZ,
|
||||
})
|
||||
|
||||
sensor := hts221.New(machine.I2C1)
|
||||
|
||||
if !sensor.Connected() {
|
||||
println("HTS221 not connected!")
|
||||
return
|
||||
}
|
||||
|
||||
sensor.Configure() // power on and calibrate
|
||||
|
||||
for {
|
||||
|
||||
h, _ := sensor.ReadHumidity()
|
||||
t, _ := sensor.ReadTemperature()
|
||||
println("h =", float32(h)/100.0, "% / t =", float32(t)/1000.0, "*C")
|
||||
time.Sleep(time.Second)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"machine"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/adt7410"
|
||||
"tinygo.org/x/drivers/i2csoft"
|
||||
)
|
||||
|
||||
func main() {
|
||||
i2c := i2csoft.New(machine.SCL_PIN, machine.SDA_PIN)
|
||||
i2c.Configure(i2csoft.I2CConfig{
|
||||
Frequency: 400e3,
|
||||
})
|
||||
|
||||
sensor := adt7410.New(i2c)
|
||||
sensor.Configure()
|
||||
|
||||
for {
|
||||
temp := sensor.ReadTempF()
|
||||
fmt.Printf("temperature: %f\r\n", temp)
|
||||
time.Sleep(time.Second)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
// +build atsamd21
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
|
||||
"tinygo.org/x/drivers/ili9341"
|
||||
)
|
||||
|
||||
var (
|
||||
display = ili9341.NewSPI(
|
||||
machine.SPI0,
|
||||
machine.D0,
|
||||
machine.D1,
|
||||
machine.D2,
|
||||
)
|
||||
|
||||
backlight = machine.D3
|
||||
)
|
||||
|
||||
func init() {
|
||||
machine.SPI0.Configure(machine.SPIConfig{
|
||||
SCK: machine.SPI0_SCK_PIN,
|
||||
SDO: machine.SPI0_SDO_PIN,
|
||||
SDI: machine.SPI0_SDI_PIN,
|
||||
Frequency: 24000000,
|
||||
})
|
||||
}
|
||||
@@ -2,9 +2,9 @@ package main
|
||||
|
||||
import (
|
||||
"image/color"
|
||||
"machine"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/examples/ili9341/initdisplay"
|
||||
"tinygo.org/x/drivers/ili9341"
|
||||
)
|
||||
|
||||
@@ -16,16 +16,15 @@ var (
|
||||
green = color.RGBA{0, 255, 0, 255}
|
||||
)
|
||||
|
||||
var (
|
||||
display *ili9341.Device
|
||||
)
|
||||
|
||||
func main() {
|
||||
display = initdisplay.InitDisplay()
|
||||
|
||||
backlight.Configure(machine.PinConfig{machine.PinOutput})
|
||||
|
||||
display.Configure(ili9341.Config{})
|
||||
width, height := display.Size()
|
||||
|
||||
display.FillScreen(black)
|
||||
backlight.High()
|
||||
|
||||
display.FillRectangle(0, 0, width/2, height/2, white)
|
||||
display.FillRectangle(width/2, 0, width/2, height/2, red)
|
||||
display.FillRectangle(0, height/2, width/2, height/2, green)
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
// +build pyportal
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
|
||||
"tinygo.org/x/drivers/ili9341"
|
||||
)
|
||||
|
||||
var (
|
||||
display = ili9341.NewParallel(
|
||||
machine.LCD_DATA0,
|
||||
machine.TFT_WR,
|
||||
machine.TFT_DC,
|
||||
machine.TFT_CS,
|
||||
machine.TFT_RESET,
|
||||
machine.TFT_RD,
|
||||
)
|
||||
|
||||
backlight = machine.TFT_BACKLIGHT
|
||||
)
|
||||
@@ -1,29 +0,0 @@
|
||||
// +build wioterminal
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
|
||||
"tinygo.org/x/drivers/ili9341"
|
||||
)
|
||||
|
||||
var (
|
||||
display = ili9341.NewSPI(
|
||||
machine.SPI3,
|
||||
machine.LCD_DC,
|
||||
machine.LCD_SS_PIN,
|
||||
machine.LCD_RESET,
|
||||
)
|
||||
|
||||
backlight = machine.LCD_BACKLIGHT
|
||||
)
|
||||
|
||||
func init() {
|
||||
machine.SPI3.Configure(machine.SPIConfig{
|
||||
SCK: machine.LCD_SCK_PIN,
|
||||
SDO: machine.LCD_SDO_PIN,
|
||||
SDI: machine.LCD_SDI_PIN,
|
||||
Frequency: 40000000,
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
//go:build atsamd21
|
||||
// +build atsamd21
|
||||
|
||||
package initdisplay
|
||||
|
||||
import (
|
||||
"machine"
|
||||
|
||||
"tinygo.org/x/drivers/ili9341"
|
||||
)
|
||||
|
||||
var ()
|
||||
|
||||
func InitDisplay() *ili9341.Device {
|
||||
machine.SPI0.Configure(machine.SPIConfig{
|
||||
SCK: machine.SPI0_SCK_PIN,
|
||||
SDO: machine.SPI0_SDO_PIN,
|
||||
SDI: machine.SPI0_SDI_PIN,
|
||||
Frequency: 24000000,
|
||||
})
|
||||
|
||||
// configure backlight
|
||||
backlight := machine.D3
|
||||
backlight.Configure(machine.PinConfig{machine.PinOutput})
|
||||
|
||||
display := ili9341.NewSPI(
|
||||
machine.SPI0,
|
||||
machine.D0,
|
||||
machine.D1,
|
||||
machine.D2,
|
||||
)
|
||||
|
||||
// configure display
|
||||
display.Configure(ili9341.Config{})
|
||||
|
||||
backlight.High()
|
||||
|
||||
display.SetRotation(ili9341.Rotation270)
|
||||
|
||||
return display
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
//go:build pyportal
|
||||
// +build pyportal
|
||||
|
||||
package initdisplay
|
||||
|
||||
import (
|
||||
"machine"
|
||||
|
||||
"tinygo.org/x/drivers/ili9341"
|
||||
)
|
||||
|
||||
func InitDisplay() *ili9341.Device {
|
||||
display := ili9341.NewParallel(
|
||||
machine.LCD_DATA0,
|
||||
machine.TFT_WR,
|
||||
machine.TFT_DC,
|
||||
machine.TFT_CS,
|
||||
machine.TFT_RESET,
|
||||
machine.TFT_RD,
|
||||
)
|
||||
|
||||
// configure backlight
|
||||
backlight := machine.TFT_BACKLIGHT
|
||||
backlight.Configure(machine.PinConfig{machine.PinOutput})
|
||||
|
||||
// configure display
|
||||
display.Configure(ili9341.Config{})
|
||||
|
||||
backlight.High()
|
||||
|
||||
display.SetRotation(ili9341.Rotation270)
|
||||
|
||||
return display
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
//go:build wioterminal
|
||||
// +build wioterminal
|
||||
|
||||
package initdisplay
|
||||
|
||||
import (
|
||||
"machine"
|
||||
|
||||
"tinygo.org/x/drivers/ili9341"
|
||||
)
|
||||
|
||||
func InitDisplay() *ili9341.Device {
|
||||
machine.SPI3.Configure(machine.SPIConfig{
|
||||
SCK: machine.LCD_SCK_PIN,
|
||||
SDO: machine.LCD_SDO_PIN,
|
||||
SDI: machine.LCD_SDI_PIN,
|
||||
Frequency: 40000000,
|
||||
})
|
||||
|
||||
// configure backlight
|
||||
backlight := machine.LCD_BACKLIGHT
|
||||
backlight.Configure(machine.PinConfig{machine.PinOutput})
|
||||
|
||||
display := ili9341.NewSPI(
|
||||
machine.SPI3,
|
||||
machine.LCD_DC,
|
||||
machine.LCD_SS_PIN,
|
||||
machine.LCD_RESET,
|
||||
)
|
||||
|
||||
// configure display
|
||||
display.Configure(ili9341.Config{})
|
||||
|
||||
backlight.High()
|
||||
|
||||
display.SetRotation(ili9341.Rotation270)
|
||||
|
||||
return display
|
||||
}
|
||||
@@ -3,9 +3,9 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/examples/ili9341/initdisplay"
|
||||
"tinygo.org/x/drivers/examples/ili9341/pyportal_boing/graphics"
|
||||
"tinygo.org/x/drivers/ili9341"
|
||||
)
|
||||
@@ -44,20 +44,17 @@ var (
|
||||
palette [16]uint16
|
||||
)
|
||||
|
||||
var (
|
||||
display *ili9341.Device
|
||||
)
|
||||
|
||||
func main() {
|
||||
display = initdisplay.InitDisplay()
|
||||
|
||||
// configure backlight
|
||||
backlight.Configure(machine.PinConfig{machine.PinOutput})
|
||||
|
||||
// configure display
|
||||
display.Configure(ili9341.Config{})
|
||||
print("width, height == ")
|
||||
width, height := display.Size()
|
||||
println(width, height)
|
||||
|
||||
backlight.High()
|
||||
|
||||
display.SetRotation(ili9341.Rotation270)
|
||||
DrawBackground()
|
||||
|
||||
startTime = time.Now().UnixNano()
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
// +build pyportal
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
|
||||
"tinygo.org/x/drivers/ili9341"
|
||||
)
|
||||
|
||||
var (
|
||||
display = ili9341.NewParallel(
|
||||
machine.LCD_DATA0,
|
||||
machine.TFT_WR,
|
||||
machine.TFT_DC,
|
||||
machine.TFT_CS,
|
||||
machine.TFT_RESET,
|
||||
machine.TFT_RD,
|
||||
)
|
||||
|
||||
backlight = machine.TFT_BACKLIGHT
|
||||
)
|
||||
@@ -1,29 +0,0 @@
|
||||
// +build wioterminal
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
|
||||
"tinygo.org/x/drivers/ili9341"
|
||||
)
|
||||
|
||||
var (
|
||||
display = ili9341.NewSPI(
|
||||
machine.SPI3,
|
||||
machine.LCD_DC,
|
||||
machine.LCD_SS_PIN,
|
||||
machine.LCD_RESET,
|
||||
)
|
||||
|
||||
backlight = machine.LCD_BACKLIGHT
|
||||
)
|
||||
|
||||
func init() {
|
||||
machine.SPI3.Configure(machine.SPIConfig{
|
||||
SCK: machine.LCD_SCK_PIN,
|
||||
SDO: machine.LCD_SDO_PIN,
|
||||
SDI: machine.LCD_SDI_PIN,
|
||||
Frequency: 40000000,
|
||||
})
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
// +build atsamd21
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
|
||||
"tinygo.org/x/drivers/ili9341"
|
||||
)
|
||||
|
||||
var (
|
||||
display = ili9341.NewSPI(
|
||||
machine.SPI0,
|
||||
machine.D0,
|
||||
machine.D1,
|
||||
machine.D2,
|
||||
)
|
||||
|
||||
backlight = machine.D3
|
||||
)
|
||||
|
||||
func init() {
|
||||
machine.SPI0.Configure(machine.SPIConfig{
|
||||
SCK: machine.SPI0_SCK_PIN,
|
||||
SDO: machine.SPI0_SDO_PIN,
|
||||
SDI: machine.SPI0_SDI_PIN,
|
||||
Frequency: 24000000,
|
||||
})
|
||||
}
|
||||
@@ -2,9 +2,9 @@ package main
|
||||
|
||||
import (
|
||||
"image/color"
|
||||
"machine"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/examples/ili9341/initdisplay"
|
||||
"tinygo.org/x/drivers/ili9341"
|
||||
)
|
||||
|
||||
@@ -16,16 +16,15 @@ var (
|
||||
white = color.RGBA{255, 255, 255, 255}
|
||||
)
|
||||
|
||||
var (
|
||||
display *ili9341.Device
|
||||
)
|
||||
|
||||
func main() {
|
||||
display := initdisplay.InitDisplay()
|
||||
|
||||
backlight.Configure(machine.PinConfig{machine.PinOutput})
|
||||
|
||||
display.Configure(ili9341.Config{})
|
||||
width, height := display.Size()
|
||||
|
||||
display.FillScreen(black)
|
||||
backlight.High()
|
||||
|
||||
display.FillRectangle(0, 0, width/2, height/2, white)
|
||||
display.FillRectangle(width/2, 0, width/2, height/2, red)
|
||||
display.FillRectangle(0, height/2, width/2, height/2, green)
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
// +build pyportal
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
|
||||
"tinygo.org/x/drivers/ili9341"
|
||||
)
|
||||
|
||||
var (
|
||||
display = ili9341.NewParallel(
|
||||
machine.LCD_DATA0,
|
||||
machine.TFT_WR,
|
||||
machine.TFT_DC,
|
||||
machine.TFT_CS,
|
||||
machine.TFT_RESET,
|
||||
machine.TFT_RD,
|
||||
)
|
||||
|
||||
backlight = machine.TFT_BACKLIGHT
|
||||
)
|
||||
@@ -1,29 +0,0 @@
|
||||
// +build wioterminal
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
|
||||
"tinygo.org/x/drivers/ili9341"
|
||||
)
|
||||
|
||||
var (
|
||||
display = ili9341.NewSPI(
|
||||
machine.SPI3,
|
||||
machine.LCD_DC,
|
||||
machine.LCD_SS_PIN,
|
||||
machine.LCD_RESET,
|
||||
)
|
||||
|
||||
backlight = machine.LCD_BACKLIGHT
|
||||
)
|
||||
|
||||
func init() {
|
||||
machine.SPI3.Configure(machine.SPIConfig{
|
||||
SCK: machine.LCD_SCK_PIN,
|
||||
SDO: machine.LCD_SDO_PIN,
|
||||
SDI: machine.LCD_SDI_PIN,
|
||||
Frequency: 40000000,
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
# examples/ili9341/slideshow
|
||||
|
||||

|
||||
|
||||
This example uses the image package for TinyGo to display png and jpeg images.
|
||||
|
||||
## How to create an image
|
||||
|
||||
The following program will output an image binary like the one in [images.go](./images.go).
|
||||
|
||||
```
|
||||
go run ./examples/ili9341/slideshow/convert2bin ./path/to/png_or_jpg.png
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
Displaying a 320x240 png or jpeg often requires more than 50KB of memory.
|
||||
The examples include samd21 settings, but if you run them as is, you will get a memory size error.
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,95 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"image/color"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/examples/ili9341/initdisplay"
|
||||
"tinygo.org/x/drivers/ili9341"
|
||||
"tinygo.org/x/drivers/image/jpeg"
|
||||
"tinygo.org/x/drivers/image/png"
|
||||
)
|
||||
|
||||
var (
|
||||
black = color.RGBA{0, 0, 0, 255}
|
||||
white = color.RGBA{255, 255, 255, 255}
|
||||
red = color.RGBA{255, 0, 0, 255}
|
||||
blue = color.RGBA{0, 0, 255, 255}
|
||||
green = color.RGBA{0, 255, 0, 255}
|
||||
)
|
||||
|
||||
var (
|
||||
display *ili9341.Device
|
||||
)
|
||||
|
||||
func main() {
|
||||
err := run()
|
||||
for err != nil {
|
||||
errorMessage(err)
|
||||
}
|
||||
}
|
||||
|
||||
func run() error {
|
||||
display = initdisplay.InitDisplay()
|
||||
|
||||
width, height := display.Size()
|
||||
if width < 320 || height < 240 {
|
||||
display.SetRotation(ili9341.Rotation270)
|
||||
}
|
||||
|
||||
display.FillScreen(black)
|
||||
|
||||
for {
|
||||
err := drawJpeg(display)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
time.Sleep(time.Second)
|
||||
|
||||
err = drawPng(display)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
time.Sleep(time.Second)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Define the buffer required for the callback. In most cases, this setting
|
||||
// should be sufficient. For jpeg, the callback will always be called every
|
||||
// 3*8*8*4 pix. png will be called every line, i.e. every width pix.
|
||||
var buffer [3 * 8 * 8 * 4]uint16
|
||||
|
||||
func drawPng(display *ili9341.Device) error {
|
||||
p := strings.NewReader(pngImage)
|
||||
png.SetCallback(buffer[:], func(data []uint16, x, y, w, h, width, height int16) {
|
||||
err := display.DrawRGBBitmap(x, y, data[:w*h], w, h)
|
||||
if err != nil {
|
||||
errorMessage(fmt.Errorf("error drawPng: %s", err))
|
||||
}
|
||||
})
|
||||
|
||||
return png.Decode(p)
|
||||
}
|
||||
|
||||
func drawJpeg(display *ili9341.Device) error {
|
||||
p := strings.NewReader(jpegImage)
|
||||
jpeg.SetCallback(buffer[:], func(data []uint16, x, y, w, h, width, height int16) {
|
||||
err := display.DrawRGBBitmap(x, y, data[:w*h], w, h)
|
||||
if err != nil {
|
||||
errorMessage(fmt.Errorf("error drawJpeg: %s", err))
|
||||
}
|
||||
})
|
||||
|
||||
return jpeg.Decode(p)
|
||||
}
|
||||
|
||||
func errorMessage(err error) {
|
||||
for {
|
||||
fmt.Printf("%s\r\n", err.Error())
|
||||
time.Sleep(5 * time.Second)
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 30 KiB |
@@ -0,0 +1,37 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/lps22hb"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
machine.I2C1.Configure(machine.I2CConfig{
|
||||
SCL: machine.P0_15, // SCL1 on Nano 33 BLE Sense
|
||||
SDA: machine.P0_14, // SDA1 on Nano 33 BLE Sense
|
||||
Frequency: machine.TWI_FREQ_400KHZ,
|
||||
})
|
||||
|
||||
sensor := lps22hb.New(machine.I2C1)
|
||||
|
||||
if !sensor.Connected() {
|
||||
println("LPS22HB not connected!")
|
||||
return
|
||||
}
|
||||
|
||||
sensor.Configure()
|
||||
|
||||
for {
|
||||
|
||||
p, _ := sensor.ReadPressure()
|
||||
t, _ := sensor.ReadTemperature()
|
||||
println("p =", float32(p)/1000.0, "hPa / t =", float32(t)/1000.0, "*C")
|
||||
time.Sleep(time.Second)
|
||||
// note: the device would power down itself after each query
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
// Connects to an LSM6DSOX I2C a 6 axis Inertial Measurement Unit (IMU)
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"machine"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/lsm6dsox"
|
||||
)
|
||||
|
||||
const (
|
||||
PLOTTER = true
|
||||
SHOW_ACCELERATION = false
|
||||
SHOW_ROTATION = true
|
||||
SHOW_TEMPERATURE = false
|
||||
|
||||
// calibration is naive, good enough for a demo: do not shake a second after flashing
|
||||
GYRO_CALIBRATION = true
|
||||
)
|
||||
|
||||
var cal = [...]float32{0, 0, 0}
|
||||
|
||||
func main() {
|
||||
|
||||
machine.I2C0.Configure(machine.I2CConfig{})
|
||||
|
||||
device := lsm6dsox.New(machine.I2C0)
|
||||
device.Configure(lsm6dsox.Configuration{
|
||||
AccelRange: lsm6dsox.ACCEL_2G,
|
||||
AccelSampleRate: lsm6dsox.ACCEL_SR_104,
|
||||
GyroRange: lsm6dsox.GYRO_250DPS,
|
||||
GyroSampleRate: lsm6dsox.GYRO_SR_104,
|
||||
})
|
||||
|
||||
for {
|
||||
|
||||
if !device.Connected() {
|
||||
println("LSM6DSOX not connected")
|
||||
time.Sleep(time.Second)
|
||||
continue
|
||||
}
|
||||
|
||||
// heuristic: after successful calibration the value can't be 0
|
||||
if GYRO_CALIBRATION && cal[0] == 0 {
|
||||
calibrateGyro(device)
|
||||
}
|
||||
|
||||
ax, ay, az := device.ReadAcceleration()
|
||||
gx, gy, gz := device.ReadRotation()
|
||||
t, _ := device.ReadTemperature()
|
||||
|
||||
if PLOTTER {
|
||||
printPlotter(ax, ay, az, gx, gy, gz, t)
|
||||
time.Sleep(time.Millisecond * 100)
|
||||
} else {
|
||||
printMonitor(ax, ay, az, gx, gy, gz, t)
|
||||
time.Sleep(time.Millisecond * 1000)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func calibrateGyro(device *lsm6dsox.Device) {
|
||||
for i := 0; i < 100; i++ {
|
||||
gx, gy, gz := device.ReadRotation()
|
||||
cal[0] += float32(gx) / 1000000
|
||||
cal[1] += float32(gy) / 1000000
|
||||
cal[2] += float32(gz) / 1000000
|
||||
time.Sleep(time.Millisecond * 10)
|
||||
}
|
||||
cal[0] /= 100
|
||||
cal[1] /= 100
|
||||
cal[2] /= 100
|
||||
}
|
||||
|
||||
// Arduino IDE's Serial Plotter
|
||||
func printPlotter(ax, ay, az, gx, gy, gz, t int32) {
|
||||
if SHOW_ACCELERATION {
|
||||
fmt.Printf("AX:%f, AY:%f, AZ:%f,", axis(ax, 0), axis(ay, 0), axis(az, 0))
|
||||
}
|
||||
if SHOW_ROTATION {
|
||||
fmt.Printf("GX:%f, GY:%f, GZ:%f,", axis(gx, cal[0]), axis(gy, cal[1]), axis(gz, cal[2]))
|
||||
}
|
||||
if SHOW_TEMPERATURE {
|
||||
fmt.Printf("T:%f", float32(t)/1000)
|
||||
}
|
||||
println()
|
||||
}
|
||||
|
||||
// Any Serial Monitor
|
||||
func printMonitor(ax, ay, az, gx, gy, gz, t int32) {
|
||||
if SHOW_ACCELERATION {
|
||||
fmt.Printf("Acceleration: %f, %f, %f\r\n", axis(ax, 0), axis(ay, 0), axis(az, 0))
|
||||
}
|
||||
if SHOW_ROTATION {
|
||||
fmt.Printf("Rotation: %f, %f, %f\r\n", axis(gx, cal[0]), axis(gy, cal[1]), axis(gz, cal[2]))
|
||||
}
|
||||
if SHOW_TEMPERATURE {
|
||||
fmt.Printf("Temperature C: %f\r\n", float32(t)/1000)
|
||||
}
|
||||
println()
|
||||
}
|
||||
|
||||
func axis(raw int32, cal float32) float32 {
|
||||
return float32(raw)/1000000 - cal
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
// LSM9DS1, 9 axis Inertial Measurement Unit (IMU)
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"machine"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/lsm9ds1"
|
||||
)
|
||||
|
||||
const (
|
||||
PLOTTER = false
|
||||
SHOW_ACCELERATION = true
|
||||
SHOW_ROTATION = true
|
||||
SHOW_MAGNETIC_FIELD = true
|
||||
SHOW_TEMPERATURE = true
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
// I2C configure
|
||||
machine.I2C0.Configure(machine.I2CConfig{})
|
||||
|
||||
// LSM9DS1 setup
|
||||
device := lsm9ds1.New(machine.I2C0)
|
||||
err := device.Configure(lsm9ds1.Configuration{
|
||||
AccelRange: lsm9ds1.ACCEL_2G,
|
||||
AccelSampleRate: lsm9ds1.ACCEL_SR_119,
|
||||
GyroRange: lsm9ds1.GYRO_250DPS,
|
||||
GyroSampleRate: lsm9ds1.GYRO_SR_119,
|
||||
MagRange: lsm9ds1.MAG_4G,
|
||||
MagSampleRate: lsm9ds1.MAG_SR_40,
|
||||
})
|
||||
if err != nil {
|
||||
for {
|
||||
println("Failed to configure", err.Error())
|
||||
time.Sleep(time.Second)
|
||||
}
|
||||
}
|
||||
|
||||
for {
|
||||
|
||||
if con, err := device.Connected(); !con || err != nil {
|
||||
println("LSM9DS1 not connected")
|
||||
time.Sleep(time.Second)
|
||||
continue
|
||||
}
|
||||
|
||||
ax, ay, az, _ := device.ReadAcceleration()
|
||||
gx, gy, gz, _ := device.ReadRotation()
|
||||
mx, my, mz, _ := device.ReadMagneticField()
|
||||
t, _ := device.ReadTemperature()
|
||||
|
||||
if PLOTTER {
|
||||
printPlotter(ax, ay, az, gx, gy, gz, mx, my, mz, t)
|
||||
time.Sleep(time.Millisecond * 100)
|
||||
} else {
|
||||
printMonitor(ax, ay, az, gx, gy, gz, mx, my, mz, t)
|
||||
time.Sleep(time.Millisecond * 1000)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Arduino IDE's Serial Plotter
|
||||
func printPlotter(ax, ay, az, gx, gy, gz, mx, my, mz, t int32) {
|
||||
if SHOW_ACCELERATION {
|
||||
fmt.Printf("AX:%f, AY:%f, AZ:%f,", axis(ax), axis(ay), axis(az))
|
||||
}
|
||||
if SHOW_ROTATION {
|
||||
fmt.Printf("GX:%f, GY:%f, GZ:%f,", axis(gx), axis(gy), axis(gz))
|
||||
}
|
||||
if SHOW_MAGNETIC_FIELD {
|
||||
fmt.Printf("MX:%d, MY:%d, MZ:%d,", mx, my, mz)
|
||||
}
|
||||
if SHOW_TEMPERATURE {
|
||||
fmt.Printf("T:%f", float32(t)/1000)
|
||||
}
|
||||
println()
|
||||
}
|
||||
|
||||
// Any Serial Monitor
|
||||
func printMonitor(ax, ay, az, gx, gy, gz, mx, my, mz, t int32) {
|
||||
if SHOW_ACCELERATION {
|
||||
fmt.Printf("Acceleration (g): %f, %f, %f\r\n", axis(ax), axis(ay), axis(az))
|
||||
}
|
||||
if SHOW_ROTATION {
|
||||
fmt.Printf("Rotation (dps): %f, %f, %f\r\n", axis(gx), axis(gy), axis(gz))
|
||||
}
|
||||
if SHOW_MAGNETIC_FIELD {
|
||||
fmt.Printf("Magnetic field (nT): %d, %d, %d\r\n", mx, my, mz)
|
||||
}
|
||||
if SHOW_TEMPERATURE {
|
||||
fmt.Printf("Temperature C: %f\r\n", float32(t)/1000)
|
||||
}
|
||||
println()
|
||||
}
|
||||
|
||||
func axis(raw int32) float32 {
|
||||
return float32(raw) / 1000000
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
// This is a sensor station that uses a RTL8720DN running on the device UART2.
|
||||
// It creates an MQTT connection that publishes a message every second
|
||||
// to an MQTT broker.
|
||||
//
|
||||
// In other words:
|
||||
// Your computer <--> USB-CDC <--> MCU <--> UART2 <--> RTL8720DN <--> Internet <--> MQTT broker.
|
||||
//
|
||||
// You must install the Paho MQTT package to build this program:
|
||||
//
|
||||
// go get -u github.com/eclipse/paho.mqtt.golang
|
||||
//
|
||||
// You can check that mqttpub is running successfully with the following command.
|
||||
//
|
||||
// mosquitto_sub -h test.mosquitto.org -t tinygo
|
||||
//
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/net"
|
||||
"tinygo.org/x/drivers/net/mqtt"
|
||||
"tinygo.org/x/drivers/rtl8720dn"
|
||||
)
|
||||
|
||||
// You can override the setting with the init() in another source code.
|
||||
// func init() {
|
||||
// ssid = "your-ssid"
|
||||
// password = "your-password"
|
||||
// debug = true
|
||||
// server = "tinygo.org"
|
||||
// }
|
||||
|
||||
var (
|
||||
ssid string
|
||||
password string
|
||||
server string = "tcp://test.mosquitto.org:1883"
|
||||
debug = false
|
||||
)
|
||||
|
||||
var buf [0x400]byte
|
||||
|
||||
var lastRequestTime time.Time
|
||||
var conn net.Conn
|
||||
var adaptor *rtl8720dn.RTL8720DN
|
||||
|
||||
func main() {
|
||||
err := run()
|
||||
for err != nil {
|
||||
fmt.Printf("error: %s\r\n", err.Error())
|
||||
time.Sleep(5 * time.Second)
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
topic = "tinygo"
|
||||
)
|
||||
|
||||
func run() error {
|
||||
rtl, err := setupRTL8720DN()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
net.UseDriver(rtl)
|
||||
|
||||
err = rtl.ConnectToAP(ssid, password)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ip, subnet, gateway, err := rtl.GetIP()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("IP Address : %s\r\n", ip)
|
||||
fmt.Printf("Mask : %s\r\n", subnet)
|
||||
fmt.Printf("Gateway : %s\r\n", gateway)
|
||||
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
|
||||
opts := mqtt.NewClientOptions()
|
||||
opts.AddBroker(server).SetClientID("tinygo-client-" + randomString(10))
|
||||
|
||||
println("Connectng to MQTT...")
|
||||
cl := mqtt.NewClient(opts)
|
||||
if token := cl.Connect(); token.Wait() && token.Error() != nil {
|
||||
failMessage(token.Error().Error())
|
||||
}
|
||||
|
||||
for i := 0; ; i++ {
|
||||
println("Publishing MQTT message...")
|
||||
data := []byte(fmt.Sprintf(`{"e":[{"n":"hello %d","v":101}]}`, i))
|
||||
token := cl.Publish(topic, 0, false, data)
|
||||
token.Wait()
|
||||
if err := token.Error(); err != nil {
|
||||
return err
|
||||
}
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
}
|
||||
|
||||
// Right now this code is never reached. Need a way to trigger it...
|
||||
println("Disconnecting MQTT...")
|
||||
cl.Disconnect(100)
|
||||
|
||||
println("Done.")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Returns an int >= min, < max
|
||||
func randomInt(min, max int) int {
|
||||
return min + rand.Intn(max-min)
|
||||
}
|
||||
|
||||
// Generate a random string of A-Z chars with len = l
|
||||
func randomString(len int) string {
|
||||
bytes := make([]byte, len)
|
||||
for i := 0; i < len; i++ {
|
||||
bytes[i] = byte(randomInt(65, 90))
|
||||
}
|
||||
return string(bytes)
|
||||
}
|
||||
|
||||
func failMessage(msg string) {
|
||||
for {
|
||||
println(msg)
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
//go:build wioterminal
|
||||
// +build wioterminal
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"device/sam"
|
||||
"machine"
|
||||
"runtime/interrupt"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/rtl8720dn"
|
||||
)
|
||||
|
||||
var (
|
||||
uart UARTx
|
||||
)
|
||||
|
||||
func handleInterrupt(interrupt.Interrupt) {
|
||||
// should reset IRQ
|
||||
uart.Receive(byte((uart.Bus.DATA.Get() & 0xFF)))
|
||||
uart.Bus.INTFLAG.SetBits(sam.SERCOM_USART_INT_INTFLAG_RXC)
|
||||
}
|
||||
|
||||
func setupRTL8720DN() (*rtl8720dn.RTL8720DN, error) {
|
||||
machine.RTL8720D_CHIP_PU.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
machine.RTL8720D_CHIP_PU.Low()
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
machine.RTL8720D_CHIP_PU.High()
|
||||
time.Sleep(1000 * time.Millisecond)
|
||||
if debug {
|
||||
waitSerial()
|
||||
}
|
||||
|
||||
uart = UARTx{
|
||||
UART: &machine.UART{
|
||||
Buffer: machine.NewRingBuffer(),
|
||||
Bus: sam.SERCOM0_USART_INT,
|
||||
SERCOM: 0,
|
||||
},
|
||||
}
|
||||
|
||||
uart.Interrupt = interrupt.New(sam.IRQ_SERCOM0_2, handleInterrupt)
|
||||
uart.Configure(machine.UARTConfig{TX: machine.PB24, RX: machine.PC24, BaudRate: 614400})
|
||||
|
||||
rtl := rtl8720dn.New(uart)
|
||||
rtl.Debug(debug)
|
||||
|
||||
_, err := rtl.Rpc_tcpip_adapter_init()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return rtl, nil
|
||||
}
|
||||
|
||||
// Wait for user to open serial console
|
||||
func waitSerial() {
|
||||
for !machine.Serial.DTR() {
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
|
||||
type UARTx struct {
|
||||
*machine.UART
|
||||
}
|
||||
|
||||
func (u UARTx) Read(p []byte) (n int, err error) {
|
||||
if u.Buffered() == 0 {
|
||||
time.Sleep(1 * time.Millisecond)
|
||||
return 0, nil
|
||||
}
|
||||
return u.UART.Read(p)
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
// This is a sensor station that uses a RTL8720DN running on the device UART2.
|
||||
// It creates an MQTT connection that publishes a message every second
|
||||
// to an MQTT broker.
|
||||
//
|
||||
// In other words:
|
||||
// Your computer <--> USB-CDC <--> MCU <--> UART2 <--> RTL8720DN <--> Internet <--> MQTT broker.
|
||||
//
|
||||
// You must also install the Paho MQTT package to build this program:
|
||||
//
|
||||
// go get -u github.com/eclipse/paho.mqtt.golang
|
||||
//
|
||||
// You can check that mqttpub/mqttsub is running successfully with the following command.
|
||||
//
|
||||
// mosquitto_sub -h test.mosquitto.org -t tinygo/tx
|
||||
// mosquitto_pub -h test.mosquitto.org -t tinygo/rx -m "hello world"
|
||||
//
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/net"
|
||||
"tinygo.org/x/drivers/net/mqtt"
|
||||
"tinygo.org/x/drivers/rtl8720dn"
|
||||
)
|
||||
|
||||
// You can override the setting with the init() in another source code.
|
||||
// func init() {
|
||||
// ssid = "your-ssid"
|
||||
// password = "your-password"
|
||||
// debug = true
|
||||
// server = "tinygo.org"
|
||||
// }
|
||||
|
||||
var (
|
||||
ssid string
|
||||
password string
|
||||
server string = "tcp://test.mosquitto.org:1883"
|
||||
debug = false
|
||||
)
|
||||
|
||||
var buf [0x400]byte
|
||||
|
||||
var lastRequestTime time.Time
|
||||
var conn net.Conn
|
||||
var adaptor *rtl8720dn.RTL8720DN
|
||||
|
||||
func main() {
|
||||
err := run()
|
||||
for err != nil {
|
||||
fmt.Printf("error: %s\r\n", err.Error())
|
||||
time.Sleep(5 * time.Second)
|
||||
}
|
||||
}
|
||||
|
||||
// change these to connect to a different UART or pins for the ESP8266/ESP32
|
||||
var (
|
||||
cl mqtt.Client
|
||||
topicTx = "tinygo/tx"
|
||||
topicRx = "tinygo/rx"
|
||||
)
|
||||
|
||||
func subHandler(client mqtt.Client, msg mqtt.Message) {
|
||||
fmt.Printf("[%s] ", msg.Topic())
|
||||
fmt.Printf("%s\r\n", msg.Payload())
|
||||
}
|
||||
|
||||
func run() error {
|
||||
rtl, err := setupRTL8720DN()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
net.UseDriver(rtl)
|
||||
|
||||
err = rtl.ConnectToAP(ssid, password)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ip, subnet, gateway, err := rtl.GetIP()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("IP Address : %s\r\n", ip)
|
||||
fmt.Printf("Mask : %s\r\n", subnet)
|
||||
fmt.Printf("Gateway : %s\r\n", gateway)
|
||||
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
|
||||
opts := mqtt.NewClientOptions()
|
||||
opts.AddBroker(server).SetClientID("tinygo-client-" + randomString(10))
|
||||
|
||||
println("Connecting to MQTT broker at", server)
|
||||
cl = mqtt.NewClient(opts)
|
||||
if token := cl.Connect(); token.Wait() && token.Error() != nil {
|
||||
failMessage(token.Error().Error())
|
||||
}
|
||||
|
||||
// subscribe
|
||||
token := cl.Subscribe(topicRx, 0, subHandler)
|
||||
token.Wait()
|
||||
if token.Error() != nil {
|
||||
failMessage(token.Error().Error())
|
||||
}
|
||||
|
||||
go publishing()
|
||||
|
||||
select {}
|
||||
|
||||
// Right now this code is never reached. Need a way to trigger it...
|
||||
println("Disconnecting MQTT...")
|
||||
cl.Disconnect(100)
|
||||
|
||||
println("Done.")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func publishing() {
|
||||
for i := 0; ; i++ {
|
||||
println("Publishing MQTT message...")
|
||||
data := []byte(fmt.Sprintf(`{"e":[{"n":"hello %d","v":101}]}`, i))
|
||||
token := cl.Publish(topicTx, 0, false, data)
|
||||
token.Wait()
|
||||
if token.Error() != nil {
|
||||
println(token.Error().Error())
|
||||
}
|
||||
|
||||
time.Sleep(20 * 100 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
|
||||
// Returns an int >= min, < max
|
||||
func randomInt(min, max int) int {
|
||||
return min + rand.Intn(max-min)
|
||||
}
|
||||
|
||||
// Generate a random string of A-Z chars with len = l
|
||||
func randomString(len int) string {
|
||||
bytes := make([]byte, len)
|
||||
for i := 0; i < len; i++ {
|
||||
bytes[i] = byte(randomInt(65, 90))
|
||||
}
|
||||
return string(bytes)
|
||||
}
|
||||
|
||||
func failMessage(msg string) {
|
||||
for {
|
||||
println(msg)
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
//go:build wioterminal
|
||||
// +build wioterminal
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"device/sam"
|
||||
"machine"
|
||||
"runtime/interrupt"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/rtl8720dn"
|
||||
)
|
||||
|
||||
var (
|
||||
uart UARTx
|
||||
)
|
||||
|
||||
func handleInterrupt(interrupt.Interrupt) {
|
||||
// should reset IRQ
|
||||
uart.Receive(byte((uart.Bus.DATA.Get() & 0xFF)))
|
||||
uart.Bus.INTFLAG.SetBits(sam.SERCOM_USART_INT_INTFLAG_RXC)
|
||||
}
|
||||
|
||||
func setupRTL8720DN() (*rtl8720dn.RTL8720DN, error) {
|
||||
machine.RTL8720D_CHIP_PU.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
machine.RTL8720D_CHIP_PU.Low()
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
machine.RTL8720D_CHIP_PU.High()
|
||||
time.Sleep(1000 * time.Millisecond)
|
||||
if debug {
|
||||
waitSerial()
|
||||
}
|
||||
|
||||
uart = UARTx{
|
||||
UART: &machine.UART{
|
||||
Buffer: machine.NewRingBuffer(),
|
||||
Bus: sam.SERCOM0_USART_INT,
|
||||
SERCOM: 0,
|
||||
},
|
||||
}
|
||||
|
||||
uart.Interrupt = interrupt.New(sam.IRQ_SERCOM0_2, handleInterrupt)
|
||||
uart.Configure(machine.UARTConfig{TX: machine.PB24, RX: machine.PC24, BaudRate: 614400})
|
||||
|
||||
rtl := rtl8720dn.New(uart)
|
||||
rtl.Debug(debug)
|
||||
|
||||
_, err := rtl.Rpc_tcpip_adapter_init()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return rtl, nil
|
||||
}
|
||||
|
||||
// Wait for user to open serial console
|
||||
func waitSerial() {
|
||||
for !machine.Serial.DTR() {
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
|
||||
type UARTx struct {
|
||||
*machine.UART
|
||||
}
|
||||
|
||||
func (u UARTx) Read(p []byte) (n int, err error) {
|
||||
if u.Buffered() == 0 {
|
||||
time.Sleep(1 * time.Millisecond)
|
||||
return 0, nil
|
||||
}
|
||||
return u.UART.Read(p)
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
// This is an example of using the rtl8720dn driver to implement a NTP client.
|
||||
// It creates a UDP connection to request the current time and parse the
|
||||
// response from a NTP server.
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/net"
|
||||
)
|
||||
|
||||
// IP address of the server aka "hub". Replace with your own info.
|
||||
// You can override the setting with the init() in another source code.
|
||||
// func init() {
|
||||
// ssid = "your-ssid"
|
||||
// password = "your-password"
|
||||
// ntpHost = "129.6.15.29"
|
||||
// debug = true
|
||||
// }
|
||||
|
||||
var (
|
||||
ssid string
|
||||
password string
|
||||
ntpHost = "129.6.15.29"
|
||||
debug = false
|
||||
)
|
||||
|
||||
const NTP_PACKET_SIZE = 48
|
||||
|
||||
var b = make([]byte, NTP_PACKET_SIZE)
|
||||
|
||||
func main() {
|
||||
err := run()
|
||||
for err != nil {
|
||||
fmt.Printf("error: %s\r\n", err.Error())
|
||||
time.Sleep(5 * time.Second)
|
||||
}
|
||||
}
|
||||
|
||||
func run() error {
|
||||
rtl, err := setupRTL8720DN()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
net.UseDriver(rtl)
|
||||
|
||||
err = rtl.ConnectToAP(ssid, password)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ip, subnet, gateway, err := rtl.GetIP()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("IP Address : %s\r\n", ip)
|
||||
fmt.Printf("Mask : %s\r\n", subnet)
|
||||
fmt.Printf("Gateway : %s\r\n", gateway)
|
||||
|
||||
// now make UDP connection
|
||||
hip := net.ParseIP(ntpHost)
|
||||
raddr := &net.UDPAddr{IP: hip, Port: 123}
|
||||
laddr := &net.UDPAddr{Port: 2390}
|
||||
conn, err := net.DialUDP("udp", laddr, raddr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for {
|
||||
// send data
|
||||
println("Requesting NTP time...")
|
||||
t, err := getCurrentTime(conn)
|
||||
if err != nil {
|
||||
message("Error getting current time: %v", err)
|
||||
} else {
|
||||
message("NTP time: %v", t)
|
||||
}
|
||||
runtime.AdjustTimeOffset(-1 * int64(time.Since(t)))
|
||||
for i := 0; i < 10; i++ {
|
||||
message("Current time: %v", time.Now())
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func getCurrentTime(conn *net.UDPSerialConn) (time.Time, error) {
|
||||
if err := sendNTPpacket(conn); err != nil {
|
||||
return time.Time{}, err
|
||||
}
|
||||
clearBuffer()
|
||||
for now := time.Now(); time.Since(now) < time.Second; {
|
||||
time.Sleep(5 * time.Millisecond)
|
||||
if n, err := conn.Read(b); err != nil {
|
||||
return time.Time{}, fmt.Errorf("error reading UDP packet: %w", err)
|
||||
} else if n == 0 {
|
||||
continue // no packet received yet
|
||||
} else if n != NTP_PACKET_SIZE {
|
||||
if n != NTP_PACKET_SIZE {
|
||||
return time.Time{}, fmt.Errorf("expected NTP packet size of %d: %d", NTP_PACKET_SIZE, n)
|
||||
}
|
||||
}
|
||||
return parseNTPpacket(), nil
|
||||
}
|
||||
return time.Time{}, errors.New("no packet received after 1 second")
|
||||
}
|
||||
|
||||
func sendNTPpacket(conn *net.UDPSerialConn) error {
|
||||
clearBuffer()
|
||||
b[0] = 0b11100011 // LI, Version, Mode
|
||||
b[1] = 0 // Stratum, or type of clock
|
||||
b[2] = 6 // Polling Interval
|
||||
b[3] = 0xEC // Peer Clock Precision
|
||||
// 8 bytes of zero for Root Delay & Root Dispersion
|
||||
b[12] = 49
|
||||
b[13] = 0x4E
|
||||
b[14] = 49
|
||||
b[15] = 52
|
||||
if _, err := conn.Write(b); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseNTPpacket() time.Time {
|
||||
// the timestamp starts at byte 40 of the received packet and is four bytes,
|
||||
// this is NTP time (seconds since Jan 1 1900):
|
||||
t := uint32(b[40])<<24 | uint32(b[41])<<16 | uint32(b[42])<<8 | uint32(b[43])
|
||||
const seventyYears = 2208988800
|
||||
return time.Unix(int64(t-seventyYears), 0)
|
||||
}
|
||||
|
||||
func clearBuffer() {
|
||||
for i := range b {
|
||||
b[i] = 0
|
||||
}
|
||||
}
|
||||
|
||||
func message(format string, args ...interface{}) {
|
||||
println(fmt.Sprintf(format, args...), "\r")
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
//go:build wioterminal
|
||||
// +build wioterminal
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"device/sam"
|
||||
"machine"
|
||||
"runtime/interrupt"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/rtl8720dn"
|
||||
)
|
||||
|
||||
var (
|
||||
uart UARTx
|
||||
)
|
||||
|
||||
func handleInterrupt(interrupt.Interrupt) {
|
||||
// should reset IRQ
|
||||
uart.Receive(byte((uart.Bus.DATA.Get() & 0xFF)))
|
||||
uart.Bus.INTFLAG.SetBits(sam.SERCOM_USART_INT_INTFLAG_RXC)
|
||||
}
|
||||
|
||||
func setupRTL8720DN() (*rtl8720dn.RTL8720DN, error) {
|
||||
machine.RTL8720D_CHIP_PU.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
machine.RTL8720D_CHIP_PU.Low()
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
machine.RTL8720D_CHIP_PU.High()
|
||||
time.Sleep(1000 * time.Millisecond)
|
||||
waitSerial()
|
||||
|
||||
uart = UARTx{
|
||||
UART: &machine.UART{
|
||||
Buffer: machine.NewRingBuffer(),
|
||||
Bus: sam.SERCOM0_USART_INT,
|
||||
SERCOM: 0,
|
||||
},
|
||||
}
|
||||
|
||||
uart.Interrupt = interrupt.New(sam.IRQ_SERCOM0_2, handleInterrupt)
|
||||
uart.Configure(machine.UARTConfig{TX: machine.PB24, RX: machine.PC24, BaudRate: 614400})
|
||||
|
||||
rtl := rtl8720dn.New(uart)
|
||||
rtl.Debug(debug)
|
||||
|
||||
_, err := rtl.Rpc_tcpip_adapter_init()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return rtl, nil
|
||||
}
|
||||
|
||||
// Wait for user to open serial console
|
||||
func waitSerial() {
|
||||
for !machine.Serial.DTR() {
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
|
||||
type UARTx struct {
|
||||
*machine.UART
|
||||
}
|
||||
|
||||
func (u UARTx) Read(p []byte) (n int, err error) {
|
||||
if u.Buffered() == 0 {
|
||||
time.Sleep(1 * time.Millisecond)
|
||||
return 0, nil
|
||||
}
|
||||
return u.UART.Read(p)
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
// This example opens a TCP connection using a device with RTL8720DN firmware
|
||||
// and sends some data, for the purpose of testing speed and connectivity.
|
||||
//
|
||||
// You can open a server to accept connections from this program using:
|
||||
//
|
||||
// nc -w 5 -lk 8080
|
||||
//
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/net"
|
||||
)
|
||||
|
||||
// You can override the setting with the init() in another source code.
|
||||
// func init() {
|
||||
// ssid = "sghome-gw"
|
||||
// password = "3af25537b4524"
|
||||
// serverIP = "192.168.1.119"
|
||||
// debug = true
|
||||
// }
|
||||
|
||||
var (
|
||||
ssid string
|
||||
password string
|
||||
serverIP = ""
|
||||
debug = false
|
||||
)
|
||||
|
||||
var buf = &bytes.Buffer{}
|
||||
|
||||
func main() {
|
||||
err := run()
|
||||
for err != nil {
|
||||
fmt.Printf("error: %s\r\n", err.Error())
|
||||
time.Sleep(5 * time.Second)
|
||||
}
|
||||
}
|
||||
|
||||
func run() error {
|
||||
rtl, err := setupRTL8720DN()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
net.UseDriver(rtl)
|
||||
|
||||
err = rtl.ConnectToAP(ssid, password)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ip, subnet, gateway, err := rtl.GetIP()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("IP Address : %s\r\n", ip)
|
||||
fmt.Printf("Mask : %s\r\n", subnet)
|
||||
fmt.Printf("Gateway : %s\r\n", gateway)
|
||||
|
||||
for {
|
||||
sendBatch()
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
}
|
||||
println("Done.")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func sendBatch() {
|
||||
|
||||
// make TCP connection
|
||||
ip := net.ParseIP(serverIP)
|
||||
raddr := &net.TCPAddr{IP: ip, Port: 8080}
|
||||
laddr := &net.TCPAddr{Port: 8080}
|
||||
|
||||
message("---------------\r\nDialing TCP connection")
|
||||
conn, err := net.DialTCP("tcp", laddr, raddr)
|
||||
for ; err != nil; conn, err = net.DialTCP("tcp", laddr, raddr) {
|
||||
message(err.Error())
|
||||
time.Sleep(5 * time.Second)
|
||||
}
|
||||
|
||||
n := 0
|
||||
w := 0
|
||||
start := time.Now()
|
||||
|
||||
// send data
|
||||
message("Sending data")
|
||||
|
||||
for i := 0; i < 1000; i++ {
|
||||
buf.Reset()
|
||||
fmt.Fprint(buf,
|
||||
"\r---------------------------- i == ", i, " ----------------------------"+
|
||||
"\r---------------------------- i == ", i, " ----------------------------")
|
||||
if w, err = conn.Write(buf.Bytes()); err != nil {
|
||||
println("error:", err.Error(), "\r")
|
||||
continue
|
||||
}
|
||||
n += w
|
||||
}
|
||||
|
||||
buf.Reset()
|
||||
ms := time.Now().Sub(start).Milliseconds()
|
||||
fmt.Fprint(buf, "\nWrote ", n, " bytes in ", ms, " ms\r\n")
|
||||
message(buf.String())
|
||||
|
||||
if _, err := conn.Write(buf.Bytes()); err != nil {
|
||||
println("error:", err.Error(), "\r")
|
||||
}
|
||||
|
||||
// Right now this code is never reached. Need a way to trigger it...
|
||||
println("Disconnecting TCP...")
|
||||
conn.Close()
|
||||
}
|
||||
|
||||
func message(msg string) {
|
||||
println(msg, "\r")
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
//go:build wioterminal
|
||||
// +build wioterminal
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"device/sam"
|
||||
"machine"
|
||||
"runtime/interrupt"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/rtl8720dn"
|
||||
)
|
||||
|
||||
var (
|
||||
uart UARTx
|
||||
)
|
||||
|
||||
func handleInterrupt(interrupt.Interrupt) {
|
||||
// should reset IRQ
|
||||
uart.Receive(byte((uart.Bus.DATA.Get() & 0xFF)))
|
||||
uart.Bus.INTFLAG.SetBits(sam.SERCOM_USART_INT_INTFLAG_RXC)
|
||||
}
|
||||
|
||||
func setupRTL8720DN() (*rtl8720dn.RTL8720DN, error) {
|
||||
machine.RTL8720D_CHIP_PU.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
machine.RTL8720D_CHIP_PU.Low()
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
machine.RTL8720D_CHIP_PU.High()
|
||||
time.Sleep(1000 * time.Millisecond)
|
||||
waitSerial()
|
||||
|
||||
uart = UARTx{
|
||||
UART: &machine.UART{
|
||||
Buffer: machine.NewRingBuffer(),
|
||||
Bus: sam.SERCOM0_USART_INT,
|
||||
SERCOM: 0,
|
||||
},
|
||||
}
|
||||
|
||||
uart.Interrupt = interrupt.New(sam.IRQ_SERCOM0_2, handleInterrupt)
|
||||
uart.Configure(machine.UARTConfig{TX: machine.PB24, RX: machine.PC24, BaudRate: 614400})
|
||||
|
||||
rtl := rtl8720dn.New(uart)
|
||||
rtl.Debug(debug)
|
||||
|
||||
_, err := rtl.Rpc_tcpip_adapter_init()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return rtl, nil
|
||||
}
|
||||
|
||||
// Wait for user to open serial console
|
||||
func waitSerial() {
|
||||
for !machine.Serial.DTR() {
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
|
||||
type UARTx struct {
|
||||
*machine.UART
|
||||
}
|
||||
|
||||
func (u UARTx) Read(p []byte) (n int, err error) {
|
||||
if u.Buffered() == 0 {
|
||||
time.Sleep(1 * time.Millisecond)
|
||||
return 0, nil
|
||||
}
|
||||
return u.UART.Read(p)
|
||||
}
|
||||
@@ -1,12 +1,13 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/net"
|
||||
"tinygo.org/x/drivers/net/tls"
|
||||
"tinygo.org/x/drivers/net/http"
|
||||
"tinygo.org/x/drivers/rtl8720dn"
|
||||
)
|
||||
|
||||
@@ -15,14 +16,14 @@ import (
|
||||
// ssid = "your-ssid"
|
||||
// password = "your-password"
|
||||
// debug = true
|
||||
// server = "tinygo.org"
|
||||
// url = "https://www.example.com"
|
||||
// test_root_ca = "..."
|
||||
// }
|
||||
|
||||
var (
|
||||
ssid string
|
||||
password string
|
||||
server string = "www.example.com"
|
||||
url string = "https://www.example.com"
|
||||
debug = false
|
||||
)
|
||||
|
||||
@@ -52,7 +53,7 @@ CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4=
|
||||
-----END CERTIFICATE-----
|
||||
`
|
||||
|
||||
var buf [0x400]byte
|
||||
var buf [0x1000]byte
|
||||
|
||||
var lastRequestTime time.Time
|
||||
var conn net.Conn
|
||||
@@ -73,6 +74,7 @@ func run() error {
|
||||
}
|
||||
rtl.SetRootCA(&test_root_ca)
|
||||
net.UseDriver(rtl)
|
||||
http.SetBuf(buf[:])
|
||||
|
||||
err = rtl.ConnectToAP(ssid, password)
|
||||
if err != nil {
|
||||
@@ -87,55 +89,49 @@ func run() error {
|
||||
fmt.Printf("Mask : %s\r\n", subnet)
|
||||
fmt.Printf("Gateway : %s\r\n", gateway)
|
||||
|
||||
// You can send and receive cookies in the following way
|
||||
// import "tinygo.org/x/drivers/net/http/cookiejar"
|
||||
// jar, err := cookiejar.New(nil)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// client := &http.Client{Jar: jar}
|
||||
// http.DefaultClient = client
|
||||
|
||||
cnt := 0
|
||||
for {
|
||||
readConnection()
|
||||
if time.Now().Sub(lastRequestTime).Milliseconds() >= 10000 {
|
||||
makeHTTPSRequest()
|
||||
cnt++
|
||||
fmt.Printf("-------- %d --------\r\n", cnt)
|
||||
// Various examples are as follows
|
||||
//
|
||||
// -- Get
|
||||
// resp, err := http.Get(url)
|
||||
//
|
||||
// -- Post
|
||||
// body := `cnt=12`
|
||||
// resp, err = http.Post(url, "application/x-www-form-urlencoded", strings.NewReader(body))
|
||||
//
|
||||
// -- Post with JSON
|
||||
// body := `{"msg": "hello"}`
|
||||
// resp, err := http.Post(url, "application/json", strings.NewReader(body))
|
||||
|
||||
resp, err := http.Get(url)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func readConnection() {
|
||||
if conn != nil {
|
||||
for n, err := conn.Read(buf[:]); n > 0; n, err = conn.Read(buf[:]) {
|
||||
if err != nil {
|
||||
println("Read error: " + err.Error())
|
||||
} else {
|
||||
print(string(buf[0:n]))
|
||||
}
|
||||
fmt.Printf("%s %s\r\n", resp.Proto, resp.Status)
|
||||
for k, v := range resp.Header {
|
||||
fmt.Printf("%s: %s\r\n", k, strings.Join(v, " "))
|
||||
}
|
||||
fmt.Printf("\r\n")
|
||||
|
||||
scanner := bufio.NewScanner(resp.Body)
|
||||
for scanner.Scan() {
|
||||
fmt.Printf("%s\r\n", scanner.Text())
|
||||
}
|
||||
resp.Body.Close()
|
||||
|
||||
cnt++
|
||||
fmt.Printf("-------- %d --------\r\n", cnt)
|
||||
time.Sleep(10 * time.Second)
|
||||
}
|
||||
}
|
||||
|
||||
func makeHTTPSRequest() {
|
||||
|
||||
var err error
|
||||
if conn != nil {
|
||||
conn.Close()
|
||||
}
|
||||
|
||||
message("\r\n---------------\r\nDialing TCP connection")
|
||||
conn, err = tls.Dial("tcp", server, nil)
|
||||
for ; err != nil; conn, err = tls.Dial("tcp", server, nil) {
|
||||
message("Connection failed: " + err.Error())
|
||||
time.Sleep(5 * time.Second)
|
||||
}
|
||||
println("Connected!\r")
|
||||
|
||||
print("Sending HTTPS request...")
|
||||
fmt.Fprintln(conn, "GET / HTTP/1.1")
|
||||
fmt.Fprintln(conn, "Host:", strings.Split(server, ":")[0])
|
||||
fmt.Fprintln(conn, "User-Agent: TinyGo")
|
||||
fmt.Fprintln(conn, "Connection: close")
|
||||
fmt.Fprintln(conn)
|
||||
println("Sent!\r\n\r")
|
||||
|
||||
lastRequestTime = time.Now()
|
||||
}
|
||||
|
||||
func message(msg string) {
|
||||
println(msg, "\r")
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
//go:build wioterminal
|
||||
// +build wioterminal
|
||||
|
||||
package main
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/net"
|
||||
"tinygo.org/x/drivers/net/http"
|
||||
)
|
||||
|
||||
// IP address of the server aka "hub". Replace with your own info.
|
||||
// You can override the setting with the init() in another source code.
|
||||
// func init() {
|
||||
// ssid = "your-ssid"
|
||||
// password = "your-password"
|
||||
// hubIP = "192.168.1.118"
|
||||
// debug = true
|
||||
// }
|
||||
|
||||
var (
|
||||
ssid string
|
||||
password string
|
||||
hubIP = ""
|
||||
debug = false
|
||||
)
|
||||
|
||||
var buf [0x400]byte
|
||||
|
||||
func main() {
|
||||
err := run()
|
||||
for err != nil {
|
||||
fmt.Printf("error: %s\r\n", err.Error())
|
||||
time.Sleep(5 * time.Second)
|
||||
}
|
||||
}
|
||||
|
||||
func run() error {
|
||||
rtl, err := setupRTL8720DN()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
net.UseDriver(rtl)
|
||||
http.SetBuf(buf[:])
|
||||
|
||||
err = rtl.ConnectToAP(ssid, password)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ip, subnet, gateway, err := rtl.GetIP()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("IP Address : %s\r\n", ip)
|
||||
fmt.Printf("Mask : %s\r\n", subnet)
|
||||
fmt.Printf("Gateway : %s\r\n", gateway)
|
||||
|
||||
// now make UDP connection
|
||||
hip := net.ParseIP(hubIP)
|
||||
raddr := &net.UDPAddr{IP: hip, Port: 2222}
|
||||
laddr := &net.UDPAddr{Port: 2222}
|
||||
|
||||
println("Dialing UDP connection...")
|
||||
conn, err := net.DialUDP("udp", laddr, raddr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for {
|
||||
// send data
|
||||
println("Sending data...")
|
||||
for i := 0; i < 25; i++ {
|
||||
conn.Write([]byte("hello " + strconv.Itoa(i) + "\r\n"))
|
||||
}
|
||||
time.Sleep(1000 * time.Millisecond)
|
||||
}
|
||||
|
||||
// Right now this code is never reached. Need a way to trigger it...
|
||||
println("Disconnecting UDP...")
|
||||
conn.Close()
|
||||
println("Done.")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func message(msg string) {
|
||||
println(msg, "\r")
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
//go:build wioterminal
|
||||
// +build wioterminal
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"device/sam"
|
||||
"machine"
|
||||
"runtime/interrupt"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/rtl8720dn"
|
||||
)
|
||||
|
||||
var (
|
||||
uart UARTx
|
||||
)
|
||||
|
||||
func handleInterrupt(interrupt.Interrupt) {
|
||||
// should reset IRQ
|
||||
uart.Receive(byte((uart.Bus.DATA.Get() & 0xFF)))
|
||||
uart.Bus.INTFLAG.SetBits(sam.SERCOM_USART_INT_INTFLAG_RXC)
|
||||
}
|
||||
|
||||
func setupRTL8720DN() (*rtl8720dn.RTL8720DN, error) {
|
||||
machine.RTL8720D_CHIP_PU.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
machine.RTL8720D_CHIP_PU.Low()
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
machine.RTL8720D_CHIP_PU.High()
|
||||
time.Sleep(1000 * time.Millisecond)
|
||||
waitSerial()
|
||||
|
||||
uart = UARTx{
|
||||
UART: &machine.UART{
|
||||
Buffer: machine.NewRingBuffer(),
|
||||
Bus: sam.SERCOM0_USART_INT,
|
||||
SERCOM: 0,
|
||||
},
|
||||
}
|
||||
|
||||
uart.Interrupt = interrupt.New(sam.IRQ_SERCOM0_2, handleInterrupt)
|
||||
uart.Configure(machine.UARTConfig{TX: machine.PB24, RX: machine.PC24, BaudRate: 614400})
|
||||
|
||||
rtl := rtl8720dn.New(uart)
|
||||
rtl.Debug(debug)
|
||||
|
||||
_, err := rtl.Rpc_tcpip_adapter_init()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return rtl, nil
|
||||
}
|
||||
|
||||
// Wait for user to open serial console
|
||||
func waitSerial() {
|
||||
for !machine.Serial.DTR() {
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
|
||||
type UARTx struct {
|
||||
*machine.UART
|
||||
}
|
||||
|
||||
func (u UARTx) Read(p []byte) (n int, err error) {
|
||||
if u.Buffered() == 0 {
|
||||
time.Sleep(1 * time.Millisecond)
|
||||
return 0, nil
|
||||
}
|
||||
return u.UART.Read(p)
|
||||
}
|
||||
@@ -1,29 +1,32 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"image/color"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/net"
|
||||
"tinygo.org/x/drivers/rtl8720dn"
|
||||
"tinygo.org/x/drivers/net/http"
|
||||
"tinygo.org/x/tinyfont/proggy"
|
||||
"tinygo.org/x/tinyterm"
|
||||
)
|
||||
|
||||
// You can override the setting with the init() in another source code.
|
||||
// If debug is enabled, a serial connection is required.
|
||||
// func init() {
|
||||
// ssid = "your-ssid"
|
||||
// password = "your-password"
|
||||
// debug = true
|
||||
// debug = false // true
|
||||
// server = "tinygo.org"
|
||||
// }
|
||||
|
||||
var (
|
||||
ssid string
|
||||
password string
|
||||
server string = "tinygo.org"
|
||||
debug = false
|
||||
url = "http://tinygo.org/"
|
||||
debug = false
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -40,10 +43,6 @@ var (
|
||||
|
||||
var buf [0x400]byte
|
||||
|
||||
var lastRequestTime time.Time
|
||||
var conn net.Conn
|
||||
var adaptor *rtl8720dn.RTL8720DN
|
||||
|
||||
func main() {
|
||||
display.FillScreen(black)
|
||||
backlight.High()
|
||||
@@ -63,11 +62,16 @@ func main() {
|
||||
|
||||
func run() error {
|
||||
fmt.Fprintf(terminal, "setupRTL8720DN()\r\n")
|
||||
if debug {
|
||||
fmt.Fprintf(terminal, "Running in debug mode.\r\n")
|
||||
fmt.Fprintf(terminal, "A serial connection is required to continue execution.\r\n")
|
||||
}
|
||||
rtl, err := setupRTL8720DN()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
net.UseDriver(rtl)
|
||||
http.SetBuf(buf[:])
|
||||
|
||||
fmt.Fprintf(terminal, "ConnectToAP()\r\n")
|
||||
err = rtl.ConnectToAP(ssid, password)
|
||||
@@ -84,60 +88,49 @@ func run() error {
|
||||
fmt.Fprintf(terminal, "Mask : %s\r\n", subnet)
|
||||
fmt.Fprintf(terminal, "Gateway : %s\r\n", gateway)
|
||||
|
||||
// You can send and receive cookies in the following way
|
||||
// import "tinygo.org/x/drivers/net/http/cookiejar"
|
||||
// jar, err := cookiejar.New(nil)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// client := &http.Client{Jar: jar}
|
||||
// http.DefaultClient = client
|
||||
|
||||
cnt := 0
|
||||
for {
|
||||
readConnection()
|
||||
if time.Now().Sub(lastRequestTime).Milliseconds() >= 10000 {
|
||||
makeHTTPRequest()
|
||||
cnt++
|
||||
fmt.Fprintf(terminal, "-------- %d --------\r\n", cnt)
|
||||
// Various examples are as follows
|
||||
//
|
||||
// -- Get
|
||||
// resp, err := http.Get(url)
|
||||
//
|
||||
// -- Post
|
||||
// body := `cnt=12`
|
||||
// resp, err = http.Post(url, "application/x-www-form-urlencoded", strings.NewReader(body))
|
||||
//
|
||||
// -- Post with JSON
|
||||
// body := `{"msg": "hello"}`
|
||||
// resp, err := http.Post(url, "application/json", strings.NewReader(body))
|
||||
|
||||
resp, err := http.Get(url)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func readConnection() {
|
||||
if conn != nil {
|
||||
for n, err := conn.Read(buf[:]); n > 0; n, err = conn.Read(buf[:]) {
|
||||
if err != nil {
|
||||
fmt.Fprintf(terminal, "Read error: "+err.Error()+"\r\n")
|
||||
} else {
|
||||
fmt.Fprintf(terminal, string(buf[0:n]))
|
||||
}
|
||||
fmt.Fprintf(terminal, "%s %s\r\n", resp.Proto, resp.Status)
|
||||
for k, v := range resp.Header {
|
||||
fmt.Fprintf(terminal, "%s: %s\r\n", k, strings.Join(v, " "))
|
||||
}
|
||||
fmt.Printf("\r\n")
|
||||
|
||||
scanner := bufio.NewScanner(resp.Body)
|
||||
for scanner.Scan() {
|
||||
fmt.Fprintf(terminal, "%s\r\n", scanner.Text())
|
||||
}
|
||||
resp.Body.Close()
|
||||
|
||||
cnt++
|
||||
fmt.Fprintf(terminal, "-------- %d --------\r\n", cnt)
|
||||
time.Sleep(10 * time.Second)
|
||||
}
|
||||
}
|
||||
|
||||
func makeHTTPRequest() {
|
||||
|
||||
var err error
|
||||
if conn != nil {
|
||||
conn.Close()
|
||||
}
|
||||
|
||||
// make TCP connection
|
||||
ip := net.ParseIP(server)
|
||||
raddr := &net.TCPAddr{IP: ip, Port: 80}
|
||||
laddr := &net.TCPAddr{Port: 8080}
|
||||
|
||||
message("\r\n---------------\r\nDialing TCP connection")
|
||||
conn, err = net.DialTCP("tcp", laddr, raddr)
|
||||
for ; err != nil; conn, err = net.DialTCP("tcp", laddr, raddr) {
|
||||
message("Connection failed: " + err.Error())
|
||||
time.Sleep(5 * time.Second)
|
||||
}
|
||||
fmt.Fprintf(terminal, "Connected!\r\n")
|
||||
|
||||
fmt.Fprintf(terminal, "Sending HTTP request...")
|
||||
fmt.Fprintln(conn, "GET / HTTP/1.1")
|
||||
fmt.Fprintln(conn, "Host:", server)
|
||||
fmt.Fprintln(conn, "User-Agent: TinyGo")
|
||||
fmt.Fprintln(conn, "Connection: close")
|
||||
fmt.Fprintln(conn)
|
||||
fmt.Fprintf(terminal, "Sent!\r\n\r\n")
|
||||
|
||||
lastRequestTime = time.Now()
|
||||
}
|
||||
|
||||
func message(msg string) {
|
||||
fmt.Fprintf(terminal, "%s\r\n", msg)
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
//go:build wioterminal
|
||||
// +build wioterminal
|
||||
|
||||
package main
|
||||
|
||||
@@ -1,34 +1,32 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/net"
|
||||
"tinygo.org/x/drivers/rtl8720dn"
|
||||
"tinygo.org/x/drivers/net/http"
|
||||
)
|
||||
|
||||
// You can override the setting with the init() in another source code.
|
||||
// func init() {
|
||||
// ssid = "your-ssid"
|
||||
// password = "your-password"
|
||||
// url = "http://tinygo.org/"
|
||||
// debug = true
|
||||
// server = "tinygo.org"
|
||||
// }
|
||||
|
||||
var (
|
||||
ssid string
|
||||
password string
|
||||
server string = "tinygo.org"
|
||||
debug = false
|
||||
url = "http://tinygo.org/"
|
||||
debug = false
|
||||
)
|
||||
|
||||
var buf [0x400]byte
|
||||
|
||||
var lastRequestTime time.Time
|
||||
var conn net.Conn
|
||||
var adaptor *rtl8720dn.RTL8720DN
|
||||
|
||||
func main() {
|
||||
err := run()
|
||||
for err != nil {
|
||||
@@ -43,6 +41,7 @@ func run() error {
|
||||
return err
|
||||
}
|
||||
net.UseDriver(rtl)
|
||||
http.SetBuf(buf[:])
|
||||
|
||||
err = rtl.ConnectToAP(ssid, password)
|
||||
if err != nil {
|
||||
@@ -57,60 +56,49 @@ func run() error {
|
||||
fmt.Printf("Mask : %s\r\n", subnet)
|
||||
fmt.Printf("Gateway : %s\r\n", gateway)
|
||||
|
||||
// You can send and receive cookies in the following way
|
||||
// import "tinygo.org/x/drivers/net/http/cookiejar"
|
||||
// jar, err := cookiejar.New(nil)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// client := &http.Client{Jar: jar}
|
||||
// http.DefaultClient = client
|
||||
|
||||
cnt := 0
|
||||
for {
|
||||
readConnection()
|
||||
if time.Now().Sub(lastRequestTime).Milliseconds() >= 10000 {
|
||||
makeHTTPRequest()
|
||||
cnt++
|
||||
fmt.Printf("-------- %d --------\r\n", cnt)
|
||||
// Various examples are as follows
|
||||
//
|
||||
// -- Get
|
||||
// resp, err := http.Get(url)
|
||||
//
|
||||
// -- Post
|
||||
// body := `cnt=12`
|
||||
// resp, err = http.Post(url, "application/x-www-form-urlencoded", strings.NewReader(body))
|
||||
//
|
||||
// -- Post with JSON
|
||||
// body := `{"msg": "hello"}`
|
||||
// resp, err := http.Post(url, "application/json", strings.NewReader(body))
|
||||
|
||||
resp, err := http.Get(url)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func readConnection() {
|
||||
if conn != nil {
|
||||
for n, err := conn.Read(buf[:]); n > 0; n, err = conn.Read(buf[:]) {
|
||||
if err != nil {
|
||||
println("Read error: " + err.Error())
|
||||
} else {
|
||||
print(string(buf[0:n]))
|
||||
}
|
||||
fmt.Printf("%s %s\r\n", resp.Proto, resp.Status)
|
||||
for k, v := range resp.Header {
|
||||
fmt.Printf("%s: %s\r\n", k, strings.Join(v, " "))
|
||||
}
|
||||
fmt.Printf("\r\n")
|
||||
|
||||
scanner := bufio.NewScanner(resp.Body)
|
||||
for scanner.Scan() {
|
||||
fmt.Printf("%s\r\n", scanner.Text())
|
||||
}
|
||||
resp.Body.Close()
|
||||
|
||||
cnt++
|
||||
fmt.Printf("-------- %d --------\r\n", cnt)
|
||||
time.Sleep(10 * time.Second)
|
||||
}
|
||||
}
|
||||
|
||||
func makeHTTPRequest() {
|
||||
|
||||
var err error
|
||||
if conn != nil {
|
||||
conn.Close()
|
||||
}
|
||||
|
||||
// make TCP connection
|
||||
ip := net.ParseIP(server)
|
||||
raddr := &net.TCPAddr{IP: ip, Port: 80}
|
||||
laddr := &net.TCPAddr{Port: 8080}
|
||||
|
||||
message("\r\n---------------\r\nDialing TCP connection")
|
||||
conn, err = net.DialTCP("tcp", laddr, raddr)
|
||||
for ; err != nil; conn, err = net.DialTCP("tcp", laddr, raddr) {
|
||||
message("Connection failed: " + err.Error())
|
||||
time.Sleep(5 * time.Second)
|
||||
}
|
||||
println("Connected!\r")
|
||||
|
||||
print("Sending HTTP request...")
|
||||
fmt.Fprintln(conn, "GET / HTTP/1.1")
|
||||
fmt.Fprintln(conn, "Host:", server)
|
||||
fmt.Fprintln(conn, "User-Agent: TinyGo")
|
||||
fmt.Fprintln(conn, "Connection: close")
|
||||
fmt.Fprintln(conn)
|
||||
println("Sent!\r\n\r")
|
||||
|
||||
lastRequestTime = time.Now()
|
||||
}
|
||||
|
||||
func message(msg string) {
|
||||
println(msg, "\r")
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
//go:build wioterminal
|
||||
// +build wioterminal
|
||||
|
||||
package main
|
||||
|
||||
@@ -0,0 +1,192 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"machine"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/net/http"
|
||||
)
|
||||
|
||||
// You can override the setting with the init() in another source code.
|
||||
// func init() {
|
||||
// ssid = "your-ssid"
|
||||
// password = "your-password"
|
||||
// debug = true
|
||||
// }
|
||||
|
||||
var (
|
||||
ssid string
|
||||
password string
|
||||
debug = false
|
||||
)
|
||||
|
||||
var led = machine.LED
|
||||
var backlight = machine.LCD_BACKLIGHT
|
||||
|
||||
func main() {
|
||||
led.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
backlight.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
|
||||
err := run()
|
||||
for err != nil {
|
||||
fmt.Printf("error: %s\r\n", err.Error())
|
||||
time.Sleep(5 * time.Second)
|
||||
}
|
||||
}
|
||||
|
||||
func run() error {
|
||||
rtl, err := setupRTL8720DN()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
http.UseDriver(rtl)
|
||||
|
||||
err = rtl.ConnectToAP(ssid, password)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ip, subnet, gateway, err := rtl.GetIP()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("IP Address : %s\r\n", ip)
|
||||
fmt.Printf("Mask : %s\r\n", subnet)
|
||||
fmt.Printf("Gateway : %s\r\n", gateway)
|
||||
|
||||
http.HandleFunc("/", root)
|
||||
http.HandleFunc("/hello", hello)
|
||||
http.HandleFunc("/cnt", cnt)
|
||||
http.HandleFunc("/6", sixlines)
|
||||
http.HandleFunc("/off", LED_OFF)
|
||||
http.HandleFunc("/on", LED_ON)
|
||||
if err := http.ListenAndServe(":80", nil); err != nil {
|
||||
message(err.Error())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func root(w http.ResponseWriter, r *http.Request) {
|
||||
access := 1
|
||||
|
||||
cookie, err := r.Cookie("access")
|
||||
if err != nil {
|
||||
if err == http.ErrNoCookie {
|
||||
cookie = &http.Cookie{
|
||||
Name: "access",
|
||||
Value: "1",
|
||||
}
|
||||
} else {
|
||||
http.Error(w, fmt.Sprintf("%s", err.Error()), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
v, err := strconv.ParseInt(cookie.Value, 10, 0)
|
||||
if err != nil {
|
||||
http.Error(w, fmt.Sprintf("invalid cookie.Value : %s", cookie.Value), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
cookie.Value = fmt.Sprintf("%d", v+1)
|
||||
access = int(v) + 1
|
||||
}
|
||||
http.SetCookie(w, cookie)
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
fmt.Fprintf(w, `
|
||||
<html>
|
||||
<head>
|
||||
<title>TinyGo HTTP Server</title>
|
||||
<script language="javascript" type="text/javascript">
|
||||
var counter = 0
|
||||
function ledOn() { fetch("/on").then(response => response.text()).then(text => { led.innerHTML = "<p>on</p>"; }); }
|
||||
function ledOff() { fetch("/off").then(response => response.text()).then(text => { led.innerHTML = "<p>off</p>"; }); }
|
||||
function fetchCnt() { fetch("/cnt").then(response => response.json()).then(json => { counter = json.cnt; cnt.innerHTML = counter; }); }
|
||||
function incrCnt() { counter = counter + 1; fetch("/cnt?cnt=" + counter, { method: 'POST' }).then(response => response.json()).then(json => { counter = json.cnt; cnt.innerHTML = counter; }); }
|
||||
function setCnt() { fetch("/cnt", {
|
||||
method: "POST",
|
||||
body: "cnt=" + document.getElementsByName("cnt")[0].value,
|
||||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
||||
}).then(response => response.json()).then(json => { counter = json.cnt; cnt.innerHTML = counter; }); return false; }
|
||||
function onLoad() { fetchCnt(); }
|
||||
</script>
|
||||
</head>
|
||||
<body onLoad="onLoad()">
|
||||
<h5>TinyGo HTTP Server</h5>
|
||||
|
||||
<p>
|
||||
access: %d
|
||||
</p>
|
||||
|
||||
<a href="/hello">/hello</a><br>
|
||||
<a href="/6">/6</a><br>
|
||||
|
||||
<p>
|
||||
LED<br>
|
||||
<a href="javascript:ledOn();">/on</a><br>
|
||||
<a href="javascript:ledOff();">/off</a><br>
|
||||
</p>
|
||||
|
||||
|
||||
<p>
|
||||
<a href="/cnt">/cnt</a><br>
|
||||
cnt: <span id="cnt"></span><br>
|
||||
<a href="javascript:incrCnt()">incrCnt()</a><br>
|
||||
<form id="form1" style="display: inline" onSubmit="return setCnt()">
|
||||
<input type="text" name="cnt">
|
||||
<input type="button" value="set cnt", onClick="setCnt()">
|
||||
</form>
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
`, access)
|
||||
}
|
||||
|
||||
func sixlines(w http.ResponseWriter, r *http.Request) {
|
||||
// https://fukuno.jig.jp/3267
|
||||
fmt.Fprint(w, `<body onload='onkeydown=e=>K=parseInt(e.key[5]||6,28)/3-8;Z=X=[B=A=12];Y=_=>`+
|
||||
`{for(C=[q=c=i=4];f=i--*K;c-=!Z[h+(K+6?p+K:C[i]=p*A-(p/9|0)*145)])p=B[i];for(c?0:K+6?h+=K:B=C;`+
|
||||
`i=K=q--;f+=Z[A+p])X[p=h+B[q]]=1;h+=A;if(f|B)for(Z=X,X=[l=228],B=[[-7,-20,6,h=17,-9,3,3][t=++t%7]-4,0,1,t-6?-A:2];l--;)`+
|
||||
`for(l%A?l-=l%A*!Z[l]:(P++,c=l+=A);--c>A;)Z[c]=Z[c-A];for(S="";i<240;S+=X[i]|(X[i]=Z[i]|=++i%A<2|i>228)?i%A?"■":"■<br>":" ");`+
|
||||
`D.innerHTML=S+P;setTimeout(Y,i-P)};Y(h=K=t=P=0)'id=D>`)
|
||||
}
|
||||
|
||||
func LED_ON(w http.ResponseWriter, r *http.Request) {
|
||||
led.High()
|
||||
backlight.High()
|
||||
w.Header().Set(`Content-Type`, `text/plain; charset=UTF-8`)
|
||||
fmt.Fprintf(w, "led.High()")
|
||||
}
|
||||
|
||||
func LED_OFF(w http.ResponseWriter, r *http.Request) {
|
||||
led.Low()
|
||||
backlight.Low()
|
||||
w.Header().Set(`Content-Type`, `text/plain; charset=UTF-8`)
|
||||
fmt.Fprintf(w, "led.Low()")
|
||||
}
|
||||
|
||||
func hello(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set(`Content-Type`, `text/plain; charset=UTF-8`)
|
||||
fmt.Fprintf(w, "hello")
|
||||
}
|
||||
|
||||
var counter int
|
||||
|
||||
func cnt(w http.ResponseWriter, r *http.Request) {
|
||||
r.ParseForm()
|
||||
if r.Method == "POST" {
|
||||
c := r.Form.Get("cnt")
|
||||
if c != "" {
|
||||
i64, _ := strconv.ParseInt(c, 0, 0)
|
||||
counter = int(i64)
|
||||
}
|
||||
}
|
||||
|
||||
w.Header().Set(`Content-Type`, `application/json`)
|
||||
fmt.Fprintf(w, `{"cnt": %d}`, counter)
|
||||
}
|
||||
|
||||
func message(msg string) {
|
||||
println(msg, "\r")
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
//go:build wioterminal
|
||||
// +build wioterminal
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"device/sam"
|
||||
"machine"
|
||||
"runtime/interrupt"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/rtl8720dn"
|
||||
)
|
||||
|
||||
var (
|
||||
uart UARTx
|
||||
)
|
||||
|
||||
func handleInterrupt(interrupt.Interrupt) {
|
||||
// should reset IRQ
|
||||
uart.Receive(byte((uart.Bus.DATA.Get() & 0xFF)))
|
||||
uart.Bus.INTFLAG.SetBits(sam.SERCOM_USART_INT_INTFLAG_RXC)
|
||||
}
|
||||
|
||||
func setupRTL8720DN() (*rtl8720dn.RTL8720DN, error) {
|
||||
machine.RTL8720D_CHIP_PU.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
machine.RTL8720D_CHIP_PU.Low()
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
machine.RTL8720D_CHIP_PU.High()
|
||||
time.Sleep(1000 * time.Millisecond)
|
||||
if debug {
|
||||
waitSerial()
|
||||
}
|
||||
|
||||
uart = UARTx{
|
||||
UART: &machine.UART{
|
||||
Buffer: machine.NewRingBuffer(),
|
||||
Bus: sam.SERCOM0_USART_INT,
|
||||
SERCOM: 0,
|
||||
},
|
||||
}
|
||||
|
||||
uart.Interrupt = interrupt.New(sam.IRQ_SERCOM0_2, handleInterrupt)
|
||||
uart.Configure(machine.UARTConfig{TX: machine.PB24, RX: machine.PC24, BaudRate: 614400})
|
||||
|
||||
rtl := rtl8720dn.New(uart)
|
||||
rtl.Debug(debug)
|
||||
|
||||
_, err := rtl.Rpc_tcpip_adapter_init()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return rtl, nil
|
||||
}
|
||||
|
||||
// Wait for user to open serial console
|
||||
func waitSerial() {
|
||||
for !machine.Serial.DTR() {
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
|
||||
type UARTx struct {
|
||||
*machine.UART
|
||||
}
|
||||
|
||||
func (u UARTx) Read(p []byte) (n int, err error) {
|
||||
if u.Buffered() == 0 {
|
||||
time.Sleep(1 * time.Millisecond)
|
||||
return 0, nil
|
||||
}
|
||||
return u.UART.Read(p)
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
//go:build feather_m4 || feather_m4_can || feather_nrf52840
|
||||
// +build feather_m4 feather_m4_can feather_nrf52840
|
||||
|
||||
package main
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// +build grandcentral-m4
|
||||
//go:build grandcentral_m4
|
||||
// +build grandcentral_m4
|
||||
|
||||
package main
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
//go:build atsamd21 && !p1am_100
|
||||
// +build atsamd21,!p1am_100
|
||||
|
||||
package main
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
//go:build p1am_100
|
||||
// +build p1am_100
|
||||
|
||||
package main
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
//go:build pygamer
|
||||
// +build pygamer
|
||||
|
||||
package main
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
//go:build pyportal
|
||||
// +build pyportal
|
||||
|
||||
package main
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
//go:build wioterminal
|
||||
// +build wioterminal
|
||||
|
||||
package main
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
//go:build tinygo
|
||||
// +build tinygo
|
||||
|
||||
package console
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
//go:build feather_m4 || feather_m4_can || feather_nrf52840
|
||||
// +build feather_m4 feather_m4_can feather_nrf52840
|
||||
|
||||
package main
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// +build grandcentral-m4
|
||||
//go:build grandcentral_m4
|
||||
// +build grandcentral_m4
|
||||
|
||||
package main
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
//go:build atsamd21 && !p1am_100
|
||||
// +build atsamd21,!p1am_100
|
||||
|
||||
package main
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
//go:build p1am_100
|
||||
// +build p1am_100
|
||||
|
||||
package main
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
//go:build pygamer
|
||||
// +build pygamer
|
||||
|
||||
package main
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
//go:build pyportal
|
||||
// +build pyportal
|
||||
|
||||
package main
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
//go:build wioterminal
|
||||
// +build wioterminal
|
||||
|
||||
package main
|
||||
|
||||
@@ -0,0 +1,161 @@
|
||||
// This example opens a TCP connection using a device with WiFiNINA firmware
|
||||
// and sends a HTTP request to retrieve a webpage, based on the following
|
||||
// Arduino example:
|
||||
//
|
||||
// https://github.com/arduino-libraries/WiFiNINA/blob/master/examples/WiFiWebClientRepeating/
|
||||
//
|
||||
// This example will not work with samd21 or other systems with less than 32KB
|
||||
// of RAM. Use the following if you want to run wifinina on samd21, etc.
|
||||
//
|
||||
// examples/wifinina/webclient
|
||||
// examples/wifinina/tlsclient
|
||||
//
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"machine"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/net"
|
||||
"tinygo.org/x/drivers/net/http"
|
||||
"tinygo.org/x/drivers/wifinina"
|
||||
)
|
||||
|
||||
// access point info
|
||||
const ssid = ""
|
||||
const pass = ""
|
||||
|
||||
// IP address of the server aka "hub". Replace with your own info.
|
||||
// Can specify a URL starting with http or https
|
||||
const url = "http://tinygo.org/"
|
||||
|
||||
// these are the default pins for the Arduino Nano33 IoT.
|
||||
// change these to connect to a different UART or pins for the ESP8266/ESP32
|
||||
var (
|
||||
|
||||
// these are the default pins for the Arduino Nano33 IoT.
|
||||
spi = machine.NINA_SPI
|
||||
|
||||
// this is the ESP chip that has the WIFININA firmware flashed on it
|
||||
adaptor *wifinina.Device
|
||||
)
|
||||
|
||||
var buf [0x400]byte
|
||||
|
||||
var lastRequestTime time.Time
|
||||
var conn net.Conn
|
||||
|
||||
func setup() {
|
||||
// Configure SPI for 8Mhz, Mode 0, MSB First
|
||||
spi.Configure(machine.SPIConfig{
|
||||
Frequency: 8 * 1e6,
|
||||
SDO: machine.NINA_SDO,
|
||||
SDI: machine.NINA_SDI,
|
||||
SCK: machine.NINA_SCK,
|
||||
})
|
||||
|
||||
adaptor = wifinina.New(spi,
|
||||
machine.NINA_CS,
|
||||
machine.NINA_ACK,
|
||||
machine.NINA_GPIO0,
|
||||
machine.NINA_RESETN)
|
||||
adaptor.Configure()
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
setup()
|
||||
http.SetBuf(buf[:])
|
||||
|
||||
waitSerial()
|
||||
|
||||
connectToAP()
|
||||
|
||||
// You can send and receive cookies in the following way
|
||||
// import "tinygo.org/x/drivers/net/http/cookiejar"
|
||||
// jar, err := cookiejar.New(nil)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// client := &http.Client{Jar: jar}
|
||||
// http.DefaultClient = client
|
||||
|
||||
cnt := 0
|
||||
for {
|
||||
// Various examples are as follows
|
||||
//
|
||||
// -- Get
|
||||
// resp, err := http.Get(url)
|
||||
//
|
||||
// -- Post
|
||||
// body := `cnt=12`
|
||||
// resp, err = http.Post(url, "application/x-www-form-urlencoded", strings.NewReader(body))
|
||||
//
|
||||
// -- Post with JSON
|
||||
// body := `{"msg": "hello"}`
|
||||
// resp, err := http.Post(url, "application/json", strings.NewReader(body))
|
||||
|
||||
resp, err := http.Get(url)
|
||||
if err != nil {
|
||||
fmt.Printf("%s\r\n", err.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
fmt.Printf("%s %s\r\n", resp.Proto, resp.Status)
|
||||
for k, v := range resp.Header {
|
||||
fmt.Printf("%s: %s\r\n", k, strings.Join(v, " "))
|
||||
}
|
||||
fmt.Printf("\r\n")
|
||||
|
||||
scanner := bufio.NewScanner(resp.Body)
|
||||
for scanner.Scan() {
|
||||
fmt.Printf("%s\r\n", scanner.Text())
|
||||
}
|
||||
resp.Body.Close()
|
||||
|
||||
cnt++
|
||||
fmt.Printf("-------- %d --------\r\n", cnt)
|
||||
time.Sleep(10 * time.Second)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Wait for user to open serial console
|
||||
func waitSerial() {
|
||||
for !machine.Serial.DTR() {
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
|
||||
// connect to access point
|
||||
func connectToAP() {
|
||||
if len(ssid) == 0 || len(pass) == 0 {
|
||||
for {
|
||||
println("Connection failed: Either ssid or password not set")
|
||||
time.Sleep(10 * time.Second)
|
||||
}
|
||||
}
|
||||
time.Sleep(2 * time.Second)
|
||||
message("Connecting to " + ssid)
|
||||
adaptor.SetPassphrase(ssid, pass)
|
||||
for st, _ := adaptor.GetConnectionStatus(); st != wifinina.StatusConnected; {
|
||||
message("Connection status: " + st.String())
|
||||
time.Sleep(1 * time.Second)
|
||||
st, _ = adaptor.GetConnectionStatus()
|
||||
}
|
||||
message("Connected.")
|
||||
time.Sleep(2 * time.Second)
|
||||
ip, _, _, err := adaptor.GetIP()
|
||||
for ; err != nil; ip, _, _, err = adaptor.GetIP() {
|
||||
message(err.Error())
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
message(ip.String())
|
||||
}
|
||||
|
||||
func message(msg string) {
|
||||
println(msg, "\r")
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
//go:build nano_rp2040
|
||||
// +build nano_rp2040
|
||||
|
||||
// This examples shows how to control RGB LED connected to
|
||||
// NINA-W102 chip on Arduino Nano RP2040 Connect board
|
||||
// Built-in LED code added for API comparison
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/wifinina"
|
||||
)
|
||||
|
||||
const (
|
||||
LED = machine.LED
|
||||
|
||||
// Arduino Nano RP2040 Connect board RGB LED pins
|
||||
// See https://docs.arduino.cc/static/3525d638b5c76a2d19588d6b41cd02a0/ABX00053-full-pinout.pdf
|
||||
LED_R wifinina.Pin = 27
|
||||
LED_G wifinina.Pin = 25
|
||||
LED_B wifinina.Pin = 26
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
// these are the default pins for the Arduino Nano-RP2040 Connect
|
||||
spi = machine.NINA_SPI
|
||||
|
||||
// this is the ESP chip that has the WIFININA firmware flashed on it
|
||||
device *wifinina.Device
|
||||
)
|
||||
|
||||
func setup() {
|
||||
|
||||
// Configure SPI for 8Mhz, Mode 0, MSB First
|
||||
spi.Configure(machine.SPIConfig{
|
||||
Frequency: 8 * 1e6,
|
||||
SDO: machine.NINA_SDO,
|
||||
SDI: machine.NINA_SDI,
|
||||
SCK: machine.NINA_SCK,
|
||||
})
|
||||
|
||||
device = wifinina.New(spi,
|
||||
machine.NINA_CS,
|
||||
machine.NINA_ACK,
|
||||
machine.NINA_GPIO0,
|
||||
machine.NINA_RESETN)
|
||||
device.Configure()
|
||||
|
||||
time.Sleep(time.Second)
|
||||
|
||||
LED.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
LED_R.Configure(wifinina.PinConfig{Mode: wifinina.PinOutput})
|
||||
LED_G.Configure(wifinina.PinConfig{Mode: wifinina.PinOutput})
|
||||
LED_B.Configure(wifinina.PinConfig{Mode: wifinina.PinOutput})
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
setup()
|
||||
|
||||
LED.Low() // OFF
|
||||
LED_R.High() // OFF
|
||||
LED_G.High() // OFF
|
||||
LED_B.High() // OFF
|
||||
|
||||
go func() {
|
||||
for {
|
||||
LED.Low()
|
||||
time.Sleep(time.Second)
|
||||
LED.High()
|
||||
time.Sleep(time.Second)
|
||||
}
|
||||
}()
|
||||
|
||||
for {
|
||||
LED_R.Low() // ON
|
||||
time.Sleep(time.Second)
|
||||
LED_R.High() // OFF
|
||||
LED_G.Low() // ON
|
||||
time.Sleep(time.Second)
|
||||
LED_G.High() // OFF
|
||||
LED_B.Low() // ON
|
||||
time.Sleep(time.Second)
|
||||
LED_B.High() // OFF
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
//go:build arduino
|
||||
// +build arduino
|
||||
|
||||
package main
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
//go:build digispark
|
||||
// +build digispark
|
||||
|
||||
package main
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
//go:build !digispark && !arduino && !qtpy
|
||||
// +build !digispark,!arduino,!qtpy
|
||||
|
||||
package main
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
//go:build qtpy
|
||||
// +build qtpy
|
||||
|
||||
package main
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
//go:build atsamd51
|
||||
// +build atsamd51
|
||||
|
||||
package flash
|
||||
|
||||
@@ -5,6 +5,7 @@ go 1.15
|
||||
require (
|
||||
github.com/eclipse/paho.mqtt.golang v1.2.0
|
||||
github.com/frankban/quicktest v1.10.2
|
||||
golang.org/x/net v0.0.0-20210614182718-04defd469f4e // indirect
|
||||
tinygo.org/x/tinyfont v0.2.1
|
||||
tinygo.org/x/tinyfs v0.1.0
|
||||
tinygo.org/x/tinyterm v0.1.0
|
||||
|
||||
@@ -11,6 +11,14 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/valyala/fastjson v1.6.3/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY=
|
||||
golang.org/x/net v0.0.0-20210614182718-04defd469f4e h1:XpT3nA5TvE525Ne3hInMh6+GETgn27Zfm9dxsThnX2Q=
|
||||
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
tinygo.org/x/drivers v0.14.0/go.mod h1:uT2svMq3EpBZpKkGO+NQHjxjGf1f42ra4OnMMwQL2aI=
|
||||
|
||||
@@ -0,0 +1,178 @@
|
||||
// Package hts221 implements a driver for HTS221,
|
||||
// a capacitive digital sensor for relative humidity and temperature.
|
||||
//
|
||||
// Datasheet: https://www.st.com/resource/en/datasheet/hts221.pdf
|
||||
//
|
||||
package hts221
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
)
|
||||
|
||||
// Device wraps an I2C connection to a HTS221 device.
|
||||
type Device struct {
|
||||
bus drivers.I2C
|
||||
Address uint8
|
||||
humiditySlope float32
|
||||
humidityZero float32
|
||||
temperatureSlope float32
|
||||
temperatureZero float32
|
||||
}
|
||||
|
||||
// Connected returns whether HTS221 has been found.
|
||||
// It does a "who am I" request and checks the response.
|
||||
func (d *Device) Connected() bool {
|
||||
data := []byte{0}
|
||||
d.bus.ReadRegister(d.Address, HTS221_WHO_AM_I_REG, data)
|
||||
return data[0] == 0xBC
|
||||
}
|
||||
|
||||
// Configure sets up the HTS221 device for communication.
|
||||
func (d *Device) Configure() {
|
||||
// read calibration data
|
||||
d.calibration()
|
||||
// activate device and use block data update mode
|
||||
d.Power(true)
|
||||
}
|
||||
|
||||
// Power is for turn on/off the HTS221 device
|
||||
func (d *Device) Power(status bool) {
|
||||
data := []byte{0}
|
||||
if status {
|
||||
data[0] = 0x84
|
||||
}
|
||||
d.bus.WriteRegister(d.Address, HTS221_CTRL1_REG, data)
|
||||
}
|
||||
|
||||
// ReadHumidity returns the relative humidity in percent * 100.
|
||||
// Returns an error if the device is not turned on.
|
||||
func (d *Device) ReadHumidity() (humidity int32, err error) {
|
||||
err = d.waitForOneShot(0x02)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// read data and calibrate
|
||||
data := []byte{0, 0}
|
||||
d.bus.ReadRegister(d.Address, HTS221_HUMID_OUT_REG, data[:1])
|
||||
d.bus.ReadRegister(d.Address, HTS221_HUMID_OUT_REG+1, data[1:])
|
||||
hValue := readInt(data[1], data[0])
|
||||
hValueCalib := float32(hValue)*d.humiditySlope + d.humidityZero
|
||||
|
||||
return int32(hValueCalib * 100), nil
|
||||
}
|
||||
|
||||
// ReadTemperature returns the temperature in celsius milli degrees (°C/1000).
|
||||
// Returns an error if the device is not turned on.
|
||||
func (d *Device) ReadTemperature() (temperature int32, err error) {
|
||||
err = d.waitForOneShot(0x01)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// read data and calibrate
|
||||
data := []byte{0, 0}
|
||||
d.bus.ReadRegister(d.Address, HTS221_TEMP_OUT_REG, data[:1])
|
||||
d.bus.ReadRegister(d.Address, HTS221_TEMP_OUT_REG+1, data[1:])
|
||||
tValue := readInt(data[1], data[0])
|
||||
tValueCalib := float32(tValue)*d.temperatureSlope + d.temperatureZero
|
||||
|
||||
return int32(tValueCalib * 1000), nil
|
||||
}
|
||||
|
||||
// Resolution sets the HTS221's resolution mode.
|
||||
// The higher resolutions are more accurate but comsume more power (see datasheet).
|
||||
// The number of averaged samples will be (h + 2) ^ 2, (t + 1) ^ 2
|
||||
//
|
||||
func (d *Device) Resolution(h uint8, t uint8) {
|
||||
if h > 7 {
|
||||
h = 3 // default
|
||||
}
|
||||
if t > 7 {
|
||||
t = 3 // default
|
||||
}
|
||||
d.bus.WriteRegister(d.Address, HTS221_AV_CONF_REG, []byte{h<<3 | t})
|
||||
}
|
||||
|
||||
// private functions
|
||||
|
||||
// read factory calibration data
|
||||
func (d *Device) calibration() {
|
||||
h0rH, h1rH := []byte{0}, []byte{0}
|
||||
t0degC, t1degC := []byte{0}, []byte{0}
|
||||
t1t0msb := []byte{0}
|
||||
h0t0Out, h1t0Out := []byte{0, 0}, []byte{0, 0}
|
||||
t0Out, t1Out := []byte{0, 0}, []byte{0, 0}
|
||||
|
||||
d.bus.ReadRegister(d.Address, HTS221_H0_rH_x2_REG, h0rH)
|
||||
d.bus.ReadRegister(d.Address, HTS221_H1_rH_x2_REG, h1rH)
|
||||
d.bus.ReadRegister(d.Address, HTS221_T0_degC_x8_REG, t0degC)
|
||||
d.bus.ReadRegister(d.Address, HTS221_T1_degC_x8_REG, t1degC)
|
||||
d.bus.ReadRegister(d.Address, HTS221_T1_T0_MSB_REG, t1t0msb)
|
||||
d.bus.ReadRegister(d.Address, HTS221_H0_T0_OUT_REG, h0t0Out[:1])
|
||||
d.bus.ReadRegister(d.Address, HTS221_H0_T0_OUT_REG+1, h0t0Out[1:])
|
||||
d.bus.ReadRegister(d.Address, HTS221_H1_T0_OUT_REG, h1t0Out[:1])
|
||||
d.bus.ReadRegister(d.Address, HTS221_H1_T0_OUT_REG+1, h1t0Out[1:])
|
||||
d.bus.ReadRegister(d.Address, HTS221_T0_OUT_REG, t0Out[:1])
|
||||
d.bus.ReadRegister(d.Address, HTS221_T0_OUT_REG+1, t0Out[1:])
|
||||
d.bus.ReadRegister(d.Address, HTS221_T1_OUT_REG, t1Out[:1])
|
||||
d.bus.ReadRegister(d.Address, HTS221_T1_OUT_REG+1, t1Out[1:])
|
||||
|
||||
h0rH_v := float32(h0rH[0]) / 2.0
|
||||
h1rH_v := float32(h1rH[0]) / 2.0
|
||||
t0degC_v := float32(readUint(t1t0msb[0]&0x03, t0degC[0])) / 8.0
|
||||
t1degC_v := float32(readUint(t1t0msb[0]&0x0C>>2, t1degC[0])) / 8.0
|
||||
h0t0Out_v := float32(readInt(h0t0Out[1], h0t0Out[0]))
|
||||
h1t0Out_v := float32(readInt(h1t0Out[1], h1t0Out[0]))
|
||||
t0Out_v := float32(readInt(t0Out[1], t0Out[0]))
|
||||
t1Out_v := float32(readInt(t1Out[1], t1Out[0]))
|
||||
|
||||
d.humiditySlope = (h1rH_v - h0rH_v) / (h1t0Out_v - h0t0Out_v)
|
||||
d.humidityZero = h0rH_v - d.humiditySlope*h0t0Out_v
|
||||
d.temperatureSlope = (t1degC_v - t0degC_v) / (t1Out_v - t0Out_v)
|
||||
d.temperatureZero = t0degC_v - d.temperatureSlope*t0Out_v
|
||||
}
|
||||
|
||||
// wait and trigger one shot in block update
|
||||
func (d *Device) waitForOneShot(filter uint8) error {
|
||||
data := []byte{0}
|
||||
|
||||
// check if the device is on
|
||||
d.bus.ReadRegister(d.Address, HTS221_CTRL1_REG, data)
|
||||
if data[0]&0x80 == 0 {
|
||||
return errors.New("device is off, unable to query")
|
||||
}
|
||||
|
||||
// wait until one shot (one conversion) is ready to go
|
||||
data[0] = 1
|
||||
for {
|
||||
d.bus.ReadRegister(d.Address, HTS221_CTRL2_REG, data)
|
||||
if data[0]&0x01 == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// trigger one shot
|
||||
d.bus.WriteRegister(d.Address, HTS221_CTRL2_REG, []byte{0x01})
|
||||
|
||||
// wait until conversion completed
|
||||
data[0] = 0
|
||||
for {
|
||||
d.bus.ReadRegister(d.Address, HTS221_STATUS_REG, data)
|
||||
if data[0]&filter == filter {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func readUint(msb byte, lsb byte) uint16 {
|
||||
return uint16(msb)<<8 | uint16(lsb)
|
||||
}
|
||||
|
||||
func readInt(msb byte, lsb byte) int16 {
|
||||
return int16(uint16(msb)<<8 | uint16(lsb))
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
//go:build !nano_33_ble
|
||||
// +build !nano_33_ble
|
||||
|
||||
package hts221
|
||||
|
||||
import "tinygo.org/x/drivers"
|
||||
|
||||
// New creates a new HTS221 connection. The I2C bus must already be
|
||||
// configured.
|
||||
//
|
||||
// This function only creates the Device object, it does not touch the device.
|
||||
func New(bus drivers.I2C) Device {
|
||||
return Device{bus: bus, Address: HTS221_ADDRESS}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
//go:build nano_33_ble
|
||||
// +build nano_33_ble
|
||||
|
||||
package hts221
|
||||
|
||||
import (
|
||||
"machine"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
)
|
||||
|
||||
// New creates a new HTS221 connection. The I2C bus must already be
|
||||
// configured.
|
||||
//
|
||||
// This function only creates the Device object, it does not touch the device.
|
||||
func New(bus drivers.I2C) Device {
|
||||
// turn on internal power pin (machine.P0_22) and I2C1 pullups power pin (machine.P1_00)
|
||||
// and wait a moment.
|
||||
ENV := machine.P0_22
|
||||
ENV.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
ENV.High()
|
||||
R := machine.P1_00
|
||||
R.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
R.High()
|
||||
time.Sleep(time.Millisecond * 10)
|
||||
|
||||
return Device{bus: bus, Address: HTS221_ADDRESS}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package hts221
|
||||
|
||||
const (
|
||||
|
||||
// I2C address
|
||||
HTS221_ADDRESS = 0x5F
|
||||
|
||||
// control/status registers
|
||||
HTS221_WHO_AM_I_REG = 0x0F
|
||||
HTS221_AV_CONF_REG = 0x10
|
||||
HTS221_CTRL1_REG = 0x20
|
||||
HTS221_CTRL2_REG = 0x21
|
||||
HTS221_STATUS_REG = 0x27
|
||||
HTS221_HUMID_OUT_REG = 0x28
|
||||
HTS221_TEMP_OUT_REG = 0x2A
|
||||
|
||||
// calibration registers
|
||||
HTS221_H0_rH_x2_REG = 0x30
|
||||
HTS221_H1_rH_x2_REG = 0x31
|
||||
HTS221_T0_degC_x8_REG = 0x32
|
||||
HTS221_T1_degC_x8_REG = 0x33
|
||||
HTS221_T1_T0_MSB_REG = 0x35
|
||||
HTS221_H0_T0_OUT_REG = 0x36
|
||||
HTS221_H1_T0_OUT_REG = 0x3A
|
||||
HTS221_T0_OUT_REG = 0x3C
|
||||
HTS221_T1_OUT_REG = 0x3E
|
||||
)
|
||||
@@ -0,0 +1,280 @@
|
||||
package i2csoft
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"machine"
|
||||
)
|
||||
|
||||
// I2C is an I2C implementation by Software. Since it is implemented by
|
||||
// software, it can be used with microcontrollers that do not have I2C
|
||||
// function. This is not efficient but works around broken or missing drivers.
|
||||
type I2C struct {
|
||||
scl machine.Pin
|
||||
sda machine.Pin
|
||||
nack bool
|
||||
baudrate uint32
|
||||
}
|
||||
|
||||
// I2CConfig is used to store config info for I2C.
|
||||
type I2CConfig struct {
|
||||
Frequency uint32
|
||||
SCL machine.Pin
|
||||
SDA machine.Pin
|
||||
}
|
||||
|
||||
var (
|
||||
errSI2CAckExpected = errors.New("I2C error: expected ACK not NACK")
|
||||
)
|
||||
|
||||
// New returns the i2csoft driver. For the arguments, specify the pins to be
|
||||
// used as SCL and SDA. As I2C is implemented in software, any GPIO pin can be
|
||||
// specified.
|
||||
func New(sclPin, sdaPin machine.Pin) *I2C {
|
||||
return &I2C{
|
||||
scl: sclPin,
|
||||
sda: sdaPin,
|
||||
baudrate: 100e3,
|
||||
}
|
||||
}
|
||||
|
||||
// Configure is intended to setup the I2C interface.
|
||||
func (i2c *I2C) Configure(config I2CConfig) error {
|
||||
// Default I2C bus speed is 100 kHz.
|
||||
if config.Frequency != 0 {
|
||||
i2c.SetBaudRate(config.Frequency)
|
||||
}
|
||||
|
||||
// This exists for compatibility with machine.I2CConfig. SCL and SDA must
|
||||
// be set at the same time. Because Pin(0) is sometimes set, it is not
|
||||
// checked for 0.
|
||||
if config.SCL != config.SDA {
|
||||
i2c.scl = config.SCL
|
||||
i2c.sda = config.SDA
|
||||
}
|
||||
|
||||
// enable pins
|
||||
i2c.sda.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
i2c.sda.High()
|
||||
i2c.scl.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
i2c.scl.High()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetBaudRate sets the communication speed for the I2C.
|
||||
func (i2c *I2C) SetBaudRate(br uint32) {
|
||||
// At this time, the value of i2c.baudrate is ignored because it is fixed
|
||||
// at 100 kHz. SetBaudrate() is exist for compatibility with machine.I2C.
|
||||
i2c.baudrate = br
|
||||
}
|
||||
|
||||
// Tx does a single I2C transaction at the specified address.
|
||||
// It clocks out the given address, writes the bytes in w, reads back len(r)
|
||||
// bytes and stores them in r, and generates a stop condition on the bus.
|
||||
func (i2c *I2C) Tx(addr uint16, w, r []byte) error {
|
||||
i2c.nack = false
|
||||
if len(w) != 0 {
|
||||
// send start/address for write
|
||||
i2c.sendAddress(addr, true)
|
||||
|
||||
// wait until transmission complete
|
||||
|
||||
// ACK received (0: ACK, 1: NACK)
|
||||
if i2c.nack {
|
||||
i2c.signalStop()
|
||||
return errSI2CAckExpected
|
||||
}
|
||||
|
||||
// write data
|
||||
for _, b := range w {
|
||||
i2c.writeByte(b)
|
||||
}
|
||||
|
||||
i2c.signalStop()
|
||||
}
|
||||
if len(r) != 0 {
|
||||
// send start/address for read
|
||||
i2c.sendAddress(addr, false)
|
||||
|
||||
// wait transmission complete
|
||||
|
||||
// ACK received (0: ACK, 1: NACK)
|
||||
if i2c.nack {
|
||||
i2c.signalStop()
|
||||
return errSI2CAckExpected
|
||||
}
|
||||
|
||||
// read first byte
|
||||
r[0] = i2c.readByte()
|
||||
for i := 1; i < len(r); i++ {
|
||||
// Send an ACK
|
||||
|
||||
i2c.signalRead()
|
||||
|
||||
// Read data and send the ACK
|
||||
r[i] = i2c.readByte()
|
||||
}
|
||||
|
||||
// Send NACK to end transmission
|
||||
i2c.sendNack()
|
||||
|
||||
i2c.signalStop()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// writeByte writes a single byte to the I2C bus.
|
||||
func (i2c *I2C) writeByte(data byte) {
|
||||
// Send data byte
|
||||
i2c.scl.Low()
|
||||
i2c.sda.High()
|
||||
i2c.sda.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
i2c.wait()
|
||||
|
||||
for i := 0; i < 8; i++ {
|
||||
i2c.scl.Low()
|
||||
if ((data >> (7 - i)) & 1) == 1 {
|
||||
i2c.sda.High()
|
||||
} else {
|
||||
i2c.sda.Low()
|
||||
}
|
||||
i2c.wait()
|
||||
i2c.wait()
|
||||
i2c.scl.High()
|
||||
i2c.wait()
|
||||
i2c.wait()
|
||||
}
|
||||
|
||||
i2c.scl.Low()
|
||||
i2c.wait()
|
||||
i2c.wait()
|
||||
i2c.sda.Configure(machine.PinConfig{Mode: machine.PinInput})
|
||||
i2c.scl.High()
|
||||
i2c.wait()
|
||||
|
||||
i2c.nack = i2c.sda.Get()
|
||||
|
||||
i2c.wait()
|
||||
|
||||
// wait until transmission successful
|
||||
}
|
||||
|
||||
// sendAddress sends the address and start signal
|
||||
func (i2c *I2C) sendAddress(address uint16, write bool) {
|
||||
data := (address << 1)
|
||||
if !write {
|
||||
data |= 1 // set read flag
|
||||
}
|
||||
|
||||
i2c.scl.High()
|
||||
i2c.sda.Low()
|
||||
i2c.wait()
|
||||
i2c.wait()
|
||||
for i := 0; i < 8; i++ {
|
||||
i2c.scl.Low()
|
||||
if ((data >> (7 - i)) & 1) == 1 {
|
||||
i2c.sda.High()
|
||||
} else {
|
||||
i2c.sda.Low()
|
||||
}
|
||||
i2c.wait()
|
||||
i2c.wait()
|
||||
i2c.scl.High()
|
||||
i2c.wait()
|
||||
i2c.wait()
|
||||
}
|
||||
|
||||
i2c.scl.Low()
|
||||
i2c.wait()
|
||||
i2c.wait()
|
||||
i2c.sda.Configure(machine.PinConfig{Mode: machine.PinInput})
|
||||
i2c.scl.High()
|
||||
i2c.wait()
|
||||
|
||||
i2c.nack = i2c.sda.Get()
|
||||
|
||||
i2c.wait()
|
||||
|
||||
// wait until bus ready
|
||||
}
|
||||
|
||||
func (i2c *I2C) signalStop() {
|
||||
i2c.scl.Low()
|
||||
i2c.sda.Low()
|
||||
i2c.sda.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
i2c.wait()
|
||||
i2c.wait()
|
||||
i2c.scl.High()
|
||||
i2c.wait()
|
||||
i2c.wait()
|
||||
i2c.sda.High()
|
||||
i2c.wait()
|
||||
i2c.wait()
|
||||
}
|
||||
|
||||
func (i2c *I2C) signalRead() {
|
||||
i2c.wait()
|
||||
i2c.wait()
|
||||
i2c.scl.Low()
|
||||
i2c.sda.Low()
|
||||
i2c.sda.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
i2c.wait()
|
||||
i2c.wait()
|
||||
i2c.scl.High()
|
||||
i2c.wait()
|
||||
i2c.wait()
|
||||
}
|
||||
|
||||
func (i2c *I2C) readByte() byte {
|
||||
var data byte
|
||||
for i := 0; i < 8; i++ {
|
||||
i2c.scl.Low()
|
||||
i2c.sda.Configure(machine.PinConfig{Mode: machine.PinInput})
|
||||
i2c.wait()
|
||||
i2c.wait()
|
||||
i2c.scl.High()
|
||||
if i2c.sda.Get() {
|
||||
data |= 1 << (7 - i)
|
||||
}
|
||||
i2c.wait()
|
||||
i2c.wait()
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
func (i2c *I2C) sendNack() {
|
||||
i2c.wait()
|
||||
i2c.wait()
|
||||
i2c.scl.Low()
|
||||
i2c.sda.High()
|
||||
i2c.sda.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
i2c.wait()
|
||||
i2c.wait()
|
||||
i2c.scl.High()
|
||||
i2c.wait()
|
||||
i2c.wait()
|
||||
}
|
||||
|
||||
// WriteRegister transmits first the register and then the data to the
|
||||
// peripheral device.
|
||||
//
|
||||
// Many I2C-compatible devices are organized in terms of registers. This method
|
||||
// is a shortcut to easily write to such registers. Also, it only works for
|
||||
// devices with 7-bit addresses, which is the vast majority.
|
||||
func (i2c *I2C) WriteRegister(address uint8, register uint8, data []byte) error {
|
||||
buf := make([]uint8, len(data)+1)
|
||||
buf[0] = register
|
||||
copy(buf[1:], data)
|
||||
return i2c.Tx(uint16(address), buf, nil)
|
||||
}
|
||||
|
||||
// ReadRegister transmits the register, restarts the connection as a read
|
||||
// operation, and reads the response.
|
||||
//
|
||||
// Many I2C-compatible devices are organized in terms of registers. This method
|
||||
// is a shortcut to easily read such registers. Also, it only works for devices
|
||||
// with 7-bit addresses, which is the vast majority.
|
||||
func (i2c *I2C) ReadRegister(address uint8, register uint8, data []byte) error {
|
||||
return i2c.Tx(uint16(address), []byte{register}, data)
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
//go:build atsamd51 || atsame5x
|
||||
// +build atsamd51 atsame5x
|
||||
|
||||
package i2csoft
|
||||
|
||||
import (
|
||||
"device"
|
||||
)
|
||||
|
||||
// wait waits for half the time of the SCL operation interval. It is set to
|
||||
// about 100 kHz.
|
||||
func (i2c *I2C) wait() {
|
||||
wait := 20
|
||||
for i := 0; i < wait; i++ {
|
||||
device.Asm(`nop`)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
//go:build esp32
|
||||
// +build esp32
|
||||
|
||||
package i2csoft
|
||||
|
||||
import (
|
||||
"device"
|
||||
)
|
||||
|
||||
// wait waits for half the time of the SCL operation interval. It is set to
|
||||
// about 100 kHz.
|
||||
func (i2c *I2C) wait() {
|
||||
wait := 60
|
||||
for i := 0; i < wait; i++ {
|
||||
device.Asm(`nop`)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
//go:build nrf52840
|
||||
// +build nrf52840
|
||||
|
||||
package i2csoft
|
||||
|
||||
import (
|
||||
"device"
|
||||
)
|
||||
|
||||
// wait waits for half the time of the SCL operation interval. It is set to
|
||||
// about 100 kHz.
|
||||
func (i2c *I2C) wait() {
|
||||
wait := 26
|
||||
for i := 0; i < wait; i++ {
|
||||
device.Asm(`nop`)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
//go:build !esp32 && !atsamd51 && !atsame5x && !stm32f4 && !rp2040 && !nrf52840
|
||||
// +build !esp32,!atsamd51,!atsame5x,!stm32f4,!rp2040,!nrf52840
|
||||
|
||||
package i2csoft
|
||||
|
||||
import (
|
||||
"device"
|
||||
)
|
||||
|
||||
// wait waits for half the time of the SCL operation interval.
|
||||
func (i2c *I2C) wait() {
|
||||
wait := 20
|
||||
for i := 0; i < wait; i++ {
|
||||
device.Asm(`nop`)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
//go:build rp2040
|
||||
// +build rp2040
|
||||
|
||||
package i2csoft
|
||||
|
||||
import (
|
||||
"device"
|
||||
)
|
||||
|
||||
// wait waits for half the time of the SCL operation interval. It is set to
|
||||
// about 100 kHz.
|
||||
func (i2c *I2C) wait() {
|
||||
wait := 50
|
||||
for i := 0; i < wait; i++ {
|
||||
device.Asm(`nop`)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
//go:build stm32f4
|
||||
// +build stm32f4
|
||||
|
||||
package i2csoft
|
||||
|
||||
import (
|
||||
"device"
|
||||
)
|
||||
|
||||
// wait waits for half the time of the SCL operation interval. It is set to
|
||||
// about 100 kHz.
|
||||
func (i2c *I2C) wait() {
|
||||
wait := 77
|
||||
for i := 0; i < wait; i++ {
|
||||
device.Asm(`nop`)
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
//go:build atsamd51
|
||||
// +build atsamd51
|
||||
|
||||
package ili9341
|
||||
|
||||
+5
-2
@@ -1,18 +1,21 @@
|
||||
//go:build !atsamd51 && !atsamd21
|
||||
// +build !atsamd51,!atsamd21
|
||||
|
||||
package ili9341
|
||||
|
||||
import (
|
||||
"machine"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
)
|
||||
|
||||
var buf [64]byte
|
||||
|
||||
type spiDriver struct {
|
||||
bus machine.SPI
|
||||
bus drivers.SPI
|
||||
}
|
||||
|
||||
func NewSPI(bus machine.SPI, dc, cs, rst machine.Pin) *Device {
|
||||
func NewSPI(bus drivers.SPI, dc, cs, rst machine.Pin) *Device {
|
||||
return &Device{
|
||||
dc: dc,
|
||||
cs: cs,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
//go:build atsamd21
|
||||
// +build atsamd21
|
||||
|
||||
package ili9341
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
//go:build atsamd51
|
||||
// +build atsamd51
|
||||
|
||||
package ili9341
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
# tinygo.org/x/drivers/image
|
||||
|
||||
This is an image package that uses less RAM to run on a microcontroller.
|
||||
Unlike Go's original image package, `image.Decode()` does not return `image.Image`.
|
||||
|
||||
Instead, a callback can be set to process the data corresponding to the image.
|
||||
|
||||
## How to use
|
||||
|
||||
First, use `SetCallback()` to set the callback.
|
||||
Then call `png.Decode()` or `jpeg.Decode()`.
|
||||
The callback will be called as many times as necessary to load the image.
|
||||
|
||||
`SetCallback()` needs to be given a Buffer to handle the callback and the actual function to be called.
|
||||
The `data []uint16` in the callback is in RGB565 format.
|
||||
|
||||
The `io.Reader` to pass to `Decode()` specifies the binary data of the image.
|
||||
|
||||
```go
|
||||
func drawPng(display *ili9341.Device) error {
|
||||
p := strings.NewReader(pngImage)
|
||||
png.SetCallback(buffer[:], func(data []uint16, x, y, w, h, width, height int16) {
|
||||
err := display.DrawRGBBitmap(x, y, data[:w*h], w, h)
|
||||
if err != nil {
|
||||
errorMessage(fmt.Errorf("error drawPng: %s", err))
|
||||
}
|
||||
})
|
||||
|
||||
return png.Decode(p)
|
||||
}
|
||||
```
|
||||
|
||||
```go
|
||||
func drawJpeg(display *ili9341.Device) error {
|
||||
p := strings.NewReader(jpegImage)
|
||||
jpeg.SetCallback(buffer[:], func(data []uint16, x, y, w, h, width, height int16) {
|
||||
err := display.DrawRGBBitmap(x, y, data[:w*h], w, h)
|
||||
if err != nil {
|
||||
errorMessage(fmt.Errorf("error drawJpeg: %s", err))
|
||||
}
|
||||
})
|
||||
|
||||
return jpeg.Decode(p)
|
||||
}
|
||||
```
|
||||
|
||||
## How to create an image
|
||||
|
||||
The following program will output an image binary like the one in [images.go](./examples/ili9341/slideshow/images.go).
|
||||
|
||||
```
|
||||
go run ./cmd/convert2bin ./path/to/png_or_jpg.png
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
An example can be found below.
|
||||
Processing jpegs requires a minimum of 32KB of RAM.
|
||||
|
||||
* [./examples/ili9341/slideshow](./examples/ili9341/slideshow)
|
||||
@@ -0,0 +1,748 @@
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package flate
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
)
|
||||
|
||||
const (
|
||||
NoCompression = 0
|
||||
BestSpeed = 1
|
||||
BestCompression = 9
|
||||
DefaultCompression = -1
|
||||
|
||||
// HuffmanOnly disables Lempel-Ziv match searching and only performs Huffman
|
||||
// entropy encoding. This mode is useful in compressing data that has
|
||||
// already been compressed with an LZ style algorithm (e.g. Snappy or LZ4)
|
||||
// that lacks an entropy encoder. Compression gains are achieved when
|
||||
// certain bytes in the input stream occur more frequently than others.
|
||||
//
|
||||
// Note that HuffmanOnly produces a compressed output that is
|
||||
// RFC 1951 compliant. That is, any valid DEFLATE decompressor will
|
||||
// continue to be able to decompress this output.
|
||||
HuffmanOnly = -2
|
||||
)
|
||||
|
||||
const (
|
||||
logWindowSize = 15
|
||||
windowSize = 1 << logWindowSize
|
||||
windowMask = windowSize - 1
|
||||
|
||||
// The LZ77 step produces a sequence of literal tokens and <length, offset>
|
||||
// pair tokens. The offset is also known as distance. The underlying wire
|
||||
// format limits the range of lengths and offsets. For example, there are
|
||||
// 256 legitimate lengths: those in the range [3, 258]. This package's
|
||||
// compressor uses a higher minimum match length, enabling optimizations
|
||||
// such as finding matches via 32-bit loads and compares.
|
||||
baseMatchLength = 3 // The smallest match length per the RFC section 3.2.5
|
||||
minMatchLength = 4 // The smallest match length that the compressor actually emits
|
||||
maxMatchLength = 258 // The largest match length
|
||||
baseMatchOffset = 1 // The smallest match offset
|
||||
maxMatchOffset = 1 << 15 // The largest match offset
|
||||
|
||||
// The maximum number of tokens we put into a single flate block, just to
|
||||
// stop things from getting too large.
|
||||
maxFlateBlockTokens = 1 << 14
|
||||
maxStoreBlockSize = 65535
|
||||
hashBits = 17 // After 17 performance degrades
|
||||
hashSize = 1 << hashBits
|
||||
hashMask = (1 << hashBits) - 1
|
||||
maxHashOffset = 1 << 24
|
||||
|
||||
skipNever = math.MaxInt32
|
||||
)
|
||||
|
||||
type compressionLevel struct {
|
||||
level, good, lazy, nice, chain, fastSkipHashing int
|
||||
}
|
||||
|
||||
var levels = []compressionLevel{
|
||||
{0, 0, 0, 0, 0, 0}, // NoCompression.
|
||||
{1, 0, 0, 0, 0, 0}, // BestSpeed uses a custom algorithm; see deflatefast.go.
|
||||
// For levels 2-3 we don't bother trying with lazy matches.
|
||||
{2, 4, 0, 16, 8, 5},
|
||||
{3, 4, 0, 32, 32, 6},
|
||||
// Levels 4-9 use increasingly more lazy matching
|
||||
// and increasingly stringent conditions for "good enough".
|
||||
{4, 4, 4, 16, 16, skipNever},
|
||||
{5, 8, 16, 32, 32, skipNever},
|
||||
{6, 8, 16, 128, 128, skipNever},
|
||||
{7, 8, 32, 128, 256, skipNever},
|
||||
{8, 32, 128, 258, 1024, skipNever},
|
||||
{9, 32, 258, 258, 4096, skipNever},
|
||||
}
|
||||
|
||||
type compressor struct {
|
||||
compressionLevel
|
||||
|
||||
w *huffmanBitWriter
|
||||
bulkHasher func([]byte, []uint32)
|
||||
|
||||
// compression algorithm
|
||||
fill func(*compressor, []byte) int // copy data to window
|
||||
step func(*compressor) // process window
|
||||
sync bool // requesting flush
|
||||
bestSpeed *deflateFast // Encoder for BestSpeed
|
||||
|
||||
// Input hash chains
|
||||
// hashHead[hashValue] contains the largest inputIndex with the specified hash value
|
||||
// If hashHead[hashValue] is within the current window, then
|
||||
// hashPrev[hashHead[hashValue] & windowMask] contains the previous index
|
||||
// with the same hash value.
|
||||
chainHead int
|
||||
hashHead [hashSize]uint32
|
||||
hashPrev [windowSize]uint32
|
||||
hashOffset int
|
||||
|
||||
// input window: unprocessed data is window[index:windowEnd]
|
||||
index int
|
||||
window []byte
|
||||
windowEnd int
|
||||
blockStart int // window index where current tokens start
|
||||
byteAvailable bool // if true, still need to process window[index-1].
|
||||
|
||||
// queued output tokens
|
||||
tokens []token
|
||||
|
||||
// deflate state
|
||||
length int
|
||||
offset int
|
||||
hash uint32
|
||||
maxInsertIndex int
|
||||
err error
|
||||
|
||||
// hashMatch must be able to contain hashes for the maximum match length.
|
||||
hashMatch [maxMatchLength - 1]uint32
|
||||
}
|
||||
|
||||
func (d *compressor) fillDeflate(b []byte) int {
|
||||
if d.index >= 2*windowSize-(minMatchLength+maxMatchLength) {
|
||||
// shift the window by windowSize
|
||||
copy(d.window, d.window[windowSize:2*windowSize])
|
||||
d.index -= windowSize
|
||||
d.windowEnd -= windowSize
|
||||
if d.blockStart >= windowSize {
|
||||
d.blockStart -= windowSize
|
||||
} else {
|
||||
d.blockStart = math.MaxInt32
|
||||
}
|
||||
d.hashOffset += windowSize
|
||||
if d.hashOffset > maxHashOffset {
|
||||
delta := d.hashOffset - 1
|
||||
d.hashOffset -= delta
|
||||
d.chainHead -= delta
|
||||
|
||||
// Iterate over slices instead of arrays to avoid copying
|
||||
// the entire table onto the stack (Issue #18625).
|
||||
for i, v := range d.hashPrev[:] {
|
||||
if int(v) > delta {
|
||||
d.hashPrev[i] = uint32(int(v) - delta)
|
||||
} else {
|
||||
d.hashPrev[i] = 0
|
||||
}
|
||||
}
|
||||
for i, v := range d.hashHead[:] {
|
||||
if int(v) > delta {
|
||||
d.hashHead[i] = uint32(int(v) - delta)
|
||||
} else {
|
||||
d.hashHead[i] = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
n := copy(d.window[d.windowEnd:], b)
|
||||
d.windowEnd += n
|
||||
return n
|
||||
}
|
||||
|
||||
func (d *compressor) writeBlock(tokens []token, index int) error {
|
||||
if index > 0 {
|
||||
var window []byte
|
||||
if d.blockStart <= index {
|
||||
window = d.window[d.blockStart:index]
|
||||
}
|
||||
d.blockStart = index
|
||||
d.w.writeBlock(tokens, false, window)
|
||||
return d.w.err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// fillWindow will fill the current window with the supplied
|
||||
// dictionary and calculate all hashes.
|
||||
// This is much faster than doing a full encode.
|
||||
// Should only be used after a reset.
|
||||
func (d *compressor) fillWindow(b []byte) {
|
||||
// Do not fill window if we are in store-only mode.
|
||||
if d.compressionLevel.level < 2 {
|
||||
return
|
||||
}
|
||||
if d.index != 0 || d.windowEnd != 0 {
|
||||
panic("internal error: fillWindow called with stale data")
|
||||
}
|
||||
|
||||
// If we are given too much, cut it.
|
||||
if len(b) > windowSize {
|
||||
b = b[len(b)-windowSize:]
|
||||
}
|
||||
// Add all to window.
|
||||
n := copy(d.window, b)
|
||||
|
||||
// Calculate 256 hashes at the time (more L1 cache hits)
|
||||
loops := (n + 256 - minMatchLength) / 256
|
||||
for j := 0; j < loops; j++ {
|
||||
index := j * 256
|
||||
end := index + 256 + minMatchLength - 1
|
||||
if end > n {
|
||||
end = n
|
||||
}
|
||||
toCheck := d.window[index:end]
|
||||
dstSize := len(toCheck) - minMatchLength + 1
|
||||
|
||||
if dstSize <= 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
dst := d.hashMatch[:dstSize]
|
||||
d.bulkHasher(toCheck, dst)
|
||||
var newH uint32
|
||||
for i, val := range dst {
|
||||
di := i + index
|
||||
newH = val
|
||||
hh := &d.hashHead[newH&hashMask]
|
||||
// Get previous value with the same hash.
|
||||
// Our chain should point to the previous value.
|
||||
d.hashPrev[di&windowMask] = *hh
|
||||
// Set the head of the hash chain to us.
|
||||
*hh = uint32(di + d.hashOffset)
|
||||
}
|
||||
d.hash = newH
|
||||
}
|
||||
// Update window information.
|
||||
d.windowEnd = n
|
||||
d.index = n
|
||||
}
|
||||
|
||||
// Try to find a match starting at index whose length is greater than prevSize.
|
||||
// We only look at chainCount possibilities before giving up.
|
||||
func (d *compressor) findMatch(pos int, prevHead int, prevLength int, lookahead int) (length, offset int, ok bool) {
|
||||
minMatchLook := maxMatchLength
|
||||
if lookahead < minMatchLook {
|
||||
minMatchLook = lookahead
|
||||
}
|
||||
|
||||
win := d.window[0 : pos+minMatchLook]
|
||||
|
||||
// We quit when we get a match that's at least nice long
|
||||
nice := len(win) - pos
|
||||
if d.nice < nice {
|
||||
nice = d.nice
|
||||
}
|
||||
|
||||
// If we've got a match that's good enough, only look in 1/4 the chain.
|
||||
tries := d.chain
|
||||
length = prevLength
|
||||
if length >= d.good {
|
||||
tries >>= 2
|
||||
}
|
||||
|
||||
wEnd := win[pos+length]
|
||||
wPos := win[pos:]
|
||||
minIndex := pos - windowSize
|
||||
|
||||
for i := prevHead; tries > 0; tries-- {
|
||||
if wEnd == win[i+length] {
|
||||
n := matchLen(win[i:], wPos, minMatchLook)
|
||||
|
||||
if n > length && (n > minMatchLength || pos-i <= 4096) {
|
||||
length = n
|
||||
offset = pos - i
|
||||
ok = true
|
||||
if n >= nice {
|
||||
// The match is good enough that we don't try to find a better one.
|
||||
break
|
||||
}
|
||||
wEnd = win[pos+n]
|
||||
}
|
||||
}
|
||||
if i == minIndex {
|
||||
// hashPrev[i & windowMask] has already been overwritten, so stop now.
|
||||
break
|
||||
}
|
||||
i = int(d.hashPrev[i&windowMask]) - d.hashOffset
|
||||
if i < minIndex || i < 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (d *compressor) writeStoredBlock(buf []byte) error {
|
||||
if d.w.writeStoredHeader(len(buf), false); d.w.err != nil {
|
||||
return d.w.err
|
||||
}
|
||||
d.w.writeBytes(buf)
|
||||
return d.w.err
|
||||
}
|
||||
|
||||
const hashmul = 0x1e35a7bd
|
||||
|
||||
// hash4 returns a hash representation of the first 4 bytes
|
||||
// of the supplied slice.
|
||||
// The caller must ensure that len(b) >= 4.
|
||||
func hash4(b []byte) uint32 {
|
||||
return ((uint32(b[3]) | uint32(b[2])<<8 | uint32(b[1])<<16 | uint32(b[0])<<24) * hashmul) >> (32 - hashBits)
|
||||
}
|
||||
|
||||
// bulkHash4 will compute hashes using the same
|
||||
// algorithm as hash4
|
||||
func bulkHash4(b []byte, dst []uint32) {
|
||||
if len(b) < minMatchLength {
|
||||
return
|
||||
}
|
||||
hb := uint32(b[3]) | uint32(b[2])<<8 | uint32(b[1])<<16 | uint32(b[0])<<24
|
||||
dst[0] = (hb * hashmul) >> (32 - hashBits)
|
||||
end := len(b) - minMatchLength + 1
|
||||
for i := 1; i < end; i++ {
|
||||
hb = (hb << 8) | uint32(b[i+3])
|
||||
dst[i] = (hb * hashmul) >> (32 - hashBits)
|
||||
}
|
||||
}
|
||||
|
||||
// matchLen returns the number of matching bytes in a and b
|
||||
// up to length 'max'. Both slices must be at least 'max'
|
||||
// bytes in size.
|
||||
func matchLen(a, b []byte, max int) int {
|
||||
a = a[:max]
|
||||
b = b[:len(a)]
|
||||
for i, av := range a {
|
||||
if b[i] != av {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return max
|
||||
}
|
||||
|
||||
// encSpeed will compress and store the currently added data,
|
||||
// if enough has been accumulated or we at the end of the stream.
|
||||
// Any error that occurred will be in d.err
|
||||
func (d *compressor) encSpeed() {
|
||||
// We only compress if we have maxStoreBlockSize.
|
||||
if d.windowEnd < maxStoreBlockSize {
|
||||
if !d.sync {
|
||||
return
|
||||
}
|
||||
|
||||
// Handle small sizes.
|
||||
if d.windowEnd < 128 {
|
||||
switch {
|
||||
case d.windowEnd == 0:
|
||||
return
|
||||
case d.windowEnd <= 16:
|
||||
d.err = d.writeStoredBlock(d.window[:d.windowEnd])
|
||||
default:
|
||||
d.w.writeBlockHuff(false, d.window[:d.windowEnd])
|
||||
d.err = d.w.err
|
||||
}
|
||||
d.windowEnd = 0
|
||||
d.bestSpeed.reset()
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
// Encode the block.
|
||||
d.tokens = d.bestSpeed.encode(d.tokens[:0], d.window[:d.windowEnd])
|
||||
|
||||
// If we removed less than 1/16th, Huffman compress the block.
|
||||
if len(d.tokens) > d.windowEnd-(d.windowEnd>>4) {
|
||||
d.w.writeBlockHuff(false, d.window[:d.windowEnd])
|
||||
} else {
|
||||
d.w.writeBlockDynamic(d.tokens, false, d.window[:d.windowEnd])
|
||||
}
|
||||
d.err = d.w.err
|
||||
d.windowEnd = 0
|
||||
}
|
||||
|
||||
func (d *compressor) initDeflate() {
|
||||
d.window = make([]byte, 2*windowSize)
|
||||
d.hashOffset = 1
|
||||
d.tokens = make([]token, 0, maxFlateBlockTokens+1)
|
||||
d.length = minMatchLength - 1
|
||||
d.offset = 0
|
||||
d.byteAvailable = false
|
||||
d.index = 0
|
||||
d.hash = 0
|
||||
d.chainHead = -1
|
||||
d.bulkHasher = bulkHash4
|
||||
}
|
||||
|
||||
func (d *compressor) deflate() {
|
||||
if d.windowEnd-d.index < minMatchLength+maxMatchLength && !d.sync {
|
||||
return
|
||||
}
|
||||
|
||||
d.maxInsertIndex = d.windowEnd - (minMatchLength - 1)
|
||||
if d.index < d.maxInsertIndex {
|
||||
d.hash = hash4(d.window[d.index : d.index+minMatchLength])
|
||||
}
|
||||
|
||||
Loop:
|
||||
for {
|
||||
if d.index > d.windowEnd {
|
||||
panic("index > windowEnd")
|
||||
}
|
||||
lookahead := d.windowEnd - d.index
|
||||
if lookahead < minMatchLength+maxMatchLength {
|
||||
if !d.sync {
|
||||
break Loop
|
||||
}
|
||||
if d.index > d.windowEnd {
|
||||
panic("index > windowEnd")
|
||||
}
|
||||
if lookahead == 0 {
|
||||
// Flush current output block if any.
|
||||
if d.byteAvailable {
|
||||
// There is still one pending token that needs to be flushed
|
||||
d.tokens = append(d.tokens, literalToken(uint32(d.window[d.index-1])))
|
||||
d.byteAvailable = false
|
||||
}
|
||||
if len(d.tokens) > 0 {
|
||||
if d.err = d.writeBlock(d.tokens, d.index); d.err != nil {
|
||||
return
|
||||
}
|
||||
d.tokens = d.tokens[:0]
|
||||
}
|
||||
break Loop
|
||||
}
|
||||
}
|
||||
if d.index < d.maxInsertIndex {
|
||||
// Update the hash
|
||||
d.hash = hash4(d.window[d.index : d.index+minMatchLength])
|
||||
hh := &d.hashHead[d.hash&hashMask]
|
||||
d.chainHead = int(*hh)
|
||||
d.hashPrev[d.index&windowMask] = uint32(d.chainHead)
|
||||
*hh = uint32(d.index + d.hashOffset)
|
||||
}
|
||||
prevLength := d.length
|
||||
prevOffset := d.offset
|
||||
d.length = minMatchLength - 1
|
||||
d.offset = 0
|
||||
minIndex := d.index - windowSize
|
||||
if minIndex < 0 {
|
||||
minIndex = 0
|
||||
}
|
||||
|
||||
if d.chainHead-d.hashOffset >= minIndex &&
|
||||
(d.fastSkipHashing != skipNever && lookahead > minMatchLength-1 ||
|
||||
d.fastSkipHashing == skipNever && lookahead > prevLength && prevLength < d.lazy) {
|
||||
if newLength, newOffset, ok := d.findMatch(d.index, d.chainHead-d.hashOffset, minMatchLength-1, lookahead); ok {
|
||||
d.length = newLength
|
||||
d.offset = newOffset
|
||||
}
|
||||
}
|
||||
if d.fastSkipHashing != skipNever && d.length >= minMatchLength ||
|
||||
d.fastSkipHashing == skipNever && prevLength >= minMatchLength && d.length <= prevLength {
|
||||
// There was a match at the previous step, and the current match is
|
||||
// not better. Output the previous match.
|
||||
if d.fastSkipHashing != skipNever {
|
||||
d.tokens = append(d.tokens, matchToken(uint32(d.length-baseMatchLength), uint32(d.offset-baseMatchOffset)))
|
||||
} else {
|
||||
d.tokens = append(d.tokens, matchToken(uint32(prevLength-baseMatchLength), uint32(prevOffset-baseMatchOffset)))
|
||||
}
|
||||
// Insert in the hash table all strings up to the end of the match.
|
||||
// index and index-1 are already inserted. If there is not enough
|
||||
// lookahead, the last two strings are not inserted into the hash
|
||||
// table.
|
||||
if d.length <= d.fastSkipHashing {
|
||||
var newIndex int
|
||||
if d.fastSkipHashing != skipNever {
|
||||
newIndex = d.index + d.length
|
||||
} else {
|
||||
newIndex = d.index + prevLength - 1
|
||||
}
|
||||
index := d.index
|
||||
for index++; index < newIndex; index++ {
|
||||
if index < d.maxInsertIndex {
|
||||
d.hash = hash4(d.window[index : index+minMatchLength])
|
||||
// Get previous value with the same hash.
|
||||
// Our chain should point to the previous value.
|
||||
hh := &d.hashHead[d.hash&hashMask]
|
||||
d.hashPrev[index&windowMask] = *hh
|
||||
// Set the head of the hash chain to us.
|
||||
*hh = uint32(index + d.hashOffset)
|
||||
}
|
||||
}
|
||||
d.index = index
|
||||
|
||||
if d.fastSkipHashing == skipNever {
|
||||
d.byteAvailable = false
|
||||
d.length = minMatchLength - 1
|
||||
}
|
||||
} else {
|
||||
// For matches this long, we don't bother inserting each individual
|
||||
// item into the table.
|
||||
d.index += d.length
|
||||
if d.index < d.maxInsertIndex {
|
||||
d.hash = hash4(d.window[d.index : d.index+minMatchLength])
|
||||
}
|
||||
}
|
||||
if len(d.tokens) == maxFlateBlockTokens {
|
||||
// The block includes the current character
|
||||
if d.err = d.writeBlock(d.tokens, d.index); d.err != nil {
|
||||
return
|
||||
}
|
||||
d.tokens = d.tokens[:0]
|
||||
}
|
||||
} else {
|
||||
if d.fastSkipHashing != skipNever || d.byteAvailable {
|
||||
i := d.index - 1
|
||||
if d.fastSkipHashing != skipNever {
|
||||
i = d.index
|
||||
}
|
||||
d.tokens = append(d.tokens, literalToken(uint32(d.window[i])))
|
||||
if len(d.tokens) == maxFlateBlockTokens {
|
||||
if d.err = d.writeBlock(d.tokens, i+1); d.err != nil {
|
||||
return
|
||||
}
|
||||
d.tokens = d.tokens[:0]
|
||||
}
|
||||
}
|
||||
d.index++
|
||||
if d.fastSkipHashing == skipNever {
|
||||
d.byteAvailable = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (d *compressor) fillStore(b []byte) int {
|
||||
n := copy(d.window[d.windowEnd:], b)
|
||||
d.windowEnd += n
|
||||
return n
|
||||
}
|
||||
|
||||
func (d *compressor) store() {
|
||||
if d.windowEnd > 0 && (d.windowEnd == maxStoreBlockSize || d.sync) {
|
||||
d.err = d.writeStoredBlock(d.window[:d.windowEnd])
|
||||
d.windowEnd = 0
|
||||
}
|
||||
}
|
||||
|
||||
// storeHuff compresses and stores the currently added data
|
||||
// when the d.window is full or we are at the end of the stream.
|
||||
// Any error that occurred will be in d.err
|
||||
func (d *compressor) storeHuff() {
|
||||
if d.windowEnd < len(d.window) && !d.sync || d.windowEnd == 0 {
|
||||
return
|
||||
}
|
||||
d.w.writeBlockHuff(false, d.window[:d.windowEnd])
|
||||
d.err = d.w.err
|
||||
d.windowEnd = 0
|
||||
}
|
||||
|
||||
func (d *compressor) write(b []byte) (n int, err error) {
|
||||
if d.err != nil {
|
||||
return 0, d.err
|
||||
}
|
||||
n = len(b)
|
||||
for len(b) > 0 {
|
||||
d.step(d)
|
||||
b = b[d.fill(d, b):]
|
||||
if d.err != nil {
|
||||
return 0, d.err
|
||||
}
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func (d *compressor) syncFlush() error {
|
||||
if d.err != nil {
|
||||
return d.err
|
||||
}
|
||||
d.sync = true
|
||||
d.step(d)
|
||||
if d.err == nil {
|
||||
d.w.writeStoredHeader(0, false)
|
||||
d.w.flush()
|
||||
d.err = d.w.err
|
||||
}
|
||||
d.sync = false
|
||||
return d.err
|
||||
}
|
||||
|
||||
func (d *compressor) init(w io.Writer, level int) (err error) {
|
||||
d.w = newHuffmanBitWriter(w)
|
||||
|
||||
switch {
|
||||
case level == NoCompression:
|
||||
d.window = make([]byte, maxStoreBlockSize)
|
||||
d.fill = (*compressor).fillStore
|
||||
d.step = (*compressor).store
|
||||
case level == HuffmanOnly:
|
||||
d.window = make([]byte, maxStoreBlockSize)
|
||||
d.fill = (*compressor).fillStore
|
||||
d.step = (*compressor).storeHuff
|
||||
case level == BestSpeed:
|
||||
d.compressionLevel = levels[level]
|
||||
d.window = make([]byte, maxStoreBlockSize)
|
||||
d.fill = (*compressor).fillStore
|
||||
d.step = (*compressor).encSpeed
|
||||
d.bestSpeed = newDeflateFast()
|
||||
d.tokens = make([]token, maxStoreBlockSize)
|
||||
case level == DefaultCompression:
|
||||
level = 6
|
||||
fallthrough
|
||||
case 2 <= level && level <= 9:
|
||||
d.compressionLevel = levels[level]
|
||||
d.initDeflate()
|
||||
d.fill = (*compressor).fillDeflate
|
||||
d.step = (*compressor).deflate
|
||||
default:
|
||||
return fmt.Errorf("flate: invalid compression level %d: want value in range [-2, 9]", level)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *compressor) reset(w io.Writer) {
|
||||
d.w.reset(w)
|
||||
d.sync = false
|
||||
d.err = nil
|
||||
switch d.compressionLevel.level {
|
||||
case NoCompression:
|
||||
d.windowEnd = 0
|
||||
case BestSpeed:
|
||||
d.windowEnd = 0
|
||||
d.tokens = d.tokens[:0]
|
||||
d.bestSpeed.reset()
|
||||
default:
|
||||
d.chainHead = -1
|
||||
for i := range d.hashHead {
|
||||
d.hashHead[i] = 0
|
||||
}
|
||||
for i := range d.hashPrev {
|
||||
d.hashPrev[i] = 0
|
||||
}
|
||||
d.hashOffset = 1
|
||||
d.index, d.windowEnd = 0, 0
|
||||
d.blockStart, d.byteAvailable = 0, false
|
||||
d.tokens = d.tokens[:0]
|
||||
d.length = minMatchLength - 1
|
||||
d.offset = 0
|
||||
d.hash = 0
|
||||
d.maxInsertIndex = 0
|
||||
}
|
||||
}
|
||||
|
||||
func (d *compressor) close() error {
|
||||
if d.err != nil {
|
||||
return d.err
|
||||
}
|
||||
d.sync = true
|
||||
d.step(d)
|
||||
if d.err != nil {
|
||||
return d.err
|
||||
}
|
||||
if d.w.writeStoredHeader(0, true); d.w.err != nil {
|
||||
return d.w.err
|
||||
}
|
||||
d.w.flush()
|
||||
return d.w.err
|
||||
}
|
||||
|
||||
// NewWriter returns a new Writer compressing data at the given level.
|
||||
// Following zlib, levels range from 1 (BestSpeed) to 9 (BestCompression);
|
||||
// higher levels typically run slower but compress more. Level 0
|
||||
// (NoCompression) does not attempt any compression; it only adds the
|
||||
// necessary DEFLATE framing.
|
||||
// Level -1 (DefaultCompression) uses the default compression level.
|
||||
// Level -2 (HuffmanOnly) will use Huffman compression only, giving
|
||||
// a very fast compression for all types of input, but sacrificing considerable
|
||||
// compression efficiency.
|
||||
//
|
||||
// If level is in the range [-2, 9] then the error returned will be nil.
|
||||
// Otherwise the error returned will be non-nil.
|
||||
func NewWriter(w io.Writer, level int) (*Writer, error) {
|
||||
var dw Writer
|
||||
if err := dw.d.init(w, level); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &dw, nil
|
||||
}
|
||||
|
||||
// NewWriterDict is like NewWriter but initializes the new
|
||||
// Writer with a preset dictionary. The returned Writer behaves
|
||||
// as if the dictionary had been written to it without producing
|
||||
// any compressed output. The compressed data written to w
|
||||
// can only be decompressed by a Reader initialized with the
|
||||
// same dictionary.
|
||||
func NewWriterDict(w io.Writer, level int, dict []byte) (*Writer, error) {
|
||||
dw := &dictWriter{w}
|
||||
zw, err := NewWriter(dw, level)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
zw.d.fillWindow(dict)
|
||||
zw.dict = append(zw.dict, dict...) // duplicate dictionary for Reset method.
|
||||
return zw, err
|
||||
}
|
||||
|
||||
type dictWriter struct {
|
||||
w io.Writer
|
||||
}
|
||||
|
||||
func (w *dictWriter) Write(b []byte) (n int, err error) {
|
||||
return w.w.Write(b)
|
||||
}
|
||||
|
||||
// A Writer takes data written to it and writes the compressed
|
||||
// form of that data to an underlying writer (see NewWriter).
|
||||
type Writer struct {
|
||||
d compressor
|
||||
dict []byte
|
||||
}
|
||||
|
||||
// Write writes data to w, which will eventually write the
|
||||
// compressed form of data to its underlying writer.
|
||||
func (w *Writer) Write(data []byte) (n int, err error) {
|
||||
return w.d.write(data)
|
||||
}
|
||||
|
||||
// Flush flushes any pending data to the underlying writer.
|
||||
// It is useful mainly in compressed network protocols, to ensure that
|
||||
// a remote reader has enough data to reconstruct a packet.
|
||||
// Flush does not return until the data has been written.
|
||||
// Calling Flush when there is no pending data still causes the Writer
|
||||
// to emit a sync marker of at least 4 bytes.
|
||||
// If the underlying writer returns an error, Flush returns that error.
|
||||
//
|
||||
// In the terminology of the zlib library, Flush is equivalent to Z_SYNC_FLUSH.
|
||||
func (w *Writer) Flush() error {
|
||||
// For more about flushing:
|
||||
// https://www.bolet.org/~pornin/deflate-flush.html
|
||||
return w.d.syncFlush()
|
||||
}
|
||||
|
||||
// Close flushes and closes the writer.
|
||||
func (w *Writer) Close() error {
|
||||
return w.d.close()
|
||||
}
|
||||
|
||||
// Reset discards the writer's state and makes it equivalent to
|
||||
// the result of NewWriter or NewWriterDict called with dst
|
||||
// and w's level and dictionary.
|
||||
func (w *Writer) Reset(dst io.Writer) {
|
||||
if dw, ok := w.d.w.writer.(*dictWriter); ok {
|
||||
// w was created with NewWriterDict
|
||||
dw.w = dst
|
||||
w.d.reset(dw)
|
||||
w.d.fillWindow(w.dict)
|
||||
} else {
|
||||
// w was created with NewWriter
|
||||
w.d.reset(dst)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,984 @@
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package flate
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"internal/testenv"
|
||||
"io"
|
||||
"math/rand"
|
||||
"os"
|
||||
"reflect"
|
||||
"runtime/debug"
|
||||
"sync"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type deflateTest struct {
|
||||
in []byte
|
||||
level int
|
||||
out []byte
|
||||
}
|
||||
|
||||
type deflateInflateTest struct {
|
||||
in []byte
|
||||
}
|
||||
|
||||
type reverseBitsTest struct {
|
||||
in uint16
|
||||
bitCount uint8
|
||||
out uint16
|
||||
}
|
||||
|
||||
var deflateTests = []*deflateTest{
|
||||
{[]byte{}, 0, []byte{1, 0, 0, 255, 255}},
|
||||
{[]byte{0x11}, -1, []byte{18, 4, 4, 0, 0, 255, 255}},
|
||||
{[]byte{0x11}, DefaultCompression, []byte{18, 4, 4, 0, 0, 255, 255}},
|
||||
{[]byte{0x11}, 4, []byte{18, 4, 4, 0, 0, 255, 255}},
|
||||
|
||||
{[]byte{0x11}, 0, []byte{0, 1, 0, 254, 255, 17, 1, 0, 0, 255, 255}},
|
||||
{[]byte{0x11, 0x12}, 0, []byte{0, 2, 0, 253, 255, 17, 18, 1, 0, 0, 255, 255}},
|
||||
{[]byte{0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11}, 0,
|
||||
[]byte{0, 8, 0, 247, 255, 17, 17, 17, 17, 17, 17, 17, 17, 1, 0, 0, 255, 255},
|
||||
},
|
||||
{[]byte{}, 2, []byte{1, 0, 0, 255, 255}},
|
||||
{[]byte{0x11}, 2, []byte{18, 4, 4, 0, 0, 255, 255}},
|
||||
{[]byte{0x11, 0x12}, 2, []byte{18, 20, 2, 4, 0, 0, 255, 255}},
|
||||
{[]byte{0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11}, 2, []byte{18, 132, 2, 64, 0, 0, 0, 255, 255}},
|
||||
{[]byte{}, 9, []byte{1, 0, 0, 255, 255}},
|
||||
{[]byte{0x11}, 9, []byte{18, 4, 4, 0, 0, 255, 255}},
|
||||
{[]byte{0x11, 0x12}, 9, []byte{18, 20, 2, 4, 0, 0, 255, 255}},
|
||||
{[]byte{0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11}, 9, []byte{18, 132, 2, 64, 0, 0, 0, 255, 255}},
|
||||
}
|
||||
|
||||
var deflateInflateTests = []*deflateInflateTest{
|
||||
{[]byte{}},
|
||||
{[]byte{0x11}},
|
||||
{[]byte{0x11, 0x12}},
|
||||
{[]byte{0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11}},
|
||||
{[]byte{0x11, 0x10, 0x13, 0x41, 0x21, 0x21, 0x41, 0x13, 0x87, 0x78, 0x13}},
|
||||
{largeDataChunk()},
|
||||
}
|
||||
|
||||
var reverseBitsTests = []*reverseBitsTest{
|
||||
{1, 1, 1},
|
||||
{1, 2, 2},
|
||||
{1, 3, 4},
|
||||
{1, 4, 8},
|
||||
{1, 5, 16},
|
||||
{17, 5, 17},
|
||||
{257, 9, 257},
|
||||
{29, 5, 23},
|
||||
}
|
||||
|
||||
func largeDataChunk() []byte {
|
||||
result := make([]byte, 100000)
|
||||
for i := range result {
|
||||
result[i] = byte(i * i & 0xFF)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func TestBulkHash4(t *testing.T) {
|
||||
for _, x := range deflateTests {
|
||||
y := x.out
|
||||
if len(y) < minMatchLength {
|
||||
continue
|
||||
}
|
||||
y = append(y, y...)
|
||||
for j := 4; j < len(y); j++ {
|
||||
y := y[:j]
|
||||
dst := make([]uint32, len(y)-minMatchLength+1)
|
||||
for i := range dst {
|
||||
dst[i] = uint32(i + 100)
|
||||
}
|
||||
bulkHash4(y, dst)
|
||||
for i, got := range dst {
|
||||
want := hash4(y[i:])
|
||||
if got != want && got == uint32(i)+100 {
|
||||
t.Errorf("Len:%d Index:%d, want 0x%08x but not modified", len(y), i, want)
|
||||
} else if got != want {
|
||||
t.Errorf("Len:%d Index:%d, got 0x%08x want:0x%08x", len(y), i, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeflate(t *testing.T) {
|
||||
for _, h := range deflateTests {
|
||||
var buf bytes.Buffer
|
||||
w, err := NewWriter(&buf, h.level)
|
||||
if err != nil {
|
||||
t.Errorf("NewWriter: %v", err)
|
||||
continue
|
||||
}
|
||||
w.Write(h.in)
|
||||
w.Close()
|
||||
if !bytes.Equal(buf.Bytes(), h.out) {
|
||||
t.Errorf("Deflate(%d, %x) = \n%#v, want \n%#v", h.level, h.in, buf.Bytes(), h.out)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A sparseReader returns a stream consisting of 0s followed by 1<<16 1s.
|
||||
// This tests missing hash references in a very large input.
|
||||
type sparseReader struct {
|
||||
l int64
|
||||
cur int64
|
||||
}
|
||||
|
||||
func (r *sparseReader) Read(b []byte) (n int, err error) {
|
||||
if r.cur >= r.l {
|
||||
return 0, io.EOF
|
||||
}
|
||||
n = len(b)
|
||||
cur := r.cur + int64(n)
|
||||
if cur > r.l {
|
||||
n -= int(cur - r.l)
|
||||
cur = r.l
|
||||
}
|
||||
for i := range b[0:n] {
|
||||
if r.cur+int64(i) >= r.l-1<<16 {
|
||||
b[i] = 1
|
||||
} else {
|
||||
b[i] = 0
|
||||
}
|
||||
}
|
||||
r.cur = cur
|
||||
return
|
||||
}
|
||||
|
||||
func TestVeryLongSparseChunk(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping sparse chunk during short test")
|
||||
}
|
||||
w, err := NewWriter(io.Discard, 1)
|
||||
if err != nil {
|
||||
t.Errorf("NewWriter: %v", err)
|
||||
return
|
||||
}
|
||||
if _, err = io.Copy(w, &sparseReader{l: 23e8}); err != nil {
|
||||
t.Errorf("Compress failed: %v", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
type syncBuffer struct {
|
||||
buf bytes.Buffer
|
||||
mu sync.RWMutex
|
||||
closed bool
|
||||
ready chan bool
|
||||
}
|
||||
|
||||
func newSyncBuffer() *syncBuffer {
|
||||
return &syncBuffer{ready: make(chan bool, 1)}
|
||||
}
|
||||
|
||||
func (b *syncBuffer) Read(p []byte) (n int, err error) {
|
||||
for {
|
||||
b.mu.RLock()
|
||||
n, err = b.buf.Read(p)
|
||||
b.mu.RUnlock()
|
||||
if n > 0 || b.closed {
|
||||
return
|
||||
}
|
||||
<-b.ready
|
||||
}
|
||||
}
|
||||
|
||||
func (b *syncBuffer) signal() {
|
||||
select {
|
||||
case b.ready <- true:
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
func (b *syncBuffer) Write(p []byte) (n int, err error) {
|
||||
n, err = b.buf.Write(p)
|
||||
b.signal()
|
||||
return
|
||||
}
|
||||
|
||||
func (b *syncBuffer) WriteMode() {
|
||||
b.mu.Lock()
|
||||
}
|
||||
|
||||
func (b *syncBuffer) ReadMode() {
|
||||
b.mu.Unlock()
|
||||
b.signal()
|
||||
}
|
||||
|
||||
func (b *syncBuffer) Close() error {
|
||||
b.closed = true
|
||||
b.signal()
|
||||
return nil
|
||||
}
|
||||
|
||||
func testSync(t *testing.T, level int, input []byte, name string) {
|
||||
if len(input) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
t.Logf("--testSync %d, %d, %s", level, len(input), name)
|
||||
buf := newSyncBuffer()
|
||||
buf1 := new(bytes.Buffer)
|
||||
buf.WriteMode()
|
||||
w, err := NewWriter(io.MultiWriter(buf, buf1), level)
|
||||
if err != nil {
|
||||
t.Errorf("NewWriter: %v", err)
|
||||
return
|
||||
}
|
||||
r := NewReader(buf)
|
||||
|
||||
// Write half the input and read back.
|
||||
for i := 0; i < 2; i++ {
|
||||
var lo, hi int
|
||||
if i == 0 {
|
||||
lo, hi = 0, (len(input)+1)/2
|
||||
} else {
|
||||
lo, hi = (len(input)+1)/2, len(input)
|
||||
}
|
||||
t.Logf("#%d: write %d-%d", i, lo, hi)
|
||||
if _, err := w.Write(input[lo:hi]); err != nil {
|
||||
t.Errorf("testSync: write: %v", err)
|
||||
return
|
||||
}
|
||||
if i == 0 {
|
||||
if err := w.Flush(); err != nil {
|
||||
t.Errorf("testSync: flush: %v", err)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if err := w.Close(); err != nil {
|
||||
t.Errorf("testSync: close: %v", err)
|
||||
}
|
||||
}
|
||||
buf.ReadMode()
|
||||
out := make([]byte, hi-lo+1)
|
||||
m, err := io.ReadAtLeast(r, out, hi-lo)
|
||||
t.Logf("#%d: read %d", i, m)
|
||||
if m != hi-lo || err != nil {
|
||||
t.Errorf("testSync/%d (%d, %d, %s): read %d: %d, %v (%d left)", i, level, len(input), name, hi-lo, m, err, buf.buf.Len())
|
||||
return
|
||||
}
|
||||
if !bytes.Equal(input[lo:hi], out[:hi-lo]) {
|
||||
t.Errorf("testSync/%d: read wrong bytes: %x vs %x", i, input[lo:hi], out[:hi-lo])
|
||||
return
|
||||
}
|
||||
// This test originally checked that after reading
|
||||
// the first half of the input, there was nothing left
|
||||
// in the read buffer (buf.buf.Len() != 0) but that is
|
||||
// not necessarily the case: the write Flush may emit
|
||||
// some extra framing bits that are not necessary
|
||||
// to process to obtain the first half of the uncompressed
|
||||
// data. The test ran correctly most of the time, because
|
||||
// the background goroutine had usually read even
|
||||
// those extra bits by now, but it's not a useful thing to
|
||||
// check.
|
||||
buf.WriteMode()
|
||||
}
|
||||
buf.ReadMode()
|
||||
out := make([]byte, 10)
|
||||
if n, err := r.Read(out); n > 0 || err != io.EOF {
|
||||
t.Errorf("testSync (%d, %d, %s): final Read: %d, %v (hex: %x)", level, len(input), name, n, err, out[0:n])
|
||||
}
|
||||
if buf.buf.Len() != 0 {
|
||||
t.Errorf("testSync (%d, %d, %s): extra data at end", level, len(input), name)
|
||||
}
|
||||
r.Close()
|
||||
|
||||
// stream should work for ordinary reader too
|
||||
r = NewReader(buf1)
|
||||
out, err = io.ReadAll(r)
|
||||
if err != nil {
|
||||
t.Errorf("testSync: read: %s", err)
|
||||
return
|
||||
}
|
||||
r.Close()
|
||||
if !bytes.Equal(input, out) {
|
||||
t.Errorf("testSync: decompress(compress(data)) != data: level=%d input=%s", level, name)
|
||||
}
|
||||
}
|
||||
|
||||
func testToFromWithLevelAndLimit(t *testing.T, level int, input []byte, name string, limit int) {
|
||||
var buffer bytes.Buffer
|
||||
w, err := NewWriter(&buffer, level)
|
||||
if err != nil {
|
||||
t.Errorf("NewWriter: %v", err)
|
||||
return
|
||||
}
|
||||
w.Write(input)
|
||||
w.Close()
|
||||
if limit > 0 && buffer.Len() > limit {
|
||||
t.Errorf("level: %d, len(compress(data)) = %d > limit = %d", level, buffer.Len(), limit)
|
||||
return
|
||||
}
|
||||
if limit > 0 {
|
||||
t.Logf("level: %d, size:%.2f%%, %d b\n", level, float64(buffer.Len()*100)/float64(limit), buffer.Len())
|
||||
}
|
||||
r := NewReader(&buffer)
|
||||
out, err := io.ReadAll(r)
|
||||
if err != nil {
|
||||
t.Errorf("read: %s", err)
|
||||
return
|
||||
}
|
||||
r.Close()
|
||||
if !bytes.Equal(input, out) {
|
||||
t.Errorf("decompress(compress(data)) != data: level=%d input=%s", level, name)
|
||||
return
|
||||
}
|
||||
testSync(t, level, input, name)
|
||||
}
|
||||
|
||||
func testToFromWithLimit(t *testing.T, input []byte, name string, limit [11]int) {
|
||||
for i := 0; i < 10; i++ {
|
||||
testToFromWithLevelAndLimit(t, i, input, name, limit[i])
|
||||
}
|
||||
// Test HuffmanCompression
|
||||
testToFromWithLevelAndLimit(t, -2, input, name, limit[10])
|
||||
}
|
||||
|
||||
func TestDeflateInflate(t *testing.T) {
|
||||
t.Parallel()
|
||||
for i, h := range deflateInflateTests {
|
||||
if testing.Short() && len(h.in) > 10000 {
|
||||
continue
|
||||
}
|
||||
testToFromWithLimit(t, h.in, fmt.Sprintf("#%d", i), [11]int{})
|
||||
}
|
||||
}
|
||||
|
||||
func TestReverseBits(t *testing.T) {
|
||||
for _, h := range reverseBitsTests {
|
||||
if v := reverseBits(h.in, h.bitCount); v != h.out {
|
||||
t.Errorf("reverseBits(%v,%v) = %v, want %v",
|
||||
h.in, h.bitCount, v, h.out)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type deflateInflateStringTest struct {
|
||||
filename string
|
||||
label string
|
||||
limit [11]int
|
||||
}
|
||||
|
||||
var deflateInflateStringTests = []deflateInflateStringTest{
|
||||
{
|
||||
"../testdata/e.txt",
|
||||
"2.718281828...",
|
||||
[...]int{100018, 50650, 50960, 51150, 50930, 50790, 50790, 50790, 50790, 50790, 43683},
|
||||
},
|
||||
{
|
||||
"../../testdata/Isaac.Newton-Opticks.txt",
|
||||
"Isaac.Newton-Opticks",
|
||||
[...]int{567248, 218338, 198211, 193152, 181100, 175427, 175427, 173597, 173422, 173422, 325240},
|
||||
},
|
||||
}
|
||||
|
||||
func TestDeflateInflateString(t *testing.T) {
|
||||
t.Parallel()
|
||||
if testing.Short() && testenv.Builder() == "" {
|
||||
t.Skip("skipping in short mode")
|
||||
}
|
||||
for _, test := range deflateInflateStringTests {
|
||||
gold, err := os.ReadFile(test.filename)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
testToFromWithLimit(t, gold, test.label, test.limit)
|
||||
if testing.Short() {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestReaderDict(t *testing.T) {
|
||||
const (
|
||||
dict = "hello world"
|
||||
text = "hello again world"
|
||||
)
|
||||
var b bytes.Buffer
|
||||
w, err := NewWriter(&b, 5)
|
||||
if err != nil {
|
||||
t.Fatalf("NewWriter: %v", err)
|
||||
}
|
||||
w.Write([]byte(dict))
|
||||
w.Flush()
|
||||
b.Reset()
|
||||
w.Write([]byte(text))
|
||||
w.Close()
|
||||
|
||||
r := NewReaderDict(&b, []byte(dict))
|
||||
data, err := io.ReadAll(r)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if string(data) != "hello again world" {
|
||||
t.Fatalf("read returned %q want %q", string(data), text)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriterDict(t *testing.T) {
|
||||
const (
|
||||
dict = "hello world"
|
||||
text = "hello again world"
|
||||
)
|
||||
var b bytes.Buffer
|
||||
w, err := NewWriter(&b, 5)
|
||||
if err != nil {
|
||||
t.Fatalf("NewWriter: %v", err)
|
||||
}
|
||||
w.Write([]byte(dict))
|
||||
w.Flush()
|
||||
b.Reset()
|
||||
w.Write([]byte(text))
|
||||
w.Close()
|
||||
|
||||
var b1 bytes.Buffer
|
||||
w, _ = NewWriterDict(&b1, 5, []byte(dict))
|
||||
w.Write([]byte(text))
|
||||
w.Close()
|
||||
|
||||
if !bytes.Equal(b1.Bytes(), b.Bytes()) {
|
||||
t.Fatalf("writer wrote %q want %q", b1.Bytes(), b.Bytes())
|
||||
}
|
||||
}
|
||||
|
||||
// See https://golang.org/issue/2508
|
||||
func TestRegression2508(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Logf("test disabled with -short")
|
||||
return
|
||||
}
|
||||
w, err := NewWriter(io.Discard, 1)
|
||||
if err != nil {
|
||||
t.Fatalf("NewWriter: %v", err)
|
||||
}
|
||||
buf := make([]byte, 1024)
|
||||
for i := 0; i < 131072; i++ {
|
||||
if _, err := w.Write(buf); err != nil {
|
||||
t.Fatalf("writer failed: %v", err)
|
||||
}
|
||||
}
|
||||
w.Close()
|
||||
}
|
||||
|
||||
func TestWriterReset(t *testing.T) {
|
||||
t.Parallel()
|
||||
for level := 0; level <= 9; level++ {
|
||||
if testing.Short() && level > 1 {
|
||||
break
|
||||
}
|
||||
w, err := NewWriter(io.Discard, level)
|
||||
if err != nil {
|
||||
t.Fatalf("NewWriter: %v", err)
|
||||
}
|
||||
buf := []byte("hello world")
|
||||
n := 1024
|
||||
if testing.Short() {
|
||||
n = 10
|
||||
}
|
||||
for i := 0; i < n; i++ {
|
||||
w.Write(buf)
|
||||
}
|
||||
w.Reset(io.Discard)
|
||||
|
||||
wref, err := NewWriter(io.Discard, level)
|
||||
if err != nil {
|
||||
t.Fatalf("NewWriter: %v", err)
|
||||
}
|
||||
|
||||
// DeepEqual doesn't compare functions.
|
||||
w.d.fill, wref.d.fill = nil, nil
|
||||
w.d.step, wref.d.step = nil, nil
|
||||
w.d.bulkHasher, wref.d.bulkHasher = nil, nil
|
||||
w.d.bestSpeed, wref.d.bestSpeed = nil, nil
|
||||
// hashMatch is always overwritten when used.
|
||||
copy(w.d.hashMatch[:], wref.d.hashMatch[:])
|
||||
if len(w.d.tokens) != 0 {
|
||||
t.Errorf("level %d Writer not reset after Reset. %d tokens were present", level, len(w.d.tokens))
|
||||
}
|
||||
// As long as the length is 0, we don't care about the content.
|
||||
w.d.tokens = wref.d.tokens
|
||||
|
||||
// We don't care if there are values in the window, as long as it is at d.index is 0
|
||||
w.d.window = wref.d.window
|
||||
if !reflect.DeepEqual(w, wref) {
|
||||
t.Errorf("level %d Writer not reset after Reset", level)
|
||||
}
|
||||
}
|
||||
|
||||
levels := []int{0, 1, 2, 5, 9}
|
||||
for _, level := range levels {
|
||||
t.Run(fmt.Sprint(level), func(t *testing.T) {
|
||||
testResetOutput(t, level, nil)
|
||||
})
|
||||
}
|
||||
|
||||
t.Run("dict", func(t *testing.T) {
|
||||
for _, level := range levels {
|
||||
t.Run(fmt.Sprint(level), func(t *testing.T) {
|
||||
testResetOutput(t, level, nil)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func testResetOutput(t *testing.T, level int, dict []byte) {
|
||||
writeData := func(w *Writer) {
|
||||
msg := []byte("now is the time for all good gophers")
|
||||
w.Write(msg)
|
||||
w.Flush()
|
||||
|
||||
hello := []byte("hello world")
|
||||
for i := 0; i < 1024; i++ {
|
||||
w.Write(hello)
|
||||
}
|
||||
|
||||
fill := bytes.Repeat([]byte("x"), 65000)
|
||||
w.Write(fill)
|
||||
}
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
var w *Writer
|
||||
var err error
|
||||
if dict == nil {
|
||||
w, err = NewWriter(buf, level)
|
||||
} else {
|
||||
w, err = NewWriterDict(buf, level, dict)
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("NewWriter: %v", err)
|
||||
}
|
||||
|
||||
writeData(w)
|
||||
w.Close()
|
||||
out1 := buf.Bytes()
|
||||
|
||||
buf2 := new(bytes.Buffer)
|
||||
w.Reset(buf2)
|
||||
writeData(w)
|
||||
w.Close()
|
||||
out2 := buf2.Bytes()
|
||||
|
||||
if len(out1) != len(out2) {
|
||||
t.Errorf("got %d, expected %d bytes", len(out2), len(out1))
|
||||
return
|
||||
}
|
||||
if !bytes.Equal(out1, out2) {
|
||||
mm := 0
|
||||
for i, b := range out1[:len(out2)] {
|
||||
if b != out2[i] {
|
||||
t.Errorf("mismatch index %d: %#02x, expected %#02x", i, out2[i], b)
|
||||
}
|
||||
mm++
|
||||
if mm == 10 {
|
||||
t.Fatal("Stopping")
|
||||
}
|
||||
}
|
||||
}
|
||||
t.Logf("got %d bytes", len(out1))
|
||||
}
|
||||
|
||||
// TestBestSpeed tests that round-tripping through deflate and then inflate
|
||||
// recovers the original input. The Write sizes are near the thresholds in the
|
||||
// compressor.encSpeed method (0, 16, 128), as well as near maxStoreBlockSize
|
||||
// (65535).
|
||||
func TestBestSpeed(t *testing.T) {
|
||||
t.Parallel()
|
||||
abc := make([]byte, 128)
|
||||
for i := range abc {
|
||||
abc[i] = byte(i)
|
||||
}
|
||||
abcabc := bytes.Repeat(abc, 131072/len(abc))
|
||||
var want []byte
|
||||
|
||||
testCases := [][]int{
|
||||
{65536, 0},
|
||||
{65536, 1},
|
||||
{65536, 1, 256},
|
||||
{65536, 1, 65536},
|
||||
{65536, 14},
|
||||
{65536, 15},
|
||||
{65536, 16},
|
||||
{65536, 16, 256},
|
||||
{65536, 16, 65536},
|
||||
{65536, 127},
|
||||
{65536, 128},
|
||||
{65536, 128, 256},
|
||||
{65536, 128, 65536},
|
||||
{65536, 129},
|
||||
{65536, 65536, 256},
|
||||
{65536, 65536, 65536},
|
||||
}
|
||||
|
||||
for i, tc := range testCases {
|
||||
if i >= 3 && testing.Short() {
|
||||
break
|
||||
}
|
||||
for _, firstN := range []int{1, 65534, 65535, 65536, 65537, 131072} {
|
||||
tc[0] = firstN
|
||||
outer:
|
||||
for _, flush := range []bool{false, true} {
|
||||
buf := new(bytes.Buffer)
|
||||
want = want[:0]
|
||||
|
||||
w, err := NewWriter(buf, BestSpeed)
|
||||
if err != nil {
|
||||
t.Errorf("i=%d, firstN=%d, flush=%t: NewWriter: %v", i, firstN, flush, err)
|
||||
continue
|
||||
}
|
||||
for _, n := range tc {
|
||||
want = append(want, abcabc[:n]...)
|
||||
if _, err := w.Write(abcabc[:n]); err != nil {
|
||||
t.Errorf("i=%d, firstN=%d, flush=%t: Write: %v", i, firstN, flush, err)
|
||||
continue outer
|
||||
}
|
||||
if !flush {
|
||||
continue
|
||||
}
|
||||
if err := w.Flush(); err != nil {
|
||||
t.Errorf("i=%d, firstN=%d, flush=%t: Flush: %v", i, firstN, flush, err)
|
||||
continue outer
|
||||
}
|
||||
}
|
||||
if err := w.Close(); err != nil {
|
||||
t.Errorf("i=%d, firstN=%d, flush=%t: Close: %v", i, firstN, flush, err)
|
||||
continue
|
||||
}
|
||||
|
||||
r := NewReader(buf)
|
||||
got, err := io.ReadAll(r)
|
||||
if err != nil {
|
||||
t.Errorf("i=%d, firstN=%d, flush=%t: ReadAll: %v", i, firstN, flush, err)
|
||||
continue
|
||||
}
|
||||
r.Close()
|
||||
|
||||
if !bytes.Equal(got, want) {
|
||||
t.Errorf("i=%d, firstN=%d, flush=%t: corruption during deflate-then-inflate", i, firstN, flush)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var errIO = errors.New("IO error")
|
||||
|
||||
// failWriter fails with errIO exactly at the nth call to Write.
|
||||
type failWriter struct{ n int }
|
||||
|
||||
func (w *failWriter) Write(b []byte) (int, error) {
|
||||
w.n--
|
||||
if w.n == -1 {
|
||||
return 0, errIO
|
||||
}
|
||||
return len(b), nil
|
||||
}
|
||||
|
||||
func TestWriterPersistentError(t *testing.T) {
|
||||
t.Parallel()
|
||||
d, err := os.ReadFile("../../testdata/Isaac.Newton-Opticks.txt")
|
||||
if err != nil {
|
||||
t.Fatalf("ReadFile: %v", err)
|
||||
}
|
||||
d = d[:10000] // Keep this test short
|
||||
|
||||
zw, err := NewWriter(nil, DefaultCompression)
|
||||
if err != nil {
|
||||
t.Fatalf("NewWriter: %v", err)
|
||||
}
|
||||
|
||||
// Sweep over the threshold at which an error is returned.
|
||||
// The variable i makes it such that the ith call to failWriter.Write will
|
||||
// return errIO. Since failWriter errors are not persistent, we must ensure
|
||||
// that flate.Writer errors are persistent.
|
||||
for i := 0; i < 1000; i++ {
|
||||
fw := &failWriter{i}
|
||||
zw.Reset(fw)
|
||||
|
||||
_, werr := zw.Write(d)
|
||||
cerr := zw.Close()
|
||||
if werr != errIO && werr != nil {
|
||||
t.Errorf("test %d, mismatching Write error: got %v, want %v", i, werr, errIO)
|
||||
}
|
||||
if cerr != errIO && fw.n < 0 {
|
||||
t.Errorf("test %d, mismatching Close error: got %v, want %v", i, cerr, errIO)
|
||||
}
|
||||
if fw.n >= 0 {
|
||||
// At this point, the failure threshold was sufficiently high enough
|
||||
// that we wrote the whole stream without any errors.
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestBestSpeedMatch(t *testing.T) {
|
||||
t.Parallel()
|
||||
cases := []struct {
|
||||
previous, current []byte
|
||||
t, s, want int32
|
||||
}{{
|
||||
previous: []byte{0, 0, 0, 1, 2},
|
||||
current: []byte{3, 4, 5, 0, 1, 2, 3, 4, 5},
|
||||
t: -3,
|
||||
s: 3,
|
||||
want: 6,
|
||||
}, {
|
||||
previous: []byte{0, 0, 0, 1, 2},
|
||||
current: []byte{2, 4, 5, 0, 1, 2, 3, 4, 5},
|
||||
t: -3,
|
||||
s: 3,
|
||||
want: 3,
|
||||
}, {
|
||||
previous: []byte{0, 0, 0, 1, 1},
|
||||
current: []byte{3, 4, 5, 0, 1, 2, 3, 4, 5},
|
||||
t: -3,
|
||||
s: 3,
|
||||
want: 2,
|
||||
}, {
|
||||
previous: []byte{0, 0, 0, 1, 2},
|
||||
current: []byte{2, 2, 2, 2, 1, 2, 3, 4, 5},
|
||||
t: -1,
|
||||
s: 0,
|
||||
want: 4,
|
||||
}, {
|
||||
previous: []byte{0, 0, 0, 1, 2, 3, 4, 5, 2, 2},
|
||||
current: []byte{2, 2, 2, 2, 1, 2, 3, 4, 5},
|
||||
t: -7,
|
||||
s: 4,
|
||||
want: 5,
|
||||
}, {
|
||||
previous: []byte{9, 9, 9, 9, 9},
|
||||
current: []byte{2, 2, 2, 2, 1, 2, 3, 4, 5},
|
||||
t: -1,
|
||||
s: 0,
|
||||
want: 0,
|
||||
}, {
|
||||
previous: []byte{9, 9, 9, 9, 9},
|
||||
current: []byte{9, 2, 2, 2, 1, 2, 3, 4, 5},
|
||||
t: 0,
|
||||
s: 1,
|
||||
want: 0,
|
||||
}, {
|
||||
previous: []byte{},
|
||||
current: []byte{9, 2, 2, 2, 1, 2, 3, 4, 5},
|
||||
t: -5,
|
||||
s: 1,
|
||||
want: 0,
|
||||
}, {
|
||||
previous: []byte{},
|
||||
current: []byte{9, 2, 2, 2, 1, 2, 3, 4, 5},
|
||||
t: -1,
|
||||
s: 1,
|
||||
want: 0,
|
||||
}, {
|
||||
previous: []byte{},
|
||||
current: []byte{2, 2, 2, 2, 1, 2, 3, 4, 5},
|
||||
t: 0,
|
||||
s: 1,
|
||||
want: 3,
|
||||
}, {
|
||||
previous: []byte{3, 4, 5},
|
||||
current: []byte{3, 4, 5},
|
||||
t: -3,
|
||||
s: 0,
|
||||
want: 3,
|
||||
}, {
|
||||
previous: make([]byte, 1000),
|
||||
current: make([]byte, 1000),
|
||||
t: -1000,
|
||||
s: 0,
|
||||
want: maxMatchLength - 4,
|
||||
}, {
|
||||
previous: make([]byte, 200),
|
||||
current: make([]byte, 500),
|
||||
t: -200,
|
||||
s: 0,
|
||||
want: maxMatchLength - 4,
|
||||
}, {
|
||||
previous: make([]byte, 200),
|
||||
current: make([]byte, 500),
|
||||
t: 0,
|
||||
s: 1,
|
||||
want: maxMatchLength - 4,
|
||||
}, {
|
||||
previous: make([]byte, maxMatchLength-4),
|
||||
current: make([]byte, 500),
|
||||
t: -(maxMatchLength - 4),
|
||||
s: 0,
|
||||
want: maxMatchLength - 4,
|
||||
}, {
|
||||
previous: make([]byte, 200),
|
||||
current: make([]byte, 500),
|
||||
t: -200,
|
||||
s: 400,
|
||||
want: 100,
|
||||
}, {
|
||||
previous: make([]byte, 10),
|
||||
current: make([]byte, 500),
|
||||
t: 200,
|
||||
s: 400,
|
||||
want: 100,
|
||||
}}
|
||||
for i, c := range cases {
|
||||
e := deflateFast{prev: c.previous}
|
||||
got := e.matchLen(c.s, c.t, c.current)
|
||||
if got != c.want {
|
||||
t.Errorf("Test %d: match length, want %d, got %d", i, c.want, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestBestSpeedMaxMatchOffset(t *testing.T) {
|
||||
t.Parallel()
|
||||
const abc, xyz = "abcdefgh", "stuvwxyz"
|
||||
for _, matchBefore := range []bool{false, true} {
|
||||
for _, extra := range []int{0, inputMargin - 1, inputMargin, inputMargin + 1, 2 * inputMargin} {
|
||||
for offsetAdj := -5; offsetAdj <= +5; offsetAdj++ {
|
||||
report := func(desc string, err error) {
|
||||
t.Errorf("matchBefore=%t, extra=%d, offsetAdj=%d: %s%v",
|
||||
matchBefore, extra, offsetAdj, desc, err)
|
||||
}
|
||||
|
||||
offset := maxMatchOffset + offsetAdj
|
||||
|
||||
// Make src to be a []byte of the form
|
||||
// "%s%s%s%s%s" % (abc, zeros0, xyzMaybe, abc, zeros1)
|
||||
// where:
|
||||
// zeros0 is approximately maxMatchOffset zeros.
|
||||
// xyzMaybe is either xyz or the empty string.
|
||||
// zeros1 is between 0 and 30 zeros.
|
||||
// The difference between the two abc's will be offset, which
|
||||
// is maxMatchOffset plus or minus a small adjustment.
|
||||
src := make([]byte, offset+len(abc)+extra)
|
||||
copy(src, abc)
|
||||
if !matchBefore {
|
||||
copy(src[offset-len(xyz):], xyz)
|
||||
}
|
||||
copy(src[offset:], abc)
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
w, err := NewWriter(buf, BestSpeed)
|
||||
if err != nil {
|
||||
report("NewWriter: ", err)
|
||||
continue
|
||||
}
|
||||
if _, err := w.Write(src); err != nil {
|
||||
report("Write: ", err)
|
||||
continue
|
||||
}
|
||||
if err := w.Close(); err != nil {
|
||||
report("Writer.Close: ", err)
|
||||
continue
|
||||
}
|
||||
|
||||
r := NewReader(buf)
|
||||
dst, err := io.ReadAll(r)
|
||||
r.Close()
|
||||
if err != nil {
|
||||
report("ReadAll: ", err)
|
||||
continue
|
||||
}
|
||||
|
||||
if !bytes.Equal(dst, src) {
|
||||
report("", fmt.Errorf("bytes differ after round-tripping"))
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestBestSpeedShiftOffsets(t *testing.T) {
|
||||
// Test if shiftoffsets properly preserves matches and resets out-of-range matches
|
||||
// seen in https://github.com/golang/go/issues/4142
|
||||
enc := newDeflateFast()
|
||||
|
||||
// testData may not generate internal matches.
|
||||
testData := make([]byte, 32)
|
||||
rng := rand.New(rand.NewSource(0))
|
||||
for i := range testData {
|
||||
testData[i] = byte(rng.Uint32())
|
||||
}
|
||||
|
||||
// Encode the testdata with clean state.
|
||||
// Second part should pick up matches from the first block.
|
||||
wantFirstTokens := len(enc.encode(nil, testData))
|
||||
wantSecondTokens := len(enc.encode(nil, testData))
|
||||
|
||||
if wantFirstTokens <= wantSecondTokens {
|
||||
t.Fatalf("test needs matches between inputs to be generated")
|
||||
}
|
||||
// Forward the current indicator to before wraparound.
|
||||
enc.cur = bufferReset - int32(len(testData))
|
||||
|
||||
// Part 1 before wrap, should match clean state.
|
||||
got := len(enc.encode(nil, testData))
|
||||
if wantFirstTokens != got {
|
||||
t.Errorf("got %d, want %d tokens", got, wantFirstTokens)
|
||||
}
|
||||
|
||||
// Verify we are about to wrap.
|
||||
if enc.cur != bufferReset {
|
||||
t.Errorf("got %d, want e.cur to be at bufferReset (%d)", enc.cur, bufferReset)
|
||||
}
|
||||
|
||||
// Part 2 should match clean state as well even if wrapped.
|
||||
got = len(enc.encode(nil, testData))
|
||||
if wantSecondTokens != got {
|
||||
t.Errorf("got %d, want %d token", got, wantSecondTokens)
|
||||
}
|
||||
|
||||
// Verify that we wrapped.
|
||||
if enc.cur >= bufferReset {
|
||||
t.Errorf("want e.cur to be < bufferReset (%d), got %d", bufferReset, enc.cur)
|
||||
}
|
||||
|
||||
// Forward the current buffer, leaving the matches at the bottom.
|
||||
enc.cur = bufferReset
|
||||
enc.shiftOffsets()
|
||||
|
||||
// Ensure that no matches were picked up.
|
||||
got = len(enc.encode(nil, testData))
|
||||
if wantFirstTokens != got {
|
||||
t.Errorf("got %d, want %d tokens", got, wantFirstTokens)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMaxStackSize(t *testing.T) {
|
||||
// This test must not run in parallel with other tests as debug.SetMaxStack
|
||||
// affects all goroutines.
|
||||
n := debug.SetMaxStack(1 << 16)
|
||||
defer debug.SetMaxStack(n)
|
||||
|
||||
var wg sync.WaitGroup
|
||||
defer wg.Wait()
|
||||
|
||||
b := make([]byte, 1<<20)
|
||||
for level := HuffmanOnly; level <= BestCompression; level++ {
|
||||
// Run in separate goroutine to increase probability of stack regrowth.
|
||||
wg.Add(1)
|
||||
go func(level int) {
|
||||
defer wg.Done()
|
||||
zw, err := NewWriter(io.Discard, level)
|
||||
if err != nil {
|
||||
t.Errorf("level %d, NewWriter() = %v, want nil", level, err)
|
||||
}
|
||||
if n, err := zw.Write(b); n != len(b) || err != nil {
|
||||
t.Errorf("level %d, Write() = (%d, %v), want (%d, nil)", level, n, err, len(b))
|
||||
}
|
||||
if err := zw.Close(); err != nil {
|
||||
t.Errorf("level %d, Close() = %v, want nil", level, err)
|
||||
}
|
||||
zw.Reset(io.Discard)
|
||||
}(level)
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user