├── .circleci └── config.yml ├── LICENSE.txt ├── Makefile ├── README.md ├── deviceid ├── atsamd21.go ├── atsamd51.go ├── main.go ├── nrf52840.go └── stm32f405.go ├── go.mod ├── go.sum ├── img └── wiokey-0.1.0.jpg ├── pininterrupt ├── README.md ├── main.go └── st7032 │ ├── registers.go │ └── st7032.go ├── wioterminal ├── buttons │ └── main.go ├── buzzer │ └── main.go ├── goroutines │ ├── README.md │ ├── label.go │ ├── main.go │ └── wioterminal_goroutines.uf2 ├── initialize │ ├── ili9341.go │ ├── ntp.go │ └── rtl8720dn.go ├── ir │ └── main.go ├── light_sensor │ └── main.go ├── lis3dh │ └── main.go ├── microphone │ └── main.go ├── mqtt │ └── main.go ├── qspi_flash │ ├── console.go │ └── main.go ├── sample │ ├── README.md │ ├── main.go │ └── wioterminal_tinygo_sample.uf2 ├── usbcdc │ ├── README.md │ ├── cmd │ │ └── wio-client │ │ │ └── main.go │ └── main.go ├── webclient │ ├── init_other.go │ ├── init_wioterminal.go │ ├── main.go │ └── server │ │ ├── server.go │ │ └── server.go.html └── wiobadge │ ├── m5stack.go │ ├── main.go │ ├── name.png │ ├── qrcode_x.png │ └── wioterminal.go └── xiao-ble ├── README.md ├── ble-led-client-xiao └── main.go ├── ble-led-client └── main.go └── ble-led-server └── main.go /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | jobs: 3 | build: 4 | docker: 5 | - image: tinygo/tinygo-dev 6 | steps: 7 | - checkout 8 | - run: | 9 | apt install -y bluez 10 | - run: tinygo version 11 | - run: 12 | name: "Enforce Go Formatted Code" 13 | command: make fmt-check 14 | - run: 15 | name: "Run build and smoke tests" 16 | command: make smoketest 17 | 18 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright 2021 sago35 2 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 3 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 4 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 5 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | smoketest: 2 | tinygo build -o /tmp/test.hex -size short -target pyportal ./pininterrupt 3 | tinygo build -o /tmp/test.hex -size short -target wioterminal ./wioterminal/buttons 4 | tinygo build -o /tmp/test.hex -size short -target wioterminal ./wioterminal/buzzer 5 | tinygo build -o /tmp/test.hex -size short -target wioterminal ./wioterminal/goroutines 6 | tinygo build -o /tmp/test.hex -size short -target wioterminal ./wioterminal/ir 7 | tinygo build -o /tmp/test.hex -size short -target wioterminal ./wioterminal/light_sensor 8 | tinygo build -o /tmp/test.hex -size short -target wioterminal ./wioterminal/lis3dh 9 | tinygo build -o /tmp/test.hex -size short -target wioterminal ./wioterminal/microphone 10 | tinygo build -o /tmp/test.hex -size short -target wioterminal ./wioterminal/mqtt 11 | tinygo build -o /tmp/test.hex -size short -target wioterminal ./wioterminal/qspi_flash 12 | tinygo build -o /tmp/test.hex -size short -target wioterminal ./wioterminal/sample 13 | tinygo build -o /tmp/test.hex -size short -target wioterminal ./wioterminal/usbcdc 14 | tinygo build -o /tmp/test.hex -size short -target wioterminal ./wioterminal/webclient 15 | go build -o /tmp/test ./wioterminal/webclient 16 | go build -o /tmp/test ./wioterminal/webclient/server 17 | tinygo build -o /tmp/test.hex -size short -target wioterminal ./deviceid 18 | go build -o /tmp/test ./wioterminal/usbcdc/cmd/wio-client 19 | tinygo build -o /tmp/test.hex --target xiao-ble --size short ./xiao-ble/ble-led-server 20 | go build -o /tmp/test ./xiao-ble/ble-led-client 21 | tinygo build -o /tmp/test.hex --target xiao-ble --size short ./xiao-ble/ble-led-client 22 | tinygo build -o /tmp/test.hex --target xiao-ble --size short ./xiao-ble/ble-led-client-xiao 23 | 24 | fmt-check: 25 | @unformatted=$$(gofmt -l `find . -name "*.go"`); [ -z "$$unformatted" ] && exit 0; echo "Unformatted:"; for fn in $$unformatted; do echo " $$fn"; done; exit 1 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![CircleCI](https://circleci.com/gh/sago35/tinygo-examples/tree/main.svg?style=svg)](https://circleci.com/gh/sago35/tinygo-examples/tree/main) 2 | 3 | # TinyGo examples 4 | 5 | ## wioterminal goroutines 6 | 7 | TinyGo example of wioterminal. 8 | https://github.com/sago35/tinygo-examples/tree/main/wioterminal/goroutines 9 | 10 | [![](https://img.youtube.com/vi/-dJ-o2cH_Fk/0.jpg)](https://www.youtube.com/watch?v=-dJ-o2cH_Fk) 11 | 12 | ## wioterminal sample 13 | 14 | TinyGo example of wioterminal. 15 | https://github.com/sago35/tinygo-examples/tree/main/wioterminal/sample 16 | 17 | [![](https://img.youtube.com/vi/9IpI9rUMXOs/0.jpg)](https://www.youtube.com/watch?v=9IpI9rUMXOs) 18 | 19 | ## wioterminal USB HID Keyboard 20 | 21 | Using tinygo-keyboard, wioterminal becomes a USB HID Keyboard. 22 | Keymap settings can be changed from a web browser using Vial. 23 | 24 | * [wiokey](https://github.com/sago35/tinygo-keyboard) example can also be run on wioterminal alone 25 | * https://vial.rocks/ 26 | 27 | ![](./img/wiokey-0.1.0.jpg) 28 | 29 | 30 | ## wioterminal other examples 31 | 32 | There are many examples below. 33 | 34 | * [./wioterminal/](./wioterminal/) 35 | * [./wioterminal/buttons/](./wioterminal/buttons/) 36 | * [./wioterminal/buzzer/](./wioterminal/buzzer/) 37 | * [./wioterminal/goroutines/](./wioterminal/goroutines/) 38 | * [./wioterminal/gpio/](./wioterminal/gpio/) 39 | * [./wioterminal/ir/](./wioterminal/ir/) 40 | * [./wioterminal/light_sensor/](./wioterminal/light_sensor/) 41 | * [./wioterminal/lis3dh/](./wioterminal/lis3dh/) 42 | * [./wioterminal/microphone/](./wioterminal/microphone/) 43 | * [./wioterminal/mqtt/](./wioterminal/mqtt/) 44 | * [./wioterminal/qspi_flash/](./wioterminal/qspi_flash/) 45 | * [./wioterminal/sample/](./wioterminal/sample/) 46 | * [./wioterminal/usbcdc/](./wioterminal/usbcdc/) 47 | * [./wioterminal/webclient/](./wioterminal/webclient/) 48 | 49 | ## xiao-ble examples 50 | 51 | TinyGo example of XIAO BLE. 52 | This is a Demo using Bluetooth. 53 | 54 | * [./xiao-ble/](./xiao-ble/) 55 | 56 | [![](https://img.youtube.com/vi/HWBxuMbNUTI/0.jpg)](https://www.youtube.com/watch?v=HWBxuMbNUTI) 57 | 58 | 59 | 60 | ## wioterminal initialize 61 | 62 | `initialize.Wifi(ssid, password)` initializes RTL8720DN and configures WiFi. 63 | 64 | * [./wioterminal/initialize/](./wioterminal/initialize/) 65 | 66 | ## pinintterrupt 67 | 68 | Example of combining pin interrupts and goroutine in TinyGo. 69 | https://github.com/sago35/tinygo-examples/tree/main/pininterrupt 70 | 71 | [![](https://img.youtube.com/vi/A-EA5iqDp7k/0.jpg)](https://www.youtube.com/watch?v=A-EA5iqDp7k) 72 | 73 | ## DeviceID / Serial Number 74 | 75 | This is an example of reading the DeviceID. 76 | In other words, it is an example of reading data from a specific address of a microcontroller. 77 | 78 | * [./deviceid/](./deviceid/) 79 | 80 | The following microcontrollers are supported. 81 | 82 | * atsamd51 83 | * atsamd21 84 | * nrf52840 85 | * stm32f405 86 | 87 | ## LICENSE 88 | 89 | MIT 90 | -------------------------------------------------------------------------------- /deviceid/atsamd21.go: -------------------------------------------------------------------------------- 1 | //go:build atsamd21 2 | // +build atsamd21 3 | 4 | package main 5 | 6 | import ( 7 | "unsafe" 8 | ) 9 | 10 | func deviceid() ([]uint32, error) { 11 | buf := [4]uint32{} 12 | 13 | //lint:ignore 14 | buf[0] = *(*uint32)(unsafe.Pointer(uintptr(0x0080A00C))) 15 | buf[1] = *(*uint32)(unsafe.Pointer(uintptr(0x0080A040))) 16 | buf[2] = *(*uint32)(unsafe.Pointer(uintptr(0x0080A044))) 17 | buf[3] = *(*uint32)(unsafe.Pointer(uintptr(0x0080A048))) 18 | 19 | return buf[:], nil 20 | } 21 | -------------------------------------------------------------------------------- /deviceid/atsamd51.go: -------------------------------------------------------------------------------- 1 | //go:build atsamd51 2 | // +build atsamd51 3 | 4 | package main 5 | 6 | import ( 7 | "unsafe" 8 | ) 9 | 10 | func deviceid() ([]uint32, error) { 11 | buf := [4]uint32{} 12 | 13 | //lint:ignore 14 | buf[0] = *(*uint32)(unsafe.Pointer(uintptr(0x008061FC))) 15 | buf[1] = *(*uint32)(unsafe.Pointer(uintptr(0x00806010))) 16 | buf[2] = *(*uint32)(unsafe.Pointer(uintptr(0x00806014))) 17 | buf[3] = *(*uint32)(unsafe.Pointer(uintptr(0x00806018))) 18 | 19 | return buf[:], nil 20 | } 21 | -------------------------------------------------------------------------------- /deviceid/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "time" 7 | ) 8 | 9 | func main() { 10 | err := run() 11 | for err != nil { 12 | log.Fatal(err) 13 | time.Sleep(10 * time.Second) 14 | } 15 | } 16 | 17 | func run() error { 18 | buf, err := deviceid() 19 | if err != nil { 20 | return err 21 | } 22 | 23 | for { 24 | for i := range buf { 25 | if i == 0 { 26 | fmt.Printf("%08X", buf[i]) 27 | } else { 28 | fmt.Printf(" %08X", buf[i]) 29 | } 30 | } 31 | fmt.Printf("\r\n") 32 | time.Sleep(5 * time.Second) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /deviceid/nrf52840.go: -------------------------------------------------------------------------------- 1 | //go:build stm32f405 2 | // +build stm32f405 3 | 4 | package main 5 | 6 | import ( 7 | "unsafe" 8 | ) 9 | 10 | func deviceid() ([]uint32, error) { 11 | buf := [3]uint32{} 12 | 13 | //lint:ignore 14 | buf[0] = *(*uint32)(unsafe.Pointer(uintptr(0x1FFF7A10))) 15 | buf[1] = *(*uint32)(unsafe.Pointer(uintptr(0x1FFF7A14))) 16 | buf[2] = *(*uint32)(unsafe.Pointer(uintptr(0x1FFF7A18))) 17 | 18 | return buf[:], nil 19 | } 20 | -------------------------------------------------------------------------------- /deviceid/stm32f405.go: -------------------------------------------------------------------------------- 1 | //go:build nrf52840 2 | // +build nrf52840 3 | 4 | package main 5 | 6 | import ( 7 | "unsafe" 8 | ) 9 | 10 | func deviceid() ([]uint32, error) { 11 | buf := [2]uint32{} 12 | 13 | //lint:ignore 14 | buf[0] = *(*uint32)(unsafe.Pointer(uintptr(0x10000060))) 15 | buf[1] = *(*uint32)(unsafe.Pointer(uintptr(0x10000064))) 16 | 17 | return buf[:], nil 18 | } 19 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/sago35/tinygo-examples 2 | 3 | go 1.14 4 | 5 | require ( 6 | github.com/sago35/tinygo-keyboard v0.0.0-20240228114517-697a66616c9a // indirect 7 | go.bug.st/serial v1.6.1 8 | tinygo.org/x/bluetooth v0.7.0 9 | tinygo.org/x/drivers v0.27.0 10 | tinygo.org/x/tinyfont v0.4.0 11 | tinygo.org/x/tinyfs v0.2.0 // indirect 12 | ) 13 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 2 | github.com/bgould/http v0.0.0-20190627042742-d268792bdee7/go.mod h1:BTqvVegvwifopl4KTEDth6Zezs9eR+lCWhvGKvkxJHE= 3 | github.com/bgould/tinygo-rotary-encoder v0.0.0-20231106003644-94bb14d88946 h1:WpPlCYe9fY1nvOxoaIql88KewO1pMMv+/mdcC8zpnsI= 4 | github.com/bgould/tinygo-rotary-encoder v0.0.0-20231106003644-94bb14d88946/go.mod h1:vozFQeh667GysGEvJ6GKutjAOilleV5usLdxYan+XYM= 5 | github.com/creack/goselect v0.1.2 h1:2DNy14+JPjRBgPzAd1thbQp4BSIihxcBf0IXhQXDRa0= 6 | github.com/creack/goselect v0.1.2/go.mod h1:a/NhLweNvqIYMuxcMOuWY516Cimucms3DglDzQP3hKY= 7 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 8 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 9 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 10 | github.com/eclipse/paho.mqtt.golang v1.2.0 h1:1F8mhG9+aO5/xpdtFkW4SxOJB67ukuDC3t2y2qayIX0= 11 | github.com/eclipse/paho.mqtt.golang v1.2.0/go.mod h1:H9keYFcgq3Qr5OUJm/JZI/i6U7joQ8SYLhZwfeOo6Ts= 12 | github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= 13 | github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= 14 | github.com/frankban/quicktest v1.10.2/go.mod h1:K+q6oSqb0W0Ininfk863uOk1lMy69l/P6txr3mVT54s= 15 | github.com/glerchundi/subcommands v0.0.0-20181212083838-923a6ccb11f8/go.mod h1:r0g3O7Y5lrWXgDfcFBRgnAKzjmPgTzwoMC2ieB345FY= 16 | github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= 17 | github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= 18 | github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= 19 | github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= 20 | github.com/godbus/dbus/v5 v5.0.3 h1:ZqHaoEF7TBzh4jzPmqVhE/5A1z9of6orkAe5uHoAeME= 21 | github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= 22 | github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 23 | github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 24 | github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= 25 | github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= 26 | github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 27 | github.com/hajimehoshi/go-jisx0208 v1.0.0/go.mod h1:yYxEStHL7lt9uL+AbdWgW9gBumwieDoZCiB1f/0X0as= 28 | github.com/itchio/lzma v0.0.0-20190703113020-d3e24e3e3d49/go.mod h1:avNrevQMli1pYPsz1+HIHMvx95pk6O+6otbWqCZPeZI= 29 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 30 | github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 31 | github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= 32 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 33 | github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= 34 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 35 | github.com/muka/go-bluetooth v0.0.0-20220830075246-0746e3a1ea53 h1:zfLHhuGzmSbthZ00FfbEjgAHUOOj7NGiITojMTCFy6U= 36 | github.com/muka/go-bluetooth v0.0.0-20220830075246-0746e3a1ea53/go.mod h1:dMCjicU6vRBk34dqOmIZm0aod6gUwZXOXzBROqGous0= 37 | github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= 38 | github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= 39 | github.com/paypal/gatt v0.0.0-20151011220935-4ae819d591cf/go.mod h1:+AwQL2mK3Pd3S+TUwg0tYQjid0q1txyNUJuuSmz8Kdk= 40 | github.com/pelletier/go-toml v1.6.0/go.mod h1:5N711Q9dKgbdkxHL+MEfF31hpT7l0S0s/t2kKREewys= 41 | github.com/peterbourgon/ff/v3 v3.1.2/go.mod h1:XNJLY8EIl6MjMVjBS4F0+G0LYoAqs0DTa4rmHHukKDE= 42 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 43 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 44 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 45 | github.com/sago35/go-bdf v0.0.0-20200313142241-6c17821c91c4/go.mod h1:rOebXGuMLsXhZAC6mF/TjxONsm45498ZyzVhel++6KM= 46 | github.com/sago35/tinygo-keyboard v0.0.0-20240228114517-697a66616c9a h1:xKgBomw59ncwytq4Bei6U4/ThCDlegr1fvL93qX732k= 47 | github.com/sago35/tinygo-keyboard v0.0.0-20240228114517-697a66616c9a/go.mod h1:2CMM+1mjzUy3n5eLFLDbpFoC4LslyViOA1q8cqqVLkY= 48 | github.com/saltosystems/winrt-go v0.0.0-20220826130236-ddc8202da421 h1:eOgynOew0HzvLwtAsughGzqkrcuTJ6XFpT7+WNCuRNU= 49 | github.com/saltosystems/winrt-go v0.0.0-20220826130236-ddc8202da421/go.mod h1:UvKm1lyhg+8ehk99i8g5Q7AX1LXUJgks0lRyAkG/ahQ= 50 | github.com/saltosystems/winrt-go v0.0.0-20230510070731-e096b9afa761 h1:xEscoMxTrGSpdho1mP9VnGsK0DGhXKwm0qP7kYcjgrI= 51 | github.com/saltosystems/winrt-go v0.0.0-20230510070731-e096b9afa761/go.mod h1:UvKm1lyhg+8ehk99i8g5Q7AX1LXUJgks0lRyAkG/ahQ= 52 | github.com/sirupsen/logrus v1.5.0/go.mod h1:+F7Ogzej0PZc/94MaYx/nvG9jOFMD2osvC3s+Squfpo= 53 | github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= 54 | github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= 55 | github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= 56 | github.com/soypat/natiu-mqtt v0.5.1/go.mod h1:xEta+cwop9izVCW7xOx2W+ct9PRMqr0gNVkvBPnQTc4= 57 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 58 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 59 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 60 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 61 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 62 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 63 | github.com/stretchr/testify v1.7.5 h1:s5PTfem8p8EbKQOctVV53k6jCJt3UX4IEJzwh+C324Q= 64 | github.com/stretchr/testify v1.7.5/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 65 | github.com/suapapa/go_eddystone v1.3.1/go.mod h1:bXC11TfJOS+3g3q/Uzd7FKd5g62STQEfeEIhcKe4Qy8= 66 | github.com/tdakkota/win32metadata v0.1.0/go.mod h1:77e6YvX0LIVW+O81fhWLnXAxxcyu/wdZdG7iwed7Fyk= 67 | github.com/tinygo-org/cbgo v0.0.4 h1:3D76CRYbH03Rudi8sEgs/YO0x3JIMdyq8jlQtk/44fU= 68 | github.com/tinygo-org/cbgo v0.0.4/go.mod h1:7+HgWIHd4nbAz0ESjGlJ1/v9LDU1Ox8MGzP9mah/fLk= 69 | github.com/valyala/fastjson v1.6.3/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY= 70 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 71 | github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= 72 | github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 73 | go.bug.st/serial v1.3.5 h1:k50SqGZCnHZ2MiBQgzccXWG+kd/XpOs1jUljpDDKzaE= 74 | go.bug.st/serial v1.3.5/go.mod h1:z8CesKorE90Qr/oRSJiEuvzYRKol9r/anJZEb5kt304= 75 | go.bug.st/serial v1.6.1 h1:VSSWmUxlj1T/YlRo2J104Zv3wJFrjHIl/T3NeruWAHY= 76 | go.bug.st/serial v1.6.1/go.mod h1:UABfsluHAiaNI+La2iESysd9Vetq7VRdpxvjx7CmmOE= 77 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 78 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 79 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 80 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 81 | golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= 82 | golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= 83 | golang.org/x/exp v0.0.0-20231226003508-02704c960a9b h1:kLiC65FbiHWFAOu+lxwNPujcsl8VYyTYYEZnsOO1WK4= 84 | golang.org/x/exp v0.0.0-20231226003508-02704c960a9b/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI= 85 | golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= 86 | golang.org/x/image v0.0.0-20220617043117-41969df76e82/go.mod h1:doUCurBvlfPMKfmIpRIywoHmhN3VyhnoFDbvIEWF4hY= 87 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 88 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= 89 | golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= 90 | golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= 91 | golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= 92 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 93 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 94 | golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 95 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 96 | golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 97 | golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f h1:OfiFi4JbukWwe3lzw+xunroH1mnC1e2Gy5cxNJApiSY= 98 | golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 99 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 100 | golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= 101 | golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= 102 | golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= 103 | golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= 104 | golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= 105 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 106 | golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 107 | golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 108 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 109 | golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 110 | golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= 111 | golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= 112 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 113 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 114 | golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 115 | golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 116 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 117 | golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 118 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 119 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 120 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 121 | golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 122 | golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 123 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 124 | golang.org/x/sys v0.0.0-20220624220833-87e55d714810/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 125 | golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 126 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 127 | golang.org/x/sys v0.0.0-20220829200755-d48e67d00261 h1:v6hYoSR9T5oet+pMXwUWkbiVqx/63mlHjefrHmxwfeY= 128 | golang.org/x/sys v0.0.0-20220829200755-d48e67d00261/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 129 | golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 130 | golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 131 | golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 132 | golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 133 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 134 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 135 | golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= 136 | golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= 137 | golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= 138 | golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= 139 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 140 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 141 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 142 | golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= 143 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 144 | golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 145 | golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= 146 | golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= 147 | golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= 148 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 149 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 150 | golang.org/x/tools v0.0.0-20200925191224-5d1fdd8fa346/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= 151 | golang.org/x/tools v0.1.11/go.mod h1:SgwaegtQh8clINPpECJMqnxLv9I09HLqnW3RMqW0CA4= 152 | golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= 153 | golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= 154 | golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= 155 | golang.org/x/tools v0.16.0/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0= 156 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 157 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 158 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 159 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 160 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 161 | gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= 162 | gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 163 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 164 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 165 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 166 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 167 | tinygo.org/x/bluetooth v0.6.0 h1:5RTUh28WBtWfRtwFcsDcdiCvlSWr9F7fHxRikQZW/Io= 168 | tinygo.org/x/bluetooth v0.6.0/go.mod h1:tiW1IiKOupcsvM2CX0PwLsf6aZRL+ciSIqP2YlgYOtQ= 169 | tinygo.org/x/bluetooth v0.7.0 h1:7lU0VrauwccbLvb6AKHf4ZedFlxZIFwwqHWQD2fLWvA= 170 | tinygo.org/x/bluetooth v0.7.0/go.mod h1:2hZPpfPDMR7Vvi6yMvyi83o8gYXeMKKsLfUnvmtBcjM= 171 | tinygo.org/x/drivers v0.14.0/go.mod h1:uT2svMq3EpBZpKkGO+NQHjxjGf1f42ra4OnMMwQL2aI= 172 | tinygo.org/x/drivers v0.15.1/go.mod h1:uT2svMq3EpBZpKkGO+NQHjxjGf1f42ra4OnMMwQL2aI= 173 | tinygo.org/x/drivers v0.16.0/go.mod h1:uT2svMq3EpBZpKkGO+NQHjxjGf1f42ra4OnMMwQL2aI= 174 | tinygo.org/x/drivers v0.19.0/go.mod h1:uJD/l1qWzxzLx+vcxaW0eY464N5RAgFi1zTVzASFdqI= 175 | tinygo.org/x/drivers v0.21.0/go.mod h1:uJD/l1qWzxzLx+vcxaW0eY464N5RAgFi1zTVzASFdqI= 176 | tinygo.org/x/drivers v0.23.0 h1:fUy4OmLOWWYCOzDp/83Qewej1Q+YgUpwkm11e7gxUc0= 177 | tinygo.org/x/drivers v0.23.0/go.mod h1:J4+51Li1kcfL5F93kmnDWEEzQF3bLGz0Am3Q7E2a8/E= 178 | tinygo.org/x/drivers v0.25.0 h1:MFnec5lY8Sxk1bIfqQWsflIbxcpAFbohWhg/qZ7psdM= 179 | tinygo.org/x/drivers v0.25.0/go.mod h1:v+mXaA4cgpz/YZJ3ZPm/86bYQJAXTaYtMkHlVwbodbw= 180 | tinygo.org/x/drivers v0.27.0 h1:TEGk1lQvEhXxfvpEhUu+pwmCnhtldPI+hpHlO9VYixI= 181 | tinygo.org/x/drivers v0.27.0/go.mod h1:q/mU8G/wz821p8xXqbkBACOlmZFDHXd//DnYnCW+dDQ= 182 | tinygo.org/x/tinydraw v0.3.0/go.mod h1:Yz0vLSP2rHsIKpLYkEmLnE+2zyhhITu2LxiVtLRiW6I= 183 | tinygo.org/x/tinyfont v0.2.1/go.mod h1:eLqnYSrFRjt5STxWaMeOWJTzrKhXqpWw7nU3bPfKOAM= 184 | tinygo.org/x/tinyfont v0.3.0/go.mod h1:+TV5q0KpwSGRWnN+ITijsIhrWYJkoUCp9MYELjKpAXk= 185 | tinygo.org/x/tinyfont v0.3.1-0.20220718115734-9b135c3a5561 h1:yz54kNGVMHWIUwtL0OiTFuI/bV3L6Zvn51NVbVyew5s= 186 | tinygo.org/x/tinyfont v0.3.1-0.20220718115734-9b135c3a5561/go.mod h1:7HVksKE/PAuHAR4kbmdd1k2ho/cOJ2WUDpi3LySNDLI= 187 | tinygo.org/x/tinyfont v0.4.0 h1:XexPKEKiHInf6p4CMCJwsIheVPY0T46HUs6ictYyZfE= 188 | tinygo.org/x/tinyfont v0.4.0/go.mod h1:7nVj3j3geqBoPDzpFukAhF1C8AP9YocMsZy0HSAcGCA= 189 | tinygo.org/x/tinyfs v0.1.0/go.mod h1:ysc8Y92iHfhTXeyEM9+c7zviUQ4fN9UCFgSOFfMWv20= 190 | tinygo.org/x/tinyfs v0.2.0/go.mod h1:6ZHYdvB3sFYeMB3ypmXZCNEnFwceKc61ADYTYHpep1E= 191 | tinygo.org/x/tinyterm v0.1.0/go.mod h1:/DDhNnGwNF2/tNgHywvyZuCGnbH3ov49Z/6e8LPLRR4= 192 | -------------------------------------------------------------------------------- /img/wiokey-0.1.0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sago35/tinygo-examples/347475af179e3759ae2f1b509df4d36593da15ab/img/wiokey-0.1.0.jpg -------------------------------------------------------------------------------- /pininterrupt/README.md: -------------------------------------------------------------------------------- 1 | # TinyGo examples 2 | 3 | ## pinintterrupt 4 | 5 | Example of combining pin interrupts and goroutine in TinyGo. 6 | https://github.com/sago35/tinygo-examples/tree/master/pininterrupt 7 | 8 | [![](https://img.youtube.com/vi/A-EA5iqDp7k/0.jpg)](https://www.youtube.com/watch?v=A-EA5iqDp7k) 9 | 10 | info: 11 | pin interrupt is still in the PR stage. 12 | 13 | * https://github.com/tinygo-org/tinygo/pull/1094 14 | * https://github.com/tinygo-org/tinygo/pull/1111 15 | 16 | ## Build 17 | 18 | ``` 19 | tinygo flash -target pyportal -size short github.com/sago35/tinygo-examples/pininterrupt 20 | ``` 21 | 22 | or 23 | 24 | ``` 25 | tinygo build -o app.uf2 -target pyportal -size short github.com/sago35/tinygo-examples/pininterrupt 26 | ``` 27 | 28 | ## Environment 29 | 30 | ``` 31 | PyPortal 32 | D3 : Switch (with pull-up) 33 | D4 : LED 34 | I2C : AE-AQM0802A (I2C 8 x 2 character LCD with st7032) 35 | ``` 36 | -------------------------------------------------------------------------------- /pininterrupt/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "machine" 6 | "runtime/volatile" 7 | "time" 8 | 9 | "github.com/sago35/tinygo-examples/pininterrupt/st7032" 10 | ) 11 | 12 | /* 13 | PyPortal 14 | D3 : Switch (with pull-up) 15 | D4 : LED 16 | I2C : AE-AQM0802A (I2C 8 x 2 character LCD with st7032) 17 | */ 18 | 19 | var ( 20 | led1 = machine.LED 21 | led2 = machine.D3 22 | button = machine.D4 23 | aqm0802 = st7032.New(machine.I2C0, 0x3E) 24 | ) 25 | 26 | // scrollp is a function to scroll str 27 | func scrollp(d *st7032.Device, lineno uint8, str string) { 28 | current := ` ` 29 | for i := 0; i < len(str); i++ { 30 | aqm0802.SetCursor(0, lineno) 31 | aqm0802.Print(current) 32 | time.Sleep(200 * time.Millisecond) 33 | current = current[1:] + string(str[i]) 34 | } 35 | for i := 0; i < 9; i++ { 36 | aqm0802.SetCursor(0, lineno) 37 | aqm0802.Print(current) 38 | time.Sleep(250 * time.Millisecond) 39 | current = current[1:] + ` ` 40 | } 41 | } 42 | 43 | // lv is used to create the first line of the LCD. 44 | type lv byte 45 | 46 | func (d lv) String() string { 47 | buf := make([]byte, 8) 48 | for i := 0; i < 8; i++ { 49 | if (d & 0x80) > 0 { 50 | buf[i] = byte('\xFF') 51 | } else { 52 | buf[i] = byte('_') 53 | } 54 | d <<= 1 55 | } 56 | return string(buf) 57 | } 58 | 59 | func (d *lv) Add(b bool) { 60 | if b { 61 | *d <<= 1 62 | *d += 1 63 | } else { 64 | *d <<= 1 65 | } 66 | } 67 | 68 | // pinInterruptChan notifies an interrupt to a pin via chan bool. 69 | // p.Get() can get the button state, but the button state at the time of getting it may be old. 70 | // Therefore, variables of type volatile.Register8 are used to manage them in the function. 71 | func pinInterruptChan(pin machine.Pin) <-chan bool { 72 | var state volatile.Register8 73 | ch := make(chan bool, 3) 74 | 75 | pin.SetInterrupt(machine.PinToggle, func(p machine.Pin) { 76 | b := false 77 | if state.Get() != 1 { 78 | state.Set(1) 79 | b = true 80 | } else { 81 | state.Set(0) 82 | } 83 | select { 84 | case ch <- b: 85 | default: 86 | } 87 | }) 88 | 89 | return ch 90 | } 91 | 92 | func main() { 93 | chCnt1 := make(chan uint32) 94 | chCnt2 := make(chan uint32, 1) 95 | chDisp := make(chan lv, 3) 96 | 97 | led1.Configure(machine.PinConfig{Mode: machine.PinOutput}) 98 | led2.Configure(machine.PinConfig{Mode: machine.PinOutput}) 99 | 100 | button.Configure(machine.PinConfig{Mode: machine.PinInput}) 101 | chBtn := pinInterruptChan(button) 102 | 103 | machine.I2C0.Configure(machine.I2CConfig{ 104 | Frequency: machine.TWI_FREQ_400KHZ, 105 | }) 106 | 107 | aqm0802.Configure() 108 | aqm0802.SetContrast(5) 109 | 110 | scrollp(aqm0802, 0, "TinyGo demo") 111 | time.Sleep(500 * time.Millisecond) 112 | aqm0802.Clear() 113 | 114 | go timer77ms(chCnt1, led2) 115 | go timer500ms(chCnt2) 116 | go disp(chBtn, chDisp, led1) 117 | 118 | for { 119 | select { 120 | case d := <-chDisp: 121 | aqm0802.SetCursor(0, 0) 122 | aqm0802.Print(d.String()) 123 | 124 | case cnt := <-chCnt1: 125 | aqm0802.SetCursor(0, 1) 126 | aqm0802.Print(fmt.Sprintf("%4d", cnt)) 127 | 128 | case cnt := <-chCnt2: 129 | aqm0802.SetCursor(4, 1) 130 | aqm0802.Print(fmt.Sprintf("%4d", cnt)) 131 | } 132 | time.Sleep(1 * time.Millisecond) 133 | } 134 | } 135 | 136 | func timer77ms(ch chan<- uint32, led machine.Pin) { 137 | cnt := uint32(0) 138 | for { 139 | ch <- cnt 140 | cnt++ 141 | 142 | led.Toggle() 143 | time.Sleep(77 * time.Millisecond) 144 | } 145 | } 146 | 147 | func timer500ms(ch chan<- uint32) { 148 | cnt := uint32(0) 149 | for { 150 | ch <- cnt 151 | cnt++ 152 | 153 | time.Sleep(500 * time.Millisecond) 154 | } 155 | } 156 | 157 | func disp(ch <-chan bool, chDisp chan<- lv, led machine.Pin) { 158 | s := time.Now() 159 | e := time.Now() 160 | d := lv(0xFF) 161 | btn := false 162 | for { 163 | select { 164 | case btn = <-ch: 165 | led.Set(btn) 166 | default: 167 | } 168 | 169 | e = time.Now() 170 | if e.UnixNano()-s.UnixNano() > 100*1000*1000 { 171 | d.Add(btn) 172 | chDisp <- d 173 | s = e 174 | } 175 | time.Sleep(1 * time.Millisecond) 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /pininterrupt/st7032/registers.go: -------------------------------------------------------------------------------- 1 | package st7032 2 | 3 | // Registers 4 | const ( 5 | // commands 6 | LCD_CLEARDISPLAY = 0x01 7 | LCD_RETURNHOME = 0x02 8 | LCD_ENTRYMODESET = 0x04 9 | LCD_DISPLAYCONTROL = 0x08 10 | LCD_CURSORSHIFT = 0x10 11 | LCD_FUNCTIONSET = 0x20 12 | LCD_SETCGRAMADDR = 0x40 13 | LCD_SETDDRAMADDR = 0x80 14 | 15 | LCD_EX_SETBIASOSC = 0x10 // Bias selection / Internal OSC frequency adjust 16 | LCD_EX_SETICONRAMADDR = 0x40 // Set ICON RAM address 17 | LCD_EX_POWICONCONTRASTH = 0x50 // Power / ICON control / Contrast set(high byte) 18 | LCD_EX_FOLLOWERCONTROL = 0x60 // Follower control 19 | LCD_EX_CONTRASTSETL = 0x70 // Contrast set(low byte) 20 | 21 | // flags for display entry mode 22 | LCD_ENTRYRIGHT = 0x00 23 | LCD_ENTRYLEFT = 0x02 24 | LCD_ENTRYSHIFTINCREMENT = 0x01 25 | LCD_ENTRYSHIFTDECREMENT = 0x00 26 | 27 | // flags for display on/off control 28 | LCD_DISPLAYON = 0x04 29 | LCD_DISPLAYOFF = 0x00 30 | LCD_CURSORON = 0x02 31 | LCD_CURSOROFF = 0x00 32 | LCD_BLINKON = 0x01 33 | LCD_BLINKOFF = 0x00 34 | 35 | // flags for display/cursor shift 36 | LCD_DISPLAYMOVE = 0x08 37 | LCD_CURSORMOVE = 0x00 38 | LCD_MOVERIGHT = 0x04 39 | LCD_MOVELEFT = 0x00 40 | 41 | // flags for function set 42 | LCD_8BITMODE = 0x10 43 | LCD_4BITMODE = 0x00 44 | LCD_2LINE = 0x08 45 | LCD_1LINE = 0x00 46 | LCD_5x10DOTS = 0x04 47 | LCD_5x8DOTS = 0x00 48 | LCD_EX_INSTRUCTION = 0x01 // IS: instruction table select 49 | 50 | // flags for Bias selection 51 | LCD_BIAS_1_4 = 0x08 // bias will be 1/4 52 | LCD_BIAS_1_5 = 0x00 // bias will be 1/5 53 | 54 | // flags Power / ICON control / Contrast set(high byte) 55 | LCD_ICON_ON = 0x08 // ICON display on 56 | LCD_ICON_OFF = 0x00 // ICON display off 57 | LCD_BOOST_ON = 0x04 // booster circuit is turn on 58 | LCD_BOOST_OFF = 0x00 // booster circuit is turn off 59 | LCD_OSC_122HZ = 0x00 // 122Hz@3.0V 60 | LCD_OSC_131HZ = 0x01 // 131Hz@3.0V 61 | LCD_OSC_144HZ = 0x02 // 144Hz@3.0V 62 | LCD_OSC_161HZ = 0x03 // 161Hz@3.0V 63 | LCD_OSC_183HZ = 0x04 // 183Hz@3.0V 64 | LCD_OSC_221HZ = 0x05 // 221Hz@3.0V 65 | LCD_OSC_274HZ = 0x06 // 274Hz@3.0V 66 | LCD_OSC_347HZ = 0x07 // 347Hz@3.0V 67 | 68 | // flags Follower control 69 | LCD_FOLLOWER_ON = 0x08 // internal follower circuit is turn on 70 | LCD_FOLLOWER_OFF = 0x00 // internal follower circuit is turn off 71 | LCD_RAB_1_00 = 0x00 // 1+(Rb/Ra)=1.00 72 | LCD_RAB_1_25 = 0x01 // 1+(Rb/Ra)=1.25 73 | LCD_RAB_1_50 = 0x02 // 1+(Rb/Ra)=1.50 74 | LCD_RAB_1_80 = 0x03 // 1+(Rb/Ra)=1.80 75 | LCD_RAB_2_00 = 0x04 // 1+(Rb/Ra)=2.00 76 | LCD_RAB_2_50 = 0x05 // 1+(Rb/Ra)=2.50 77 | LCD_RAB_3_00 = 0x06 // 1+(Rb/Ra)=3.00 78 | LCD_RAB_3_75 = 0x07 // 1+(Rb/Ra)=3.75 79 | ) 80 | 81 | //https://github.com/tomozh/arduino_ST7032/blob/master/ST7032.h 82 | -------------------------------------------------------------------------------- /pininterrupt/st7032/st7032.go: -------------------------------------------------------------------------------- 1 | package st7032 2 | 3 | import ( 4 | "fmt" 5 | "machine" 6 | "time" 7 | ) 8 | 9 | type Device struct { 10 | bus *machine.I2C 11 | buf []byte 12 | addr uint8 13 | 14 | displayFunction uint8 15 | displayControl uint8 16 | displayMode uint8 17 | 18 | cols uint8 19 | lines uint8 20 | currline uint8 21 | numlines uint8 22 | } 23 | 24 | func New(i2c *machine.I2C, address uint8) *Device { 25 | return &Device{ 26 | bus: i2c, 27 | buf: make([]byte, 2), 28 | addr: address, 29 | } 30 | } 31 | 32 | func (d *Device) Configure() { 33 | d.cols = 8 34 | d.lines = 2 35 | d.currline = 0 36 | d.numlines = d.lines 37 | 38 | d.displayFunction = LCD_8BITMODE | LCD_1LINE | LCD_5x8DOTS 39 | 40 | if d.lines > 1 { 41 | d.displayFunction |= LCD_2LINE 42 | } 43 | 44 | // power on and external reset 45 | // wait > 40ms 46 | time.Sleep(40 * time.Millisecond) 47 | 48 | d.normalFunctionSet() 49 | 50 | d.extendFunctionSet() 51 | d.command(LCD_EX_SETBIASOSC | LCD_BIAS_1_5 | LCD_OSC_183HZ) // 1/5bias, OSC=183Hz@3.0V 52 | d.command(LCD_EX_CONTRASTSETL) // Contrast set 53 | d.command(LCD_EX_POWICONCONTRASTH | LCD_ICON_OFF | LCD_BOOST_ON | 0x02) // Power/ICON control/Contrast set 54 | d.command(LCD_EX_FOLLOWERCONTROL | LCD_FOLLOWER_ON | LCD_RAB_2_00) // internal follower circuit is turn on 55 | time.Sleep(300 * time.Millisecond) // Wait time >200ms (for power stable) 56 | d.normalFunctionSet() 57 | 58 | d.displayControl = 0x00 59 | d.setDisplayControl(LCD_DISPLAYON | LCD_CURSOROFF | LCD_BLINKOFF) 60 | 61 | d.Clear() 62 | 63 | d.displayMode = 0x00 64 | //d.setEntryMode(LCD_ENTRYLEFT | LCD_ENTRYSHIFTDECREMENT) 65 | } 66 | 67 | func (d *Device) Clear() { 68 | d.command(LCD_CLEARDISPLAY) 69 | time.Sleep((3 + 2) * time.Millisecond) 70 | } 71 | 72 | func (d *Device) setDisplayControl(setBit uint8) { 73 | d.displayControl |= setBit 74 | d.command(LCD_DISPLAYCONTROL | d.displayControl) 75 | } 76 | 77 | func resetDisplayControl(resetBit uint8) { 78 | //_displaycontrol &= ~resetBit; 79 | //command(LCD_DISPLAYCONTROL | _displaycontrol) 80 | } 81 | 82 | func (d *Device) setEntryMode(setBit uint8) { 83 | d.displayMode |= setBit 84 | d.command(LCD_ENTRYMODESET | d.displayMode) 85 | } 86 | 87 | func (d *Device) resetEntryMode(resetBit uint8) { 88 | //_displaymode &= ~resetBit; 89 | //command(LCD_ENTRYMODESET | _displaymode) 90 | } 91 | 92 | func (d *Device) normalFunctionSet() { 93 | d.command(LCD_FUNCTIONSET | d.displayFunction) 94 | } 95 | 96 | func (d *Device) extendFunctionSet() { 97 | d.command(LCD_FUNCTIONSET | d.displayFunction | LCD_EX_INSTRUCTION) 98 | } 99 | 100 | func (d *Device) SetCursor(col, row uint8) { 101 | var row_offsets = []uint8{0x00, 0x40, 0x14, 0x54} 102 | 103 | if row > d.numlines { 104 | row = d.numlines - 1 // we count rows starting w/0 105 | } 106 | 107 | d.command(LCD_SETDDRAMADDR | (col + row_offsets[row])) 108 | } 109 | 110 | func (d *Device) Print(str string) { 111 | for _, s := range []byte(str) { 112 | d.write(uint8(s)) 113 | } 114 | //d.bus.WriteRegister(d.addr, 0x40, []byte(str)) 115 | //time.Sleep(26300 * 5 * time.Nanosecond) // >26.3us 116 | } 117 | 118 | func (d *Device) SetContrast(contrast uint8) { 119 | if contrast > 63 { 120 | contrast = 63 121 | } 122 | if contrast < 1 { 123 | contrast = 1 124 | } 125 | d.extendFunctionSet() 126 | d.command(LCD_EX_CONTRASTSETL | (contrast & 0x0F)) // Contrast set 127 | d.command(LCD_EX_POWICONCONTRASTH | LCD_ICON_ON | LCD_BOOST_ON | ((contrast >> 4) & 0x03)) // Power/ICON control/Contrast set 128 | d.normalFunctionSet() 129 | } 130 | 131 | /*********** mid level commands, for sending data/cmds */ 132 | func (d *Device) writeByte(reg uint8, data byte) { 133 | d.buf[0] = reg 134 | d.buf[1] = data 135 | err := d.bus.Tx(uint16(d.addr), d.buf, nil) 136 | if err != nil { 137 | fmt.Println(err.Error()) 138 | } 139 | } 140 | 141 | func (d *Device) command(value uint8) { 142 | //fmt.Printf("command: %08X\r\n", value) 143 | d.writeByte(0x00, value) 144 | //time.Sleep(26300 * time.Nanosecond) // >26.3us 145 | time.Sleep(26300 * 5 * time.Nanosecond) // >26.3us 146 | //time.Sleep(4 * time.Millisecond) // >26.3us 147 | } 148 | 149 | func (d *Device) write(value uint8) uint8 { 150 | d.writeByte(0x40, value) 151 | time.Sleep(26300 * time.Nanosecond) // >26.3us 152 | //time.Sleep(4 * time.Millisecond) // >26.3us 153 | 154 | return 1 155 | } 156 | 157 | //class ST7032 : public Print { 158 | //public: 159 | // ST7032(int i2c_addr = ST7032_I2C_DEFAULT_ADDR); 160 | // 161 | // void begin(uint8_t cols, uint8_t rows, uint8_t charsize = LCD_5x8DOTS); 162 | // 163 | // void setContrast(uint8_t cont); 164 | // void setIcon(uint8_t addr, uint8_t bit); 165 | // void clear(); 166 | // void home(); 167 | // 168 | // void noDisplay(); 169 | // void display(); 170 | // void noBlink(); 171 | // void blink(); 172 | // void noCursor(); 173 | // void cursor(); 174 | // void scrollDisplayLeft(); 175 | // void scrollDisplayRight(); 176 | // void leftToRight(); 177 | // void rightToLeft(); 178 | // void autoscroll(); 179 | // void noAutoscroll(); 180 | // 181 | // void createChar(uint8_t location, uint8_t charmap[]); 182 | // void setCursor(uint8_t col, uint8_t row); 183 | // virtual size_t write(uint8_t value); 184 | // void command(uint8_t value); 185 | // 186 | //private: 187 | // void setDisplayControl(uint8_t setBit); 188 | // void resetDisplayControl(uint8_t resetBit); 189 | // void setEntryMode(uint8_t setBit); 190 | // void resetEntryMode(uint8_t resetBit); 191 | // void normalFunctionSet(); 192 | // void extendFunctionSet(); 193 | // 194 | //// void send(uint8_t, uint8_t); 195 | ///* 196 | // uint8_t _rs_pin; // LOW: command. HIGH: character. 197 | // uint8_t _rw_pin; // LOW: write to LCD. HIGH: read from LCD. 198 | // uint8_t _enable_pin; // activated by a HIGH pulse. 199 | // uint8_t _data_pins[8]; 200 | //*/ 201 | // uint8_t _displayfunction; 202 | // uint8_t _displaycontrol; 203 | // uint8_t _displaymode; 204 | //// uint8_t _iconfunction; 205 | // 206 | // uint8_t _initialized; 207 | // 208 | // uint8_t _numlines; 209 | // uint8_t _currline; 210 | // 211 | // uint8_t _i2c_addr; 212 | //}; 213 | 214 | // https://github.com/FaBoPlatform/FaBoLCDmini-AQM0802A-Library/blob/master/src/FaBoLCDmini_AQM0802A.cpp 215 | -------------------------------------------------------------------------------- /wioterminal/buttons/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "machine" 6 | "time" 7 | ) 8 | 9 | const ( 10 | led = machine.LED 11 | ) 12 | 13 | func main() { 14 | led.Configure(machine.PinConfig{Mode: machine.PinOutput}) 15 | 16 | machine.WIO_KEY_A.Configure(machine.PinConfig{Mode: machine.PinInputPullup}) 17 | machine.WIO_KEY_B.Configure(machine.PinConfig{Mode: machine.PinInputPullup}) 18 | machine.WIO_KEY_C.Configure(machine.PinConfig{Mode: machine.PinInputPullup}) 19 | 20 | machine.WIO_5S_UP.Configure(machine.PinConfig{Mode: machine.PinInputPullup}) 21 | machine.WIO_5S_LEFT.Configure(machine.PinConfig{Mode: machine.PinInputPullup}) 22 | machine.WIO_5S_RIGHT.Configure(machine.PinConfig{Mode: machine.PinInputPullup}) 23 | machine.WIO_5S_DOWN.Configure(machine.PinConfig{Mode: machine.PinInputPullup}) 24 | machine.WIO_5S_PRESS.Configure(machine.PinConfig{Mode: machine.PinInputPullup}) 25 | 26 | for { 27 | if !machine.WIO_KEY_A.Get() { 28 | led.Low() 29 | fmt.Printf("machine.WIO_KEY_A pressed\r\n") 30 | } else if !machine.WIO_KEY_B.Get() { 31 | led.Low() 32 | fmt.Printf("machine.WIO_KEY_B pressed\r\n") 33 | } else if !machine.WIO_KEY_C.Get() { 34 | led.Low() 35 | fmt.Printf("machine.WIO_KEY_C pressed\r\n") 36 | } else if !machine.WIO_5S_UP.Get() { 37 | led.Low() 38 | fmt.Printf("machine.WIO_5S_UP pressed\r\n") 39 | } else if !machine.WIO_5S_LEFT.Get() { 40 | led.Low() 41 | fmt.Printf("machine.WIO_5S_LEFT pressed\r\n") 42 | } else if !machine.WIO_5S_RIGHT.Get() { 43 | led.Low() 44 | fmt.Printf("machine.WIO_5S_RIGHT pressed\r\n") 45 | } else if !machine.WIO_5S_DOWN.Get() { 46 | led.Low() 47 | fmt.Printf("machine.WIO_5S_DOWN pressed\r\n") 48 | } else if !machine.WIO_5S_PRESS.Get() { 49 | led.Low() 50 | fmt.Printf("machine.WIO_5S_PRESS pressed\r\n") 51 | } else { 52 | led.High() 53 | } 54 | 55 | time.Sleep(time.Millisecond * 10) 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /wioterminal/buzzer/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "machine" 5 | "time" 6 | 7 | "tinygo.org/x/drivers/tone" 8 | ) 9 | 10 | func main() { 11 | bzrPin := machine.WIO_BUZZER 12 | pwm := machine.TCC0 13 | speaker, err := tone.New(pwm, bzrPin) 14 | if err != nil { 15 | println("failed to configure PWM") 16 | return 17 | } 18 | 19 | song := []tone.Note{ 20 | tone.C5, 21 | tone.D5, 22 | tone.E5, 23 | tone.F5, 24 | tone.G5, 25 | tone.A5, 26 | tone.B5, 27 | tone.C6, 28 | tone.C6, 29 | tone.B5, 30 | tone.A5, 31 | tone.G5, 32 | tone.F5, 33 | tone.E5, 34 | tone.D5, 35 | tone.C5, 36 | } 37 | 38 | for { 39 | for _, val := range song { 40 | speaker.SetNote(val) 41 | time.Sleep(time.Second / 2) 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /wioterminal/goroutines/README.md: -------------------------------------------------------------------------------- 1 | # TinyGo examples 2 | 3 | ## wioterminal goroutines 4 | 5 | TinyGo example of wioterminal. 6 | https://github.com/sago35/tinygo-examples/tree/master/wioterminal/goroutines 7 | 8 | [![](https://img.youtube.com/vi/-dJ-o2cH_Fk/0.jpg)](https://www.youtube.com/watch?v=-dJ-o2cH_Fk) 9 | 10 | ## Summary 11 | 12 | This example shows the following, received via channel. 13 | TinyGo uses goroutine / channel to simplify the asynchronous process. 14 | 15 | - cnt1, which increments every 77ms 16 | - cnt2, which increments every 500ms 17 | 18 | ## Build 19 | 20 | ``` 21 | tinygo flash -target wioterminal -size short github.com/sago35/tinygo-examples/wioterminal/goroutines 22 | ``` 23 | 24 | or 25 | 26 | ``` 27 | tinygo build -o app.uf2 -target wioterminal -size short github.com/sago35/tinygo-examples/wioterminal/goroutines 28 | ``` 29 | 30 | or if you don't want to build it, use the uf2 file. 31 | 32 | * [wioterminal_goroutines.uf2](./wioterminal_goroutines.uf2) 33 | 34 | 35 | ## Environment 36 | 37 | ``` 38 | $ tinygo version 39 | tinygo version 0.15.0 linux/amd64 (using go version go0.1.0 and LLVM version 10.0.1) 40 | ``` 41 | 42 | ## Link 43 | 44 | * English 45 | * https://tinygo.org/microcontrollers/wioterminal/ 46 | * https://wiki.seeedstudio.com/Wio-Terminal-Getting-Started/ 47 | * Japanese 48 | * Wio Terminal で TinyGo プログラミングを始めよう 49 | * https://qiita.com/sago35/items/92b22e8cbbf99d0cd3ef 50 | 51 | 52 | -------------------------------------------------------------------------------- /wioterminal/goroutines/label.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "image/color" 5 | 6 | "tinygo.org/x/drivers/ili9341" 7 | "tinygo.org/x/tinyfont" 8 | "tinygo.org/x/tinyfont/freemono" 9 | ) 10 | 11 | type label struct { 12 | buf []uint16 13 | w int16 14 | h int16 15 | fontHeight int16 16 | } 17 | 18 | func NewLabel(w, h int) *label { 19 | return &label{ 20 | buf: make([]uint16, w*h), 21 | w: int16(w), 22 | h: int16(h), 23 | fontHeight: int16(tinyfont.GetGlyph(&freemono.Regular9pt7b, '0').Info().Height), 24 | } 25 | } 26 | 27 | func (l *label) Size() (int16, int16) { 28 | return l.w, l.h 29 | } 30 | 31 | func (l *label) SetPixel(x, y int16, c color.RGBA) { 32 | if x < 0 || y < 0 || l.w < x || l.h < y { 33 | return 34 | } 35 | l.buf[y*l.w+x] = ili9341.RGBATo565(c) 36 | } 37 | 38 | func (l *label) Display() error { 39 | return nil 40 | } 41 | 42 | func (l *label) SetText(str string, c color.RGBA) { 43 | for i := range l.buf { 44 | l.buf[i] = 0 45 | } 46 | 47 | tinyfont.WriteLine(l, &freemono.Regular9pt7b, 3, l.fontHeight, str, c) 48 | } 49 | -------------------------------------------------------------------------------- /wioterminal/goroutines/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "image/color" 6 | "machine" 7 | "time" 8 | 9 | "tinygo.org/x/drivers/ili9341" 10 | ) 11 | 12 | var ( 13 | led1 = machine.LED 14 | display *ili9341.Device 15 | ) 16 | 17 | var ( 18 | black = color.RGBA{0, 0, 0, 255} 19 | gray = color.RGBA{32, 32, 32, 255} 20 | white = color.RGBA{255, 255, 255, 255} 21 | red = color.RGBA{255, 0, 0, 255} 22 | blue = color.RGBA{0, 0, 255, 255} 23 | green = color.RGBA{0, 255, 0, 255} 24 | ) 25 | 26 | func initialize() error { 27 | machine.SPI3.Configure(machine.SPIConfig{ 28 | SCK: machine.LCD_SCK_PIN, 29 | SDO: machine.LCD_SDO_PIN, 30 | SDI: machine.LCD_SDI_PIN, 31 | Frequency: 48000000, 32 | }) 33 | 34 | backlight := machine.LCD_BACKLIGHT 35 | backlight.Configure(machine.PinConfig{Mode: machine.PinOutput}) 36 | 37 | display = ili9341.NewSPI( 38 | machine.SPI3, 39 | machine.LCD_DC, 40 | machine.LCD_SS_PIN, 41 | machine.LCD_RESET, 42 | ) 43 | display.Configure(ili9341.Config{ 44 | Rotation: ili9341.Rotation270, 45 | }) 46 | display.FillScreen(black) 47 | backlight.High() 48 | 49 | return nil 50 | } 51 | 52 | func errDisp(err error) { 53 | for { 54 | fmt.Printf("%s\r\n", err.Error()) 55 | time.Sleep(10 * time.Second) 56 | } 57 | } 58 | 59 | func main() { 60 | err := initialize() 61 | if err != nil { 62 | errDisp(err) 63 | } 64 | 65 | label1 := NewLabel(240, 0x12) 66 | label2 := NewLabel(240, 0x12) 67 | 68 | chCnt1 := make(chan uint32, 1) 69 | chCnt2 := make(chan uint32, 1) 70 | 71 | go timer77ms(chCnt1) 72 | go timer500ms(chCnt2) 73 | 74 | for { 75 | select { 76 | case cnt := <-chCnt1: 77 | label1.SetText(fmt.Sprintf("timer77ms : %04X", cnt), white) 78 | display.DrawRGBBitmap(0, 30, label1.buf, label1.w, label1.h) 79 | case cnt := <-chCnt2: 80 | label2.SetText(fmt.Sprintf("timer500ms: %04X", cnt), white) 81 | display.DrawRGBBitmap(0, 50, label2.buf, label2.w, label2.h) 82 | } 83 | time.Sleep(1 * time.Millisecond) 84 | } 85 | } 86 | 87 | func timer77ms(ch chan<- uint32) { 88 | cnt := uint32(0) 89 | for { 90 | ch <- cnt 91 | cnt++ 92 | time.Sleep(77 * time.Millisecond) 93 | } 94 | } 95 | 96 | func timer500ms(ch chan<- uint32) { 97 | cnt := uint32(0) 98 | for { 99 | ch <- cnt 100 | cnt++ 101 | time.Sleep(500 * time.Millisecond) 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /wioterminal/goroutines/wioterminal_goroutines.uf2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sago35/tinygo-examples/347475af179e3759ae2f1b509df4d36593da15ab/wioterminal/goroutines/wioterminal_goroutines.uf2 -------------------------------------------------------------------------------- /wioterminal/initialize/ili9341.go: -------------------------------------------------------------------------------- 1 | //go:build wioterminal 2 | // +build wioterminal 3 | 4 | package initialize 5 | 6 | import ( 7 | "tinygo.org/x/drivers/examples/ili9341/initdisplay" 8 | "tinygo.org/x/drivers/ili9341" 9 | ) 10 | 11 | func Display() *ili9341.Device { 12 | display := initdisplay.InitDisplay() 13 | return display 14 | } 15 | -------------------------------------------------------------------------------- /wioterminal/initialize/ntp.go: -------------------------------------------------------------------------------- 1 | package initialize 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "time" 7 | 8 | "tinygo.org/x/drivers/net" 9 | ) 10 | 11 | const ( 12 | ntpPacketSize = 48 13 | ntpHost = "129.6.15.29" 14 | ) 15 | 16 | var ( 17 | b = make([]byte, ntpPacketSize) 18 | ) 19 | 20 | func GetCurrentTime() (time.Time, error) { 21 | hip := net.ParseIP(ntpHost) 22 | raddr := &net.UDPAddr{IP: hip, Port: 123} 23 | laddr := &net.UDPAddr{Port: 2390} 24 | conn, err := net.DialUDP("udp", laddr, raddr) 25 | if err != nil { 26 | return time.Time{}, err 27 | } 28 | defer conn.Close() 29 | 30 | return getCurrentTime(conn) 31 | } 32 | 33 | func getCurrentTime(conn *net.UDPSerialConn) (time.Time, error) { 34 | if err := sendNTPpacket(conn); err != nil { 35 | return time.Time{}, err 36 | } 37 | clearBuffer() 38 | for now := time.Now(); time.Since(now) < time.Second; { 39 | time.Sleep(5 * time.Millisecond) 40 | if n, err := conn.Read(b); err != nil { 41 | return time.Time{}, fmt.Errorf("error reading UDP packet: %w", err) 42 | } else if n == 0 { 43 | continue // no packet received yet 44 | } else if n != ntpPacketSize { 45 | if n != ntpPacketSize { 46 | return time.Time{}, fmt.Errorf("expected NTP packet size of %d: %d", ntpPacketSize, n) 47 | } 48 | } 49 | return parseNTPpacket(), nil 50 | } 51 | return time.Time{}, errors.New("no packet received after 1 second") 52 | } 53 | 54 | func sendNTPpacket(conn *net.UDPSerialConn) error { 55 | clearBuffer() 56 | b[0] = 0b11100011 // LI, Version, Mode 57 | b[1] = 0 // Stratum, or type of clock 58 | b[2] = 6 // Polling Interval 59 | b[3] = 0xEC // Peer Clock Precision 60 | // 8 bytes of zero for Root Delay & Root Dispersion 61 | b[12] = 49 62 | b[13] = 0x4E 63 | b[14] = 49 64 | b[15] = 52 65 | if _, err := conn.Write(b); err != nil { 66 | return err 67 | } 68 | return nil 69 | } 70 | 71 | func parseNTPpacket() time.Time { 72 | // the timestamp starts at byte 40 of the received packet and is four bytes, 73 | // this is NTP time (seconds since Jan 1 1900): 74 | t := uint32(b[40])<<24 | uint32(b[41])<<16 | uint32(b[42])<<8 | uint32(b[43]) 75 | const seventyYears = 2208988800 76 | return time.Unix(int64(t-seventyYears), 0) 77 | } 78 | 79 | func clearBuffer() { 80 | for i := range b { 81 | b[i] = 0 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /wioterminal/initialize/rtl8720dn.go: -------------------------------------------------------------------------------- 1 | //go:build wioterminal 2 | // +build wioterminal 3 | 4 | package initialize 5 | 6 | import ( 7 | "device/sam" 8 | "machine" 9 | "runtime" 10 | "runtime/interrupt" 11 | "time" 12 | 13 | "tinygo.org/x/drivers/net/http" 14 | "tinygo.org/x/drivers/rtl8720dn" 15 | ) 16 | 17 | var ( 18 | rtl *rtl8720dn.Driver 19 | connected bool 20 | uart UARTx 21 | debug bool 22 | buf [0x1000]byte 23 | ) 24 | 25 | func handleInterrupt(interrupt.Interrupt) { 26 | // should reset IRQ 27 | uart.Receive(byte((uart.Bus.DATA.Get() & 0xFF))) 28 | uart.Bus.INTFLAG.SetBits(sam.SERCOM_USART_INT_INTFLAG_RXC) 29 | } 30 | 31 | // SetupRTL8720DN sets up the RTL8270DN for use. 32 | func SetupRTL8720DN() (*rtl8720dn.Driver, error) { 33 | rtl = rtl8720dn.New(machine.UART3, machine.PB24, machine.PC24, machine.RTL8720D_CHIP_PU) 34 | 35 | if debug { 36 | waitSerial() 37 | } 38 | 39 | rtl.Debug(debug) 40 | rtl.Configure() 41 | 42 | connected = true 43 | return rtl, nil 44 | } 45 | 46 | // Wifi sets up the RTL8720DN and connects it to Wi-Fi. 47 | func Wifi(ssid, pass string, timeout time.Duration) (*rtl8720dn.Driver, error) { 48 | _, err := SetupRTL8720DN() 49 | if err != nil { 50 | return nil, err 51 | } 52 | 53 | err = rtl.ConnectToAccessPoint(ssid, pass, 10*time.Second) 54 | if err != nil { 55 | return rtl, err 56 | } 57 | 58 | http.UseDriver(rtl) 59 | http.SetBuf(buf[:]) 60 | 61 | // NTP 62 | t, err := GetCurrentTime() 63 | if err != nil { 64 | return nil, err 65 | } 66 | runtime.AdjustTimeOffset(-1 * int64(time.Since(t))) 67 | 68 | return rtl, nil 69 | } 70 | 71 | func Device() *rtl8720dn.Driver { 72 | return rtl 73 | } 74 | 75 | func Connected() bool { 76 | return connected 77 | } 78 | 79 | func IP() rtl8720dn.IPAddress { 80 | ip, _, _, _ := rtl.GetIP() 81 | return ip 82 | } 83 | 84 | func Subnet() rtl8720dn.IPAddress { 85 | _, subnet, _, _ := rtl.GetIP() 86 | return subnet 87 | } 88 | 89 | func Gateway() rtl8720dn.IPAddress { 90 | _, _, gateway, _ := rtl.GetIP() 91 | return gateway 92 | } 93 | 94 | func SetRootCA(s *string) { 95 | rtl.SetRootCA(s) 96 | } 97 | 98 | // Wait for user to open serial console 99 | func waitSerial() { 100 | for !machine.Serial.DTR() { 101 | time.Sleep(100 * time.Millisecond) 102 | } 103 | } 104 | 105 | type UARTx struct { 106 | *machine.UART 107 | } 108 | 109 | func (u UARTx) Read(p []byte) (n int, err error) { 110 | if u.Buffered() == 0 { 111 | time.Sleep(1 * time.Millisecond) 112 | return 0, nil 113 | } 114 | return u.UART.Read(p) 115 | } 116 | 117 | // Debug sets the debug mode. 118 | func Debug(b bool) { 119 | debug = b 120 | } 121 | -------------------------------------------------------------------------------- /wioterminal/ir/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // This is the most minimal blinky example and should run almost everywhere. 4 | 5 | import ( 6 | "machine" 7 | "time" 8 | ) 9 | 10 | func main() { 11 | led := machine.WIO_IR 12 | led.Configure(machine.PinConfig{Mode: machine.PinOutput}) 13 | for { 14 | led.Low() 15 | time.Sleep(time.Millisecond * 27) 16 | 17 | led.High() 18 | time.Sleep(time.Millisecond * 27) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /wioterminal/light_sensor/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "machine" 5 | "time" 6 | ) 7 | 8 | // This example assumes that an analog sensor such as a rotary dial is connected to pin ADC0. 9 | // When the dial is turned past the midway point, the built-in LED will light up. 10 | 11 | func main() { 12 | machine.InitADC() 13 | 14 | led := machine.LED 15 | led.Configure(machine.PinConfig{Mode: machine.PinOutput}) 16 | 17 | sensor := machine.ADC{Pin: machine.WIO_LIGHT} 18 | sensor.Configure(machine.ADCConfig{}) 19 | 20 | for { 21 | val := sensor.Get() 22 | if val < 0x8000 { 23 | led.Low() 24 | } else { 25 | led.High() 26 | } 27 | time.Sleep(time.Millisecond * 100) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /wioterminal/lis3dh/main.go: -------------------------------------------------------------------------------- 1 | // Connects to a LIS3DH I2C accelerometer on the Adafruit Circuit Playground Express. 2 | package main 3 | 4 | import ( 5 | "fmt" 6 | "machine" 7 | "time" 8 | 9 | "tinygo.org/x/drivers/lis3dh" 10 | ) 11 | 12 | var i2c = machine.I2C0 13 | 14 | func main() { 15 | i2c.Configure(machine.I2CConfig{SCL: machine.SCL0_PIN, SDA: machine.SDA0_PIN}) 16 | 17 | accel := lis3dh.New(i2c) 18 | accel.Address = lis3dh.Address0 // address on the Wio Terminal 19 | accel.Configure() 20 | accel.SetRange(lis3dh.RANGE_2_G) 21 | 22 | println(accel.Connected()) 23 | 24 | for { 25 | x, y, z, _ := accel.ReadAcceleration() 26 | fmt.Printf("X: %-10d Y: %-10d Z: %-10d\r\n", x, y, z) 27 | 28 | //rx, ry, rz := accel.ReadRawAcceleration() 29 | //println("X (raw):", rx, "Y (raw):", ry, "Z (raw):", rz) 30 | 31 | time.Sleep(time.Millisecond * 100) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /wioterminal/microphone/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "machine" 5 | "time" 6 | ) 7 | 8 | // This example assumes that an analog sensor such as a rotary dial is connected to pin ADC0. 9 | // When the dial is turned past the midway point, the built-in LED will light up. 10 | 11 | func main() { 12 | machine.InitADC() 13 | 14 | led := machine.LED 15 | led.Configure(machine.PinConfig{Mode: machine.PinOutput}) 16 | 17 | sensor := machine.ADC{Pin: machine.WIO_MIC} 18 | sensor.Configure(machine.ADCConfig{}) 19 | 20 | for { 21 | val := sensor.Get() 22 | if val < 0x8000 { 23 | led.Low() 24 | } else { 25 | led.High() 26 | } 27 | time.Sleep(time.Millisecond * 100) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /wioterminal/mqtt/main.go: -------------------------------------------------------------------------------- 1 | // This is a sensor station that uses a RTL8720DN running on the device UART2. 2 | // It creates an MQTT connection that publishes a message every second 3 | // to an MQTT broker. 4 | // 5 | // In other words: 6 | // Your computer <--> USB-CDC <--> MCU <--> UART2 <--> RTL8720DN <--> Internet <--> MQTT broker. 7 | // 8 | // You must also install the Paho MQTT package to build this program: 9 | // 10 | // go get -u github.com/eclipse/paho.mqtt.golang 11 | // 12 | // You can check that mqttpub/mqttsub is running successfully with the following command. 13 | // 14 | // mosquitto_sub -h test.mosquitto.org -t sago35/tinygo/tx 15 | // mosquitto_pub -h test.mosquitto.org -t sago35/tinygo/rx -m "{"Temperature": 9.87, "Humidity": 54.32}" 16 | package main 17 | 18 | import ( 19 | "device/sam" 20 | "fmt" 21 | "machine" 22 | "math/rand" 23 | "time" 24 | 25 | "github.com/sago35/tinygo-examples/wioterminal/initialize" 26 | "tinygo.org/x/drivers/bme280" 27 | "tinygo.org/x/drivers/net" 28 | "tinygo.org/x/drivers/net/mqtt" 29 | "tinygo.org/x/drivers/rtl8720dn" 30 | ) 31 | 32 | // You can override the setting with the init() in another source code. 33 | // func init() { 34 | // ssid = "your-ssid" 35 | // password = "your-password" 36 | // debug = true 37 | // server = "tinygo.org" 38 | // } 39 | 40 | var ( 41 | ssid string 42 | password string 43 | server string = "tcp://test.mosquitto.org:1883" 44 | debug = false 45 | ) 46 | 47 | var lastRequestTime time.Time 48 | var conn net.Conn 49 | var adaptor *rtl8720dn.RTL8720DN 50 | 51 | func main() { 52 | err := run() 53 | for err != nil { 54 | fmt.Printf("error: %s\r\n", err.Error()) 55 | time.Sleep(5 * time.Second) 56 | } 57 | } 58 | 59 | // change these to connect to a different UART or pins for the ESP8266/ESP32 60 | var ( 61 | cl mqtt.Client 62 | topicTx = "sago35/tinygo/tx" 63 | topicRx = "sago35/tinygo/rx" 64 | ) 65 | 66 | func subHandler(client mqtt.Client, msg mqtt.Message) { 67 | fmt.Printf("[%s] ", msg.Topic()) 68 | fmt.Printf("%s\r\n", msg.Payload()) 69 | } 70 | 71 | func readTemperatureAndHumidity(sensor bme280.Device) (float64, float64) { 72 | temp, _ := sensor.ReadTemperature() 73 | hum, _ := sensor.ReadHumidity() 74 | return float64(temp) / 1000, float64(hum) / 100 75 | } 76 | 77 | func run() error { 78 | _, err := initialize.Wifi(ssid, password, 10*time.Second) 79 | if err != nil { 80 | return err 81 | } 82 | 83 | // Enable 3V3 output 84 | machine.OUTPUT_CTR_3V3.Configure(machine.PinConfig{Mode: machine.PinOutput}) 85 | machine.OUTPUT_CTR_3V3.Low() 86 | 87 | // Initialize I2C0 using BCM0 (SDA) and BCM1 (SCL) 88 | // I2C SERCOM4 : SCL0_PIN (PA12) + SDA0_PIN (PA13) 89 | i2c := &machine.I2C{Bus: sam.SERCOM4_I2CM, SERCOM: 4} 90 | i2c.Configure(machine.I2CConfig{SCL: machine.SCL0_PIN, SDA: machine.SDA0_PIN}) 91 | 92 | // Initialize BME280 93 | sensor := bme280.New(i2c) 94 | sensor.Configure() 95 | 96 | rand.Seed(time.Now().UnixNano()) 97 | 98 | opts := mqtt.NewClientOptions() 99 | opts.AddBroker(server).SetClientID("tinygo-client-" + randomString(10)) 100 | 101 | println("Connecting to MQTT broker at", server) 102 | cl = mqtt.NewClient(opts) 103 | if token := cl.Connect(); token.Wait() && token.Error() != nil { 104 | failMessage(token.Error().Error()) 105 | } 106 | 107 | // subscribe 108 | token := cl.Subscribe(topicRx, 0, subHandler) 109 | token.Wait() 110 | if token.Error() != nil { 111 | failMessage(token.Error().Error()) 112 | } 113 | 114 | go publishing(sensor) 115 | 116 | select {} 117 | 118 | // Right now this code is never reached. Need a way to trigger it... 119 | println("Disconnecting MQTT...") 120 | cl.Disconnect(100) 121 | 122 | println("Done.") 123 | 124 | return nil 125 | } 126 | 127 | func publishing(sensor bme280.Device) { 128 | for { 129 | temp, hum := readTemperatureAndHumidity(sensor) 130 | fmt.Printf("%.2f °C %.2f %%\r\n", temp, hum) 131 | body := fmt.Sprintf(`{"Temperature": %.2f, "Humidity": %.2f}`, temp, hum) 132 | data := []byte(body) 133 | token := cl.Publish(topicTx, 0, false, data) 134 | token.Wait() 135 | if token.Error() != nil { 136 | println(token.Error().Error()) 137 | } 138 | 139 | time.Sleep(2000 * time.Millisecond) 140 | } 141 | } 142 | 143 | // Returns an int >= min, < max 144 | func randomInt(min, max int) int { 145 | return min + rand.Intn(max-min) 146 | } 147 | 148 | // Generate a random string of A-Z chars with len = l 149 | func randomString(len int) string { 150 | bytes := make([]byte, len) 151 | for i := 0; i < len; i++ { 152 | bytes[i] = byte(randomInt(65, 90)) 153 | } 154 | return string(bytes) 155 | } 156 | 157 | func failMessage(msg string) { 158 | for { 159 | println(msg) 160 | time.Sleep(1 * time.Second) 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /wioterminal/qspi_flash/console.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "machine" 7 | "os" 8 | "strconv" 9 | "strings" 10 | 11 | "tinygo.org/x/drivers/flash" 12 | ) 13 | 14 | const consoleBufLen = 64 15 | const storageBufLen = 512 16 | 17 | var ( 18 | debug = false 19 | 20 | input [consoleBufLen]byte 21 | store [storageBufLen]byte 22 | 23 | console = machine.Serial 24 | 25 | dev *flash.Device 26 | 27 | commands map[string]cmdfunc = map[string]cmdfunc{ 28 | "": cmdfunc(noop), 29 | "help": cmdfunc(help), 30 | "erase": cmdfunc(erase), 31 | "lsblk": cmdfunc(lsblk), 32 | "write": cmdfunc(write), 33 | "xxd": cmdfunc(xxd), 34 | } 35 | ) 36 | 37 | type cmdfunc func(argv []string) 38 | 39 | const ( 40 | StateInput = iota 41 | StateEscape 42 | StateEscBrc 43 | StateCSI 44 | ) 45 | 46 | func RunFor(device *flash.Device) { 47 | 48 | dev = device 49 | dev.Configure(&flash.DeviceConfig{ 50 | Identifier: flash.DefaultDeviceIdentifier, 51 | }) 52 | 53 | prompt() 54 | 55 | var state = StateInput 56 | 57 | for i := 0; ; { 58 | if console.Buffered() > 0 { 59 | data, _ := console.ReadByte() 60 | if debug { 61 | fmt.Printf("\rdata: %x\r\n\r", data) 62 | prompt() 63 | console.Write(input[:i]) 64 | } 65 | switch state { 66 | case StateInput: 67 | switch data { 68 | case 0x8: 69 | fallthrough 70 | case 0x7f: // this is probably wrong... works on my machine tho :) 71 | // backspace 72 | if i > 0 { 73 | i -= 1 74 | console.Write([]byte{0x8, 0x20, 0x8}) 75 | } 76 | case 13: 77 | // return key 78 | console.Write([]byte("\r\n")) 79 | runCommand(string(input[:i])) 80 | prompt() 81 | 82 | i = 0 83 | continue 84 | case 27: 85 | // escape 86 | state = StateEscape 87 | default: 88 | // anything else, just echo the character if it is printable 89 | if strconv.IsPrint(rune(data)) { 90 | if i < (consoleBufLen - 1) { 91 | console.WriteByte(data) 92 | input[i] = data 93 | i++ 94 | } 95 | } 96 | } 97 | case StateEscape: 98 | switch data { 99 | case 0x5b: 100 | state = StateEscBrc 101 | default: 102 | state = StateInput 103 | } 104 | default: 105 | // TODO: handle escape sequences 106 | state = StateInput 107 | } 108 | } 109 | } 110 | } 111 | 112 | func runCommand(line string) { 113 | argv := strings.SplitN(strings.TrimSpace(line), " ", -1) 114 | cmd := argv[0] 115 | cmdfn, ok := commands[cmd] 116 | if !ok { 117 | println("unknown command: " + line) 118 | return 119 | } 120 | cmdfn(argv) 121 | } 122 | 123 | func noop(argv []string) {} 124 | 125 | func help(argv []string) { 126 | fmt.Printf("help\r\n") 127 | fmt.Printf("erase \r\n") 128 | fmt.Printf("lsblk\r\n") 129 | fmt.Printf("write \r\n") 130 | fmt.Printf("xxd \r\n") 131 | } 132 | 133 | func lsblk(argv []string) { 134 | attrs := dev.Attrs() 135 | status1, _ := dev.ReadStatus() 136 | status2, _ := dev.ReadStatus2() 137 | serialNumber1, _ := dev.ReadSerialNumber() 138 | fmt.Printf( 139 | "\n-------------------------------------\r\n"+ 140 | " Device Information: \r\n"+ 141 | "-------------------------------------\r\n"+ 142 | " JEDEC ID: %v\r\n"+ 143 | " Serial: %v\r\n"+ 144 | " Status 1: %02x\r\n"+ 145 | " Status 2: %02x\r\n"+ 146 | " \r\n"+ 147 | " Max clock speed (MHz): %d\r\n"+ 148 | " Has Sector Protection: %t\r\n"+ 149 | " Supports Fast Reads: %t\r\n"+ 150 | " Supports QSPI Reads: %t\r\n"+ 151 | " Supports QSPI Write: %t\r\n"+ 152 | " Write Status Split: %t\r\n"+ 153 | " Single Status Byte: %t\r\n"+ 154 | "-------------------------------------\r\n\r\n", 155 | attrs.JedecID, 156 | serialNumber1, 157 | status1, 158 | status2, 159 | attrs.MaxClockSpeedMHz, 160 | attrs.HasSectorProtection, 161 | attrs.SupportsFastRead, 162 | attrs.SupportsQSPI, 163 | attrs.SupportsQSPIWrites, 164 | attrs.WriteStatusSplit, 165 | attrs.SingleStatusByte, 166 | ) 167 | } 168 | 169 | func erase(argv []string) { 170 | if len(argv) < 3 { 171 | println("usage: erase ") 172 | return 173 | } 174 | var err error 175 | var addr uint64 = 0x0 176 | if addr, err = strconv.ParseUint(argv[2], 16, 32); err != nil { 177 | println("Invalid address: " + err.Error() + "\r\n") 178 | return 179 | } 180 | if argv[1] == "block" { 181 | if err = dev.EraseBlock(uint32(addr)); err != nil { 182 | println("Block erase error: " + err.Error() + "\r\n") 183 | } 184 | } else if argv[1] == "sector" { 185 | if err = dev.EraseSector(uint32(addr)); err != nil { 186 | println("Sector erase error: " + err.Error() + "\r\n") 187 | } 188 | } else if argv[1] == "chip" { 189 | if err = dev.EraseAll(); err != nil { 190 | println("Chip erase error: " + err.Error() + "\r\n") 191 | } 192 | } else { 193 | println("usage: erase ") 194 | } 195 | } 196 | 197 | func write(argv []string) { 198 | if len(argv) < 3 { 199 | println("usage: write ") 200 | } 201 | var err error 202 | var addr uint64 = 0x0 203 | if addr, err = strconv.ParseUint(argv[1], 16, 32); err != nil { 204 | println("Invalid address: " + err.Error() + "\r\n") 205 | return 206 | } 207 | buf := []byte(argv[2]) 208 | if _, err = dev.WriteAt(buf, int64(addr)); err != nil { 209 | println("Write error: " + err.Error() + "\r\n") 210 | } 211 | } 212 | 213 | func xxd(argv []string) { 214 | var err error 215 | var addr uint64 = 0x0 216 | var size int = 64 217 | switch len(argv) { 218 | case 3: 219 | if size, err = strconv.Atoi(argv[2]); err != nil { 220 | println("Invalid size argument: " + err.Error() + "\r\n") 221 | return 222 | } 223 | if size > storageBufLen || size < 1 { 224 | fmt.Printf("Size of hexdump must be greater than 0 and less than %d\r\n", storageBufLen) 225 | return 226 | } 227 | fallthrough 228 | case 2: 229 | if addr, err = strconv.ParseUint(argv[1], 16, 32); err != nil { 230 | println("Invalid address: " + err.Error() + "\r\n") 231 | return 232 | } 233 | fallthrough 234 | case 1: 235 | // no args supplied, so nothing to do here, just use the defaults 236 | default: 237 | println("usage: xxd \r\n") 238 | return 239 | } 240 | buf := store[0:size] 241 | dev.ReadAt(buf, int64(addr)) 242 | xxdfprint(os.Stdout, uint32(addr), buf) 243 | } 244 | 245 | func xxdfprint(w io.Writer, offset uint32, b []byte) { 246 | var l int 247 | var buf16 = make([]byte, 16) 248 | for i, c := 0, len(b); i < c; i += 16 { 249 | l = i + 16 250 | if l >= c { 251 | l = c 252 | } 253 | fmt.Fprintf(w, "%08x: % x ", offset+uint32(i), b[i:l]) 254 | for j, n := 0, l-i; j < 16; j++ { 255 | if j >= n || !strconv.IsPrint(rune(b[i+j])) { 256 | buf16[j] = '.' 257 | } else { 258 | buf16[j] = b[i+j] 259 | } 260 | } 261 | console.Write(buf16) 262 | println() 263 | } 264 | } 265 | 266 | func prompt() { 267 | print("==> ") 268 | } 269 | -------------------------------------------------------------------------------- /wioterminal/qspi_flash/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "machine" 5 | 6 | "tinygo.org/x/drivers/flash" 7 | ) 8 | 9 | func main() { 10 | RunFor( 11 | flash.NewQSPI( 12 | machine.QSPI_CS, 13 | machine.QSPI_SCK, 14 | machine.QSPI_DATA0, 15 | machine.QSPI_DATA1, 16 | machine.QSPI_DATA2, 17 | machine.QSPI_DATA3, 18 | ), 19 | ) 20 | } 21 | -------------------------------------------------------------------------------- /wioterminal/sample/README.md: -------------------------------------------------------------------------------- 1 | # TinyGo examples 2 | 3 | ## wioterminal sample 4 | 5 | TinyGo example of wioterminal. 6 | https://github.com/sago35/tinygo-examples/tree/master/wioterminal/sample 7 | 8 | [![](https://img.youtube.com/vi/9IpI9rUMXOs/0.jpg)](https://www.youtube.com/watch?v=9IpI9rUMXOs) 9 | 10 | ## Build 11 | 12 | ``` 13 | tinygo flash -target wioterminal -size short github.com/sago35/tinygo-examples/wioterminal/sample 14 | ``` 15 | 16 | or 17 | 18 | ``` 19 | tinygo build -o app.uf2 -target wioterminal -size short github.com/sago35/tinygo-examples/wioterminal/sample 20 | ``` 21 | 22 | or if you don't want to build it, use the uf2 file. 23 | 24 | * [wioterminal_tinygo_sample.uf2](./wioterminal_tinygo_sample.uf2) 25 | 26 | 27 | ## Environment 28 | 29 | ``` 30 | $ tinygo version 31 | tinygo version 0.14.0-dev linux/amd64 (using go version go1.14.4 and LLVM version 10.0.1) 32 | ``` 33 | 34 | In this example, I used the following. 35 | 36 | * Wio Terminal 37 | * LCD Screen : ILI9341 320 x 240 38 | * Accelerometer : LIS3DHTR 39 | * LED 40 | * machine.LED 41 | * Infrared Emitter 42 | * machine.WIO\_IR 43 | * Operation interface 44 | * machine.WIO\_KEY\_A 45 | * machine.WIO\_KEY\_B 46 | * machine.WIO\_KEY\_C 47 | * machine.WIO\_5S\_UP 48 | * machine.WIO\_5S\_LEFT 49 | * machine.WIO\_5S\_RIGHT 50 | * machine.WIO\_5S\_DOWN 51 | * machine.WIO\_5S\_PRESS 52 | * Light Sensor 53 | * machine.WIO\_LIGHT 54 | * Speaker 55 | * machine.WIO\_BUZZER 56 | * Microphone 57 | * machine.WIO\_MIC 58 | 59 | ## Link 60 | 61 | * English 62 | * https://tinygo.org/microcontrollers/wioterminal/ 63 | * https://wiki.seeedstudio.com/Wio-Terminal-Getting-Started/ 64 | * Japanese 65 | * Wio Terminal で TinyGo プログラミングを始めよう 66 | * https://qiita.com/sago35/items/92b22e8cbbf99d0cd3ef 67 | 68 | -------------------------------------------------------------------------------- /wioterminal/sample/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "image/color" 6 | "machine" 7 | "time" 8 | 9 | "tinygo.org/x/drivers/buzzer" 10 | "tinygo.org/x/drivers/ili9341" 11 | "tinygo.org/x/drivers/lis3dh" 12 | "tinygo.org/x/tinyfont" 13 | "tinygo.org/x/tinyfont/freemono" 14 | ) 15 | 16 | var ( 17 | black = color.RGBA{0, 0, 0, 255} 18 | gray = color.RGBA{32, 32, 32, 255} 19 | white = color.RGBA{255, 255, 255, 255} 20 | red = color.RGBA{255, 0, 0, 255} 21 | blue = color.RGBA{0, 0, 255, 255} 22 | green = color.RGBA{0, 255, 0, 255} 23 | ) 24 | 25 | var () 26 | 27 | func init() { 28 | } 29 | 30 | func main() { 31 | machine.SPI3.Configure(machine.SPIConfig{ 32 | SCK: machine.LCD_SCK_PIN, 33 | SDO: machine.LCD_SDO_PIN, 34 | SDI: machine.LCD_SDI_PIN, 35 | Frequency: 48000000, 36 | }) 37 | 38 | machine.InitADC() 39 | 40 | backlight := machine.LCD_BACKLIGHT 41 | backlight.Configure(machine.PinConfig{Mode: machine.PinOutput}) 42 | 43 | display := ili9341.NewSPI( 44 | machine.SPI3, 45 | machine.LCD_DC, 46 | machine.LCD_SS_PIN, 47 | machine.LCD_RESET, 48 | ) 49 | display.Configure(ili9341.Config{ 50 | Rotation: ili9341.Rotation270, 51 | }) 52 | display.FillScreen(black) 53 | backlight.High() 54 | 55 | led := machine.LED 56 | led.Configure(machine.PinConfig{Mode: machine.PinOutput}) 57 | 58 | ir := machine.WIO_IR 59 | ir.Configure(machine.PinConfig{Mode: machine.PinOutput}) 60 | 61 | btnA := machine.WIO_KEY_A 62 | btnA.Configure(machine.PinConfig{Mode: machine.PinInput}) 63 | 64 | btnB := machine.WIO_KEY_B 65 | btnB.Configure(machine.PinConfig{Mode: machine.PinInput}) 66 | 67 | btnC := machine.WIO_KEY_C 68 | btnC.Configure(machine.PinConfig{Mode: machine.PinInput}) 69 | 70 | btnU := machine.WIO_5S_UP 71 | btnU.Configure(machine.PinConfig{Mode: machine.PinInput}) 72 | 73 | btnL := machine.WIO_5S_LEFT 74 | btnL.Configure(machine.PinConfig{Mode: machine.PinInput}) 75 | 76 | btnR := machine.WIO_5S_RIGHT 77 | btnR.Configure(machine.PinConfig{Mode: machine.PinInput}) 78 | 79 | btnD := machine.WIO_5S_DOWN 80 | btnD.Configure(machine.PinConfig{Mode: machine.PinInput}) 81 | 82 | btnP := machine.WIO_5S_PRESS 83 | btnP.Configure(machine.PinConfig{Mode: machine.PinInput}) 84 | 85 | lightSensor := machine.ADC{Pin: machine.WIO_LIGHT} 86 | lightSensor.Configure(machine.ADCConfig{}) 87 | 88 | mic := machine.ADC{Pin: machine.WIO_MIC} 89 | mic.Configure(machine.ADCConfig{}) 90 | 91 | label1 := NewLabel(240, 0x12) 92 | label2 := NewLabel(240, 0x12) 93 | 94 | machine.I2C0.Configure(machine.I2CConfig{SCL: machine.SCL0_PIN, SDA: machine.SDA0_PIN}) 95 | accel := lis3dh.New(machine.I2C0) 96 | accel.Address = lis3dh.Address0 97 | accel.Configure() 98 | accel.SetRange(lis3dh.RANGE_2_G) 99 | 100 | label3 := NewLabel(320, 0x12) 101 | label4 := NewLabel(160, 0x12) 102 | label5 := NewLabel(160, 0x12) 103 | 104 | bzrPin := machine.WIO_BUZZER 105 | bzrPin.Configure(machine.PinConfig{Mode: machine.PinOutput}) 106 | bzr := buzzer.New(bzrPin) 107 | 108 | cnt := 0 109 | lastRequestTime := time.Now() 110 | rawXY := [16][2]int16{} 111 | rawXYi := 0 112 | 113 | for { 114 | if btnC.Get() { 115 | display.FillRectangle(10, 10, 10, 10, gray) 116 | } else { 117 | display.FillRectangle(10, 10, 10, 10, red) 118 | } 119 | 120 | if btnB.Get() { 121 | display.FillRectangle(30, 10, 10, 10, gray) 122 | } else { 123 | display.FillRectangle(30, 10, 10, 10, red) 124 | } 125 | 126 | if btnA.Get() { 127 | display.FillRectangle(50, 10, 10, 10, gray) 128 | } else { 129 | display.FillRectangle(50, 10, 10, 10, red) 130 | } 131 | 132 | if btnU.Get() { 133 | display.FillRectangle(280, 180, 10, 10, gray) 134 | } else { 135 | display.FillRectangle(280, 180, 10, 10, red) 136 | } 137 | 138 | if btnP.Get() { 139 | display.FillRectangle(280, 200, 10, 10, gray) 140 | } else { 141 | display.FillRectangle(280, 200, 10, 10, red) 142 | bzr.Tone(buzzer.G7, 0.02) 143 | } 144 | 145 | if btnD.Get() { 146 | display.FillRectangle(280, 220, 10, 10, gray) 147 | } else { 148 | display.FillRectangle(280, 220, 10, 10, red) 149 | } 150 | 151 | if btnL.Get() { 152 | display.FillRectangle(260, 200, 10, 10, gray) 153 | } else { 154 | display.FillRectangle(260, 200, 10, 10, red) 155 | } 156 | 157 | if btnR.Get() { 158 | display.FillRectangle(300, 200, 10, 10, gray) 159 | } else { 160 | display.FillRectangle(300, 200, 10, 10, red) 161 | } 162 | 163 | label1.SetText(fmt.Sprintf("WIO_LIGHT : %04X", lightSensor.Get()), white) 164 | display.DrawRGBBitmap(0, 30, label1.buf, label1.w, label1.h) 165 | 166 | label2.SetText(fmt.Sprintf("WIO_MIC : %04X", mic.Get()), white) 167 | display.DrawRGBBitmap(0, 50, label2.buf, label2.w, label2.h) 168 | 169 | x, y, z, _ := accel.ReadAcceleration() 170 | label3.SetText(fmt.Sprintf("LIS3DH : X=%8d", x), white) 171 | display.DrawRGBBitmap(0, 80, label3.buf, label3.w, label3.h) 172 | label4.SetText(fmt.Sprintf(": Y=%8d", y), white) 173 | display.DrawRGBBitmap(0x0b*10, 100, label4.buf, label4.w, label4.h) 174 | label5.SetText(fmt.Sprintf(": Z=%8d", z), white) 175 | display.DrawRGBBitmap(0x0b*10, 120, label5.buf, label5.w, label5.h) 176 | 177 | x2, y2, _ := accel.ReadRawAcceleration() 178 | for i := range rawXY { 179 | display.FillRectangle(50-1-rawXY[(rawXYi+i)%16][1], 170-1+rawXY[(rawXYi+i)%16][0], 2, 2, black) 180 | } 181 | rawXY[rawXYi][0] = x2 / 256 182 | rawXY[rawXYi][1] = y2 / 256 183 | 184 | display.DrawFastVLine(50, 120, 220, gray) 185 | display.DrawFastHLine(0, 100, 170, gray) 186 | 187 | for i := range rawXY { 188 | i = 15 - i 189 | display.FillRectangle(50-1-rawXY[(rawXYi+i)%16][1], 170-1+rawXY[(rawXYi+i)%16][0], 2, 2, color.RGBA{255 - 16*(uint8((i)%16)), 0, 0, 255}) 190 | } 191 | rawXYi = (rawXYi + 16 - 1) % 16 192 | 193 | for time.Now().Sub(lastRequestTime).Milliseconds() <= 50 { 194 | } 195 | lastRequestTime = time.Now() 196 | 197 | cnt++ 198 | if (cnt & 0x0F) == 0 { 199 | led.Toggle() 200 | ir.Toggle() 201 | } 202 | } 203 | } 204 | 205 | type label struct { 206 | buf []uint16 207 | w int16 208 | h int16 209 | fontHeight int16 210 | } 211 | 212 | func NewLabel(w, h int) *label { 213 | return &label{ 214 | buf: make([]uint16, w*h), 215 | w: int16(w), 216 | h: int16(h), 217 | fontHeight: int16(tinyfont.GetGlyph(&freemono.Regular9pt7b, '0').Info().Height), 218 | } 219 | } 220 | 221 | func (l *label) Size() (int16, int16) { 222 | return l.w, l.h 223 | } 224 | 225 | func (l *label) SetPixel(x, y int16, c color.RGBA) { 226 | if x < 0 || y < 0 || l.w < x || l.h < y { 227 | return 228 | } 229 | l.buf[y*l.w+x] = ili9341.RGBATo565(c) 230 | } 231 | 232 | func (l *label) Display() error { 233 | return nil 234 | } 235 | 236 | func (l *label) SetText(str string, c color.RGBA) { 237 | for i := range l.buf { 238 | l.buf[i] = 0 239 | } 240 | 241 | tinyfont.WriteLine(l, &freemono.Regular9pt7b, 3, l.fontHeight, str, c) 242 | } 243 | -------------------------------------------------------------------------------- /wioterminal/sample/wioterminal_tinygo_sample.uf2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sago35/tinygo-examples/347475af179e3759ae2f1b509df4d36593da15ab/wioterminal/sample/wioterminal_tinygo_sample.uf2 -------------------------------------------------------------------------------- /wioterminal/usbcdc/README.md: -------------------------------------------------------------------------------- 1 | # wioterminal/usbcdc 2 | 3 | First, flash the program to Wio Terminal. 4 | 5 | ``` 6 | $ tinygo flash --target wioterminal ./wioterminal/usbcdc/ 7 | ``` 8 | 9 | You can then run `./wioterminal/usbcdc/cmd/wio-client` to control the Wio Terminal via usbcdc. 10 | `-port` must be specified to execute the command. 11 | 12 | ``` 13 | $ go run ./wioterminal/usbcdc/cmd/wio-client -port COM8 14 | hello world 15 | こんにちは 世界 16 | hello world 17 | こんにちは 世界 18 | hello world 19 | ``` 20 | -------------------------------------------------------------------------------- /wioterminal/usbcdc/cmd/wio-client/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "flag" 6 | "fmt" 7 | "log" 8 | "time" 9 | 10 | "go.bug.st/serial" 11 | ) 12 | 13 | func main() { 14 | err := run() 15 | if err != nil { 16 | log.Fatal(err) 17 | } 18 | } 19 | 20 | func run() error { 21 | port := flag.String("port", "", "port") 22 | flag.Parse() 23 | 24 | p, err := serial.Open(*port, &serial.Mode{BaudRate: 115200}) 25 | if err != nil { 26 | return err 27 | } 28 | 29 | ch := make(chan string) 30 | 31 | go func() { 32 | for { 33 | time.Sleep(500 * time.Millisecond) 34 | ch <- "led" 35 | } 36 | }() 37 | 38 | go func() { 39 | for { 40 | time.Sleep(777 * time.Millisecond) 41 | ch <- "lcd" 42 | } 43 | }() 44 | 45 | go func() { 46 | i := 0 47 | for { 48 | time.Sleep(1 * time.Second) 49 | ch <- fmt.Sprintf("msg %d", i) 50 | i = 1 - i 51 | } 52 | }() 53 | 54 | scanner := bufio.NewScanner(p) 55 | for cmd := range ch { 56 | fmt.Fprintf(p, "%s\r\n", cmd) 57 | for { 58 | scanner.Scan() 59 | ret := scanner.Text() 60 | if ret == "ok" { 61 | break 62 | } else { 63 | fmt.Printf("%s\n", ret) 64 | } 65 | } 66 | } 67 | 68 | return nil 69 | } 70 | -------------------------------------------------------------------------------- /wioterminal/usbcdc/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | "strings" 7 | 8 | "machine" 9 | ) 10 | 11 | func main() { 12 | err := run() 13 | if err != nil { 14 | fmt.Printf("%s\n", err.Error()) 15 | } 16 | select {} 17 | } 18 | 19 | func run() error { 20 | led := machine.LED 21 | led.Configure(machine.PinConfig{Mode: machine.PinOutput}) 22 | led.Low() 23 | 24 | lcd := machine.LCD_BACKLIGHT 25 | lcd.Configure(machine.PinConfig{Mode: machine.PinOutput}) 26 | lcd.Low() 27 | 28 | ser := machine.Serial 29 | 30 | server := CommandServer{ 31 | LED: led, 32 | LCD: lcd, 33 | Messages: []string{ 34 | "hello world", 35 | "こんにちは 世界", 36 | }, 37 | } 38 | 39 | i := 0 40 | input := [256]byte{} 41 | for { 42 | if ser.Buffered() > 0 { 43 | data, _ := ser.ReadByte() 44 | 45 | switch data { 46 | case 10, 13: // CR or LF 47 | if i == 0 { 48 | continue 49 | } 50 | 51 | ret, err := server.RunCommand(string(input[:i])) 52 | if err != nil { 53 | return err 54 | } 55 | fmt.Print(ret) 56 | i = 0 57 | default: 58 | input[i] = data 59 | i++ 60 | } 61 | } 62 | } 63 | 64 | return nil 65 | } 66 | 67 | type CommandServer struct { 68 | LED machine.Pin 69 | LCD machine.Pin 70 | Messages []string 71 | } 72 | 73 | func (s *CommandServer) RunCommand(command string) (string, error) { 74 | if command == "" { 75 | return "ok\r\n", nil 76 | } 77 | 78 | spl := strings.Split(command, " ") 79 | switch spl[0] { 80 | case "led", "lcd": 81 | target := s.LED 82 | if spl[0] == "lcd" { 83 | target = s.LCD 84 | } 85 | 86 | if len(spl) == 1 { 87 | target.Toggle() 88 | } else if spl[1] == "on" { 89 | target.High() 90 | } else { 91 | target.Low() 92 | } 93 | case "msg": 94 | idx := 0 95 | if len(spl) > 1 { 96 | n, err := strconv.ParseInt(spl[1], 0, 0) 97 | if err != nil { 98 | idx = 0 99 | } else { 100 | idx = int(n) 101 | } 102 | } 103 | 104 | if idx > len(s.Messages) { 105 | idx = 0 106 | } 107 | 108 | return fmt.Sprintf("%s\r\nok\r\n", s.Messages[idx]), nil 109 | default: 110 | } 111 | 112 | return "ok\r\n", nil 113 | } 114 | -------------------------------------------------------------------------------- /wioterminal/webclient/init_other.go: -------------------------------------------------------------------------------- 1 | //go:build !wioterminal 2 | // +build !wioterminal 3 | 4 | package main 5 | 6 | import ( 7 | "io" 8 | "net/http" 9 | ) 10 | 11 | func _init() error { 12 | message = "hello from Go" 13 | return nil 14 | } 15 | 16 | func post(url, contentType string, body io.Reader) (resp *http.Response, err error) { 17 | return http.Post(url, contentType, body) 18 | } 19 | -------------------------------------------------------------------------------- /wioterminal/webclient/init_wioterminal.go: -------------------------------------------------------------------------------- 1 | //go:build wioterminal 2 | // +build wioterminal 3 | 4 | package main 5 | 6 | import ( 7 | "io" 8 | "time" 9 | 10 | "github.com/sago35/tinygo-examples/wioterminal/initialize" 11 | "tinygo.org/x/drivers/net/http" 12 | ) 13 | 14 | var ( 15 | ssid string 16 | password string 17 | ) 18 | 19 | func _init() error { 20 | message = "hello from TinyGo" 21 | _, err := initialize.Wifi(ssid, password, time.Second*10) 22 | return err 23 | } 24 | 25 | func post(url, contentType string, body io.Reader) (resp *http.Response, err error) { 26 | return http.Post(url, contentType, body) 27 | } 28 | -------------------------------------------------------------------------------- /wioterminal/webclient/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | "time" 7 | ) 8 | 9 | var message = "hello world" 10 | var url = "http://192.168.1.102/" 11 | 12 | func main() { 13 | err := _init() 14 | for err != nil { 15 | fmt.Printf("error : %w\r\n", err) 16 | time.Sleep(10 * time.Second) 17 | } 18 | 19 | body := fmt.Sprintf(`{"message": "%s"}`, message) 20 | post(url, "application/json", strings.NewReader(body)) 21 | } 22 | -------------------------------------------------------------------------------- /wioterminal/webclient/server/server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "log" 7 | "net/http" 8 | ) 9 | 10 | func handler(w http.ResponseWriter, r *http.Request) { 11 | fmt.Fprintf(w, "ok") 12 | 13 | body := r.Body 14 | defer body.Close() 15 | 16 | b, err := ioutil.ReadAll(body) 17 | if err != nil { 18 | panic(err) 19 | } 20 | log.Printf("%s\n", string(b)) 21 | } 22 | 23 | func main() { 24 | http.HandleFunc("/", handler) 25 | err := http.ListenAndServe(":80", nil) 26 | if err != nil { 27 | log.Fatal(err) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /wioterminal/webclient/server/server.go.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ~/dev/src/github.com/sago35/tinygo-examples/wioterminal/webclient/server/server.go.html 6 | 7 | 8 | 9 | 10 | 11 | 24 | 25 | 26 |
27 | package main
28 | 
29 | import (
30 |     "fmt"
31 |     "io/ioutil"
32 |     "log"
33 |     "net/http"
34 | )
35 | 
36 | func handler(w http.ResponseWriter, r *http.Request) {
37 |     fmt.Fprintf(w, "ok")
38 | 
39 |     body := r.Body
40 |     defer body.Close()
41 | 
42 |     b, err := ioutil.ReadAll(body)
43 |     if err != nil {
44 |         panic(err)
45 |     }
46 |     log.Printf("%s\n", string(b))
47 | }
48 | 
49 | func main() {
50 |     http.HandleFunc("/", handler)
51 |     err := http.ListenAndServe(":80", nil)
52 |     if err != nil {
53 |         log.Fatal(err)
54 |     }
55 | }
56 | 
57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /wioterminal/wiobadge/m5stack.go: -------------------------------------------------------------------------------- 1 | //go:build m5stack 2 | 3 | package main 4 | 5 | import ( 6 | "machine" 7 | ) 8 | 9 | func init() { 10 | button1 = machine.BUTTON 11 | } 12 | -------------------------------------------------------------------------------- /wioterminal/wiobadge/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | _ "embed" 6 | "fmt" 7 | "image/color" 8 | "machine" 9 | "time" 10 | 11 | "tinygo.org/x/drivers/examples/ili9341/initdisplay" 12 | "tinygo.org/x/drivers/ili9341" 13 | "tinygo.org/x/drivers/image/jpeg" 14 | "tinygo.org/x/drivers/image/png" 15 | ) 16 | 17 | var ( 18 | black = color.RGBA{0, 0, 0, 255} 19 | white = color.RGBA{255, 255, 255, 255} 20 | red = color.RGBA{255, 0, 0, 255} 21 | blue = color.RGBA{0, 0, 255, 255} 22 | green = color.RGBA{0, 255, 0, 255} 23 | ) 24 | 25 | var ( 26 | display *ili9341.Device 27 | ) 28 | 29 | func main() { 30 | err := run() 31 | for err != nil { 32 | errorMessage(err) 33 | } 34 | } 35 | 36 | //go:embed name.png 37 | var name_png []byte 38 | 39 | //go:embed qrcode_x.png 40 | var qrcode_x_png []byte 41 | 42 | var button1 machine.Pin 43 | 44 | func run() error { 45 | display = initdisplay.InitDisplay() 46 | 47 | width, height := display.Size() 48 | if width < 320 || height < 240 { 49 | display.SetRotation(ili9341.Rotation270) 50 | } 51 | 52 | display.FillScreen(black) 53 | 54 | button1.Configure(machine.PinConfig{Mode: machine.PinInputPullup}) 55 | 56 | var err error 57 | err = drawPng(display, name_png) 58 | if err != nil { 59 | return err 60 | } 61 | 62 | state := 0 63 | for { 64 | if !button1.Get() { 65 | state = 1 - state 66 | switch state { 67 | case 0: 68 | err = drawPng(display, name_png) 69 | if err != nil { 70 | return err 71 | } 72 | default: 73 | err = drawPng(display, qrcode_x_png) 74 | if err != nil { 75 | return err 76 | } 77 | } 78 | 79 | time.Sleep(100 * time.Millisecond) 80 | } 81 | } 82 | 83 | return nil 84 | } 85 | 86 | // Define the buffer required for the callback. In most cases, this setting 87 | // should be sufficient. For jpeg, the callback will always be called every 88 | // 3*8*8*4 pix. png will be called every line, i.e. every width pix. 89 | var buffer [3 * 8 * 8 * 4]uint16 90 | 91 | func drawPng(display *ili9341.Device, b []byte) error { 92 | p := bytes.NewReader(b) 93 | png.SetCallback(buffer[:], func(data []uint16, x, y, w, h, width, height int16) { 94 | err := display.DrawRGBBitmap(x, y, data[:w*h], w, h) 95 | if err != nil { 96 | errorMessage(fmt.Errorf("error drawPng: %s", err)) 97 | } 98 | }) 99 | 100 | _, err := png.Decode(p) 101 | return err 102 | } 103 | 104 | func drawJpeg(display *ili9341.Device, b []byte) error { 105 | p := bytes.NewReader(b) 106 | jpeg.SetCallback(buffer[:], func(data []uint16, x, y, w, h, width, height int16) { 107 | err := display.DrawRGBBitmap(x, y, data[:w*h], w, h) 108 | if err != nil { 109 | errorMessage(fmt.Errorf("error drawJpeg: %s", err)) 110 | } 111 | }) 112 | 113 | _, err := jpeg.Decode(p) 114 | return err 115 | } 116 | 117 | func errorMessage(err error) { 118 | for { 119 | fmt.Printf("%s\r\n", err.Error()) 120 | time.Sleep(5 * time.Second) 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /wioterminal/wiobadge/name.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sago35/tinygo-examples/347475af179e3759ae2f1b509df4d36593da15ab/wioterminal/wiobadge/name.png -------------------------------------------------------------------------------- /wioterminal/wiobadge/qrcode_x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sago35/tinygo-examples/347475af179e3759ae2f1b509df4d36593da15ab/wioterminal/wiobadge/qrcode_x.png -------------------------------------------------------------------------------- /wioterminal/wiobadge/wioterminal.go: -------------------------------------------------------------------------------- 1 | //go:build wioterminal 2 | 3 | package main 4 | 5 | import ( 6 | "machine" 7 | ) 8 | 9 | func init() { 10 | button1 = machine.WIO_KEY_C 11 | } 12 | -------------------------------------------------------------------------------- /xiao-ble/README.md: -------------------------------------------------------------------------------- 1 | # xiao-ble examples 2 | 3 | TinyGo example of XIAO BLE. 4 | This is a Demo using Bluetooth. 5 | 6 | * ble-led 7 | * [./xiao-ble/ble-led-client/](./xiao-ble/ble-led-client/) 8 | * [./xiao-ble/ble-led-client-xiao/](./xiao-ble/ble-led-client-xiao/) 9 | * [./xiao-ble/ble-led-server/](./xiao-ble/ble-led-server/) 10 | 11 | [![](https://img.youtube.com/vi/HWBxuMbNUTI/0.jpg)](https://www.youtube.com/watch?v=HWBxuMbNUTI) 12 | 13 | ## How to use 14 | 15 | First write ble-led-server to the XIAO BLE. 16 | 17 | ```shell 18 | $ tinygo flash --target xiao-ble --size short ./xiao-ble/ble-led-server 19 | ``` 20 | 21 | Perform the following from a Bluetooth-enabled PC. 22 | If successful, the LED on the XIAO BLE will flash. 23 | 24 | ```shell 25 | $ go run ./xiao-ble/ble-led-client/ 26 | ``` 27 | 28 | This source code works perfectly with TinyGo. 29 | The result is the same as when run from a PC. 30 | 31 | ```shell 32 | $ tinygo flash --target xiao-ble --size short ./xiao-ble/ble-led-client/ 33 | ``` 34 | 35 | If you have another XIAO BLE, you can try the same Demo as in the video. 36 | 37 | ```shell 38 | $ tinygo flash --target xiao-ble --size short ./xiao-ble/ble-led-client-xiao/ 39 | ``` 40 | -------------------------------------------------------------------------------- /xiao-ble/ble-led-client-xiao/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // This is the most minimal blinky example and should run almost everywhere. 4 | 5 | import ( 6 | "fmt" 7 | "machine" 8 | "runtime" 9 | "runtime/volatile" 10 | "time" 11 | 12 | "tinygo.org/x/bluetooth" 13 | ) 14 | 15 | // bluetooth 16 | var ( 17 | adapter = bluetooth.DefaultAdapter 18 | dc bluetooth.DeviceCharacteristic 19 | serviceUUID = bluetooth.NewUUID([16]byte{0xa0, 0xb4, 0x00, 0x01, 0x92, 0x6d, 0x4d, 0x61, 0x98, 0xdf, 0x8c, 0x5c, 0x62, 0xee, 0x53, 0xb3}) 20 | charUUID = bluetooth.NewUUID([16]byte{0xa0, 0xb4, 0x00, 0x02, 0x92, 0x6d, 0x4d, 0x61, 0x98, 0xdf, 0x8c, 0x5c, 0x62, 0xee, 0x53, 0xb3}) 21 | ) 22 | 23 | var ( 24 | led = machine.LED 25 | ledR = machine.LED_RED 26 | ledG = machine.LED_GREEN 27 | ledB = machine.LED_BLUE 28 | 29 | b1 = machine.D0 30 | b2 = machine.D1 31 | 32 | patterns = [][]byte{ 33 | {0xFF, 0x00, 0x00, 0x00}, 34 | {0x00, 0xFF, 0x00, 0x00}, 35 | {0x00, 0x00, 0xFF, 0x00}, 36 | } 37 | 38 | lightLed volatile.Register8 39 | rgbLed volatile.Register8 40 | ) 41 | 42 | func initBLE() { 43 | println("starting") 44 | must("enable BLE stack", adapter.Enable()) 45 | 46 | // The address to connect to. Set during scanning and read afterwards. 47 | var foundDevice bluetooth.ScanResult 48 | 49 | // Scan for NUS peripheral. 50 | println("Scanning...") 51 | err := adapter.Scan(func(adapter *bluetooth.Adapter, result bluetooth.ScanResult) { 52 | fmt.Printf("%#v\n", result.Address.String()) 53 | if result.LocalName() != "TinyGo ble-led-server" { 54 | return 55 | } 56 | foundDevice = result 57 | 58 | // Stop the scan. 59 | err := adapter.StopScan() 60 | if err != nil { 61 | // Unlikely, but we can't recover from this. 62 | println("failed to stop the scan:", err.Error()) 63 | } 64 | }) 65 | if err != nil { 66 | println("could not start a scan:", err.Error()) 67 | return 68 | } 69 | 70 | // Found a device: print this event. 71 | if name := foundDevice.LocalName(); name == "" { 72 | print("Connecting to ", foundDevice.Address.String(), "...") 73 | println() 74 | } else { 75 | print("Connecting to ", name, " (", foundDevice.Address.String(), ")...") 76 | println() 77 | } 78 | 79 | // Found a NUS peripheral. Connect to it. 80 | device, err := adapter.Connect(foundDevice.Address, bluetooth.ConnectionParams{}) 81 | if err != nil { 82 | println("Failed to connect:", err.Error()) 83 | return 84 | } 85 | 86 | // Connected. Look up the Nordic UART Service. 87 | println("Discovering service...") 88 | services, err := device.DiscoverServices([]bluetooth.UUID{serviceUUID}) 89 | if err != nil { 90 | println("Failed to discover the Nordic UART Service:", err.Error()) 91 | return 92 | } 93 | service := services[0] 94 | 95 | // Get the two characteristics present in this service. 96 | chars, err := service.DiscoverCharacteristics([]bluetooth.UUID{charUUID}) 97 | if err != nil { 98 | println("Failed to discover RX and TX characteristics:", err.Error()) 99 | return 100 | } 101 | 102 | dc = chars[0] 103 | } 104 | 105 | func main() { 106 | initBLE() 107 | 108 | led.Configure(machine.PinConfig{Mode: machine.PinOutput}) 109 | ledR.Configure(machine.PinConfig{Mode: machine.PinOutput}) 110 | ledG.Configure(machine.PinConfig{Mode: machine.PinOutput}) 111 | ledB.Configure(machine.PinConfig{Mode: machine.PinOutput}) 112 | 113 | b1.Configure(machine.PinConfig{Mode: machine.PinInputPullup}) 114 | b1.SetInterrupt(machine.PinFalling, func(machine.Pin) { 115 | if lightLed.Get() != 0 { 116 | lightLed.Set(0) 117 | } else { 118 | lightLed.Set(1) 119 | } 120 | }) 121 | 122 | b2.Configure(machine.PinConfig{Mode: machine.PinInputPullup}) 123 | b2.SetInterrupt(machine.PinFalling, func(machine.Pin) { 124 | if rgbLed.Get() != 0 { 125 | rgbLed.Set(0) 126 | } else { 127 | rgbLed.Set(1) 128 | } 129 | }) 130 | 131 | ledValue := []byte{0x00, 0x00, 0x00, 0x00} 132 | 133 | go func() { 134 | index := 0 135 | for { 136 | if rgbLed.Get() != 0 { 137 | ledValue[0] = patterns[index][0] 138 | ledValue[1] = patterns[index][1] 139 | ledValue[2] = patterns[index][2] 140 | time.Sleep(time.Millisecond * 500) 141 | index = (index + 1) % len(patterns) 142 | } else { 143 | runtime.Gosched() 144 | } 145 | } 146 | }() 147 | 148 | go func() { 149 | for { 150 | if lightLed.Get() != 0 { 151 | ledValue[3] = 0x00 152 | } else { 153 | ledValue[3] = 0xFF 154 | } 155 | runtime.Gosched() 156 | } 157 | }() 158 | 159 | for { 160 | setLEDs(ledValue) 161 | time.Sleep(10 * time.Millisecond) 162 | } 163 | } 164 | 165 | func setLEDs(b []byte) { 166 | ledR.Set(b[0] == 0x00) 167 | ledG.Set(b[1] == 0x00) 168 | ledB.Set(b[2] == 0x00) 169 | led.Set(b[3] == 0x00) 170 | dc.WriteWithoutResponse(b) 171 | } 172 | 173 | func must(action string, err error) { 174 | if err != nil { 175 | panic("failed to " + action + ": " + err.Error()) 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /xiao-ble/ble-led-client/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // This is the most minimal blinky example and should run almost everywhere. 4 | 5 | import ( 6 | "fmt" 7 | "time" 8 | 9 | "tinygo.org/x/bluetooth" 10 | ) 11 | 12 | // bluetooth 13 | var ( 14 | adapter = bluetooth.DefaultAdapter 15 | dc bluetooth.DeviceCharacteristic 16 | serviceUUID = bluetooth.NewUUID([16]byte{0xa0, 0xb4, 0x00, 0x01, 0x92, 0x6d, 0x4d, 0x61, 0x98, 0xdf, 0x8c, 0x5c, 0x62, 0xee, 0x53, 0xb3}) 17 | charUUID = bluetooth.NewUUID([16]byte{0xa0, 0xb4, 0x00, 0x02, 0x92, 0x6d, 0x4d, 0x61, 0x98, 0xdf, 0x8c, 0x5c, 0x62, 0xee, 0x53, 0xb3}) 18 | ) 19 | 20 | var ( 21 | patterns = [][]byte{ 22 | {0xFF, 0x00, 0x00, 0x00}, 23 | {0x00, 0xFF, 0x00, 0x00}, 24 | {0x00, 0x00, 0xFF, 0x00}, 25 | } 26 | ) 27 | 28 | func initBLE() { 29 | println("starting") 30 | must("enable BLE stack", adapter.Enable()) 31 | 32 | // The address to connect to. Set during scanning and read afterwards. 33 | var foundDevice bluetooth.ScanResult 34 | 35 | // Scan for NUS peripheral. 36 | println("Scanning...") 37 | err := adapter.Scan(func(adapter *bluetooth.Adapter, result bluetooth.ScanResult) { 38 | fmt.Printf("%#v\n", result.Address.String()) 39 | if result.LocalName() != "TinyGo ble-led-server" { 40 | return 41 | } 42 | foundDevice = result 43 | 44 | // Stop the scan. 45 | err := adapter.StopScan() 46 | if err != nil { 47 | // Unlikely, but we can't recover from this. 48 | println("failed to stop the scan:", err.Error()) 49 | } 50 | }) 51 | if err != nil { 52 | println("could not start a scan:", err.Error()) 53 | return 54 | } 55 | 56 | // Found a device: print this event. 57 | if name := foundDevice.LocalName(); name == "" { 58 | print("Connecting to ", foundDevice.Address.String(), "...") 59 | println() 60 | } else { 61 | print("Connecting to ", name, " (", foundDevice.Address.String(), ")...") 62 | println() 63 | } 64 | 65 | // Found a NUS peripheral. Connect to it. 66 | device, err := adapter.Connect(foundDevice.Address, bluetooth.ConnectionParams{}) 67 | if err != nil { 68 | println("Failed to connect:", err.Error()) 69 | return 70 | } 71 | 72 | // Connected. Look up the Nordic UART Service. 73 | println("Discovering service...") 74 | services, err := device.DiscoverServices([]bluetooth.UUID{serviceUUID}) 75 | if err != nil { 76 | println("Failed to discover the Nordic UART Service:", err.Error()) 77 | return 78 | } 79 | service := services[0] 80 | 81 | // Get the two characteristics present in this service. 82 | chars, err := service.DiscoverCharacteristics([]bluetooth.UUID{charUUID}) 83 | if err != nil { 84 | println("Failed to discover RX and TX characteristics:", err.Error()) 85 | return 86 | } 87 | 88 | dc = chars[0] 89 | } 90 | 91 | func main() { 92 | initBLE() 93 | ledValue := []byte{0x00, 0x00, 0x00, 0x00} 94 | index := 0 95 | for { 96 | ledValue[0] = patterns[index][0] 97 | ledValue[1] = patterns[index][1] 98 | ledValue[2] = patterns[index][2] 99 | index = (index + 1) % len(patterns) 100 | setLEDs(ledValue) 101 | time.Sleep(200 * time.Millisecond) 102 | } 103 | } 104 | 105 | func setLEDs(b []byte) { 106 | dc.WriteWithoutResponse(b) 107 | } 108 | 109 | func must(action string, err error) { 110 | if err != nil { 111 | panic("failed to " + action + ": " + err.Error()) 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /xiao-ble/ble-led-server/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "machine" 5 | "time" 6 | 7 | "tinygo.org/x/bluetooth" 8 | ) 9 | 10 | var adapter = bluetooth.DefaultAdapter 11 | 12 | // TODO: use atomics to access this value. 13 | var ledColor = []byte{0x00, 0x00, 0x00, 0x00} 14 | var leds = []machine.Pin{machine.LED_RED, machine.LED_GREEN, machine.LED_BLUE, machine.LED} 15 | var hasColorChange = true 16 | 17 | var ( 18 | serviceUUID = bluetooth.NewUUID([16]byte{0xa0, 0xb4, 0x00, 0x01, 0x92, 0x6d, 0x4d, 0x61, 0x98, 0xdf, 0x8c, 0x5c, 0x62, 0xee, 0x53, 0xb3}) 19 | charUUID = bluetooth.NewUUID([16]byte{0xa0, 0xb4, 0x00, 0x02, 0x92, 0x6d, 0x4d, 0x61, 0x98, 0xdf, 0x8c, 0x5c, 0x62, 0xee, 0x53, 0xb3}) 20 | ) 21 | 22 | func main() { 23 | println("starting") 24 | must("enable BLE stack", adapter.Enable()) 25 | adv := adapter.DefaultAdvertisement() 26 | must("config adv", adv.Configure(bluetooth.AdvertisementOptions{ 27 | LocalName: "TinyGo ble-led-server", 28 | })) 29 | must("start adv", adv.Start()) 30 | 31 | var ledColorCharacteristic bluetooth.Characteristic 32 | must("add service", adapter.AddService(&bluetooth.Service{ 33 | UUID: serviceUUID, 34 | Characteristics: []bluetooth.CharacteristicConfig{ 35 | { 36 | Handle: &ledColorCharacteristic, 37 | UUID: charUUID, 38 | Value: ledColor[:], 39 | Flags: bluetooth.CharacteristicReadPermission | bluetooth.CharacteristicWritePermission | bluetooth.CharacteristicWriteWithoutResponsePermission, 40 | WriteEvent: func(client bluetooth.Connection, offset int, value []byte) { 41 | if offset != 0 || len(value) != 4 { 42 | return 43 | } 44 | ledColor[0] = value[0] 45 | ledColor[1] = value[1] 46 | ledColor[2] = value[2] 47 | ledColor[3] = value[3] 48 | hasColorChange = true 49 | }, 50 | }, 51 | }, 52 | })) 53 | 54 | for _, led := range leds { 55 | led.Configure(machine.PinConfig{Mode: machine.PinOutput}) 56 | } 57 | 58 | for { 59 | for !hasColorChange { 60 | time.Sleep(10 * time.Millisecond) 61 | } 62 | hasColorChange = false 63 | for i, led := range leds { 64 | led.Set(ledColor[i] == 0) 65 | } 66 | } 67 | } 68 | 69 | func must(action string, err error) { 70 | if err != nil { 71 | panic("failed to " + action + ": " + err.Error()) 72 | } 73 | } 74 | --------------------------------------------------------------------------------