From 06dd60fba2b24144700494ebd6d298c5bc57de74 Mon Sep 17 00:00:00 2001 From: Scott Feldman Date: Fri, 21 Feb 2025 02:24:15 -0800 Subject: [PATCH] Add wifi driver comboat for Elecrow W5 rp2040 and rp2350 devices (#741) * Add wifi driver comboat for Elecrow W5 rp2040 and rp2350 devices Combo-AT driver uses AT command set to talk to onboard rtl8720d wifi device. The driver supports UDP/TCP/TLS clients in Wifi STA mode. Support for UDP/TCP servers is not supported, currently. https://aithinker-combo-guide.readthedocs.io/en/latest/docs/instruction/index.html https://aithinker-combo-guide.readthedocs.io/en/latest/docs/command-set/index.html https://aithinker-combo-guide.readthedocs.io/en/latest/docs/command-examples/index.html * Add wifi driver comboat for Elecrow W5 rp2040 and rp2350 devices Combo-AT driver uses AT command set to talk to onboard rtl8720d wifi device. The driver supports UDP/TCP/TLS clients in Wifi STA mode. Support for UDP/TCP servers is not supported, currently. https://aithinker-combo-guide.readthedocs.io/en/latest/docs/instruction/index.html https://aithinker-combo-guide.readthedocs.io/en/latest/docs/command-set/index.html https://aithinker-combo-guide.readthedocs.io/en/latest/docs/command-examples/index.html * switch to comboat_fw build tag for examples/net's that work with comboat driver --- comboat/comboat.go | 711 ++++++++++++++++++++++++++ comboat/errors.go | 86 ++++ examples/net/http-get/main.go | 2 +- examples/net/http-head/main.go | 2 +- examples/net/http-post/main.go | 2 +- examples/net/http-postform/main.go | 2 +- examples/net/mqttclient/natiu/main.go | 2 +- examples/net/mqttclient/paho/main.go | 2 +- examples/net/ntpclient/main.go | 2 +- examples/net/snake/init.go | 2 +- examples/net/snake/main.go | 2 +- examples/net/socket/main.go | 2 +- examples/net/tcpclient/main.go | 2 +- examples/net/tlsclient/main.go | 2 +- examples/net/webclient/main.go | 2 +- examples/net/websocket/dial/main.go | 2 +- netlink/probe/comboat.go | 26 + smoketest.sh | 3 + 18 files changed, 840 insertions(+), 14 deletions(-) create mode 100644 comboat/comboat.go create mode 100644 comboat/errors.go create mode 100644 netlink/probe/comboat.go diff --git a/comboat/comboat.go b/comboat/comboat.go new file mode 100644 index 0000000..2687c36 --- /dev/null +++ b/comboat/comboat.go @@ -0,0 +1,711 @@ +// Package comboat implements WiFi driver for the Aithinker-Combo-AT WiFi +// device found on the Elecrow W5 rp2040 and rp2350 devices. Ths WiFi device +// is a RTL8720d variant. The driver interface is via AT command set over UART +// (see reference docs below). +// +// NOTE: the driver doesn't support UDP/TCP server connections in STA mode, +// currently. UDP/TCP/TLS client connections are supported in STA mode. +// +// https://aithinker-combo-guide.readthedocs.io/en/latest/docs/instruction/index.html +// https://aithinker-combo-guide.readthedocs.io/en/latest/docs/command-set/index.html +// https://aithinker-combo-guide.readthedocs.io/en/latest/docs/command-examples/index.html + +package comboat // import "tinygo.org/x/drivers/comboat" + +import ( + "bytes" + "errors" + "fmt" + "io" + "machine" + "net" + "net/netip" + "strconv" + "sync" + "time" + + "tinygo.org/x/drivers" + "tinygo.org/x/drivers/netdev" + "tinygo.org/x/drivers/netlink" +) + +type Config struct { + BaudRate uint32 + Uart *machine.UART + Tx machine.Pin + Rx machine.Pin +} + +type socket struct { + protocol int + id string + rx chan []byte + remainder []byte + laddr netip.AddrPort // Set in Bind() +} + +type device struct { + cfg *Config + uart *machine.UART + uartMu sync.Mutex + mac net.HardwareAddr + ip netip.Addr + gateway netip.Addr + buf [1500]byte + pos int + last []byte + ok chan bool + txReady chan bool + accept chan string + err chan error + sockets [8]*socket + sync.Mutex +} + +func NewDevice(cfg *Config) *device { + return &device{ + cfg: cfg, + ok: make(chan bool), + txReady: make(chan bool), + accept: make(chan string), + err: make(chan error), + } +} + +func logDebug(msg string) { + //println("[DEBUG] " + msg) +} + +func logError(msg string) { + println("[ERROR] " + msg) +} + +func split(resp []byte, part int, del, on string) string { + parts := bytes.Split(resp, []byte(del)) + if part >= len(parts) { + return "Split parts error getting " + on + } + return string(parts[part]) +} + +func (d *device) getFWVersion() string { + return split(d.last, 1, ":", "FW version") +} + +func (d *device) saveMAC() { + raw := split(d.last, 1, ":", "MAC") + if len(raw) > 11 { + macStr := fmt.Sprintf("%s:%s:%s:%s:%s:%s", + raw[0:2], raw[2:4], raw[4:6], + raw[6:8], raw[8:10], raw[10:12]) + d.mac, _ = net.ParseMAC(macStr) + } +} + +var countryCodes = map[int]string{ + 1: "JP Japan", + 2: "American Samoa", + 3: "CA Canada", + 4: "US", + 5: "CN China", + 6: "Hong Kong, China", + 7: "Taiwan, China", + 8: "MO Macau, China", + 9: "IL Israel", + 10: "Singapore", + 11: "KR South Korea", + 12: "TR Türkiye", + 13: "AU Australia", + 14: "ZA South Africa", + 15: "BR Brazil", +} + +func (d *device) getCountry() (code string) { + code = split(d.last, 1, ":", "county code") + codeNum, err := strconv.Atoi(code) + if err != nil { + return + } + if val, ok := countryCodes[codeNum]; ok { + code = val + } + return +} + +func (d *device) saveIP() { + ipStr := split(d.last, 7, ",", "IP address") + gwStr := split(d.last, 8, ",", "gateway address") + d.ip, _ = netip.ParseAddr(ipStr) + d.gateway, _ = netip.ParseAddr(gwStr) +} + +func (d *device) execute(cmd string, timeout int) (err error) { + logDebug("EXECUTE " + cmd) + + d.uartMu.Lock() + _, err = d.uart.Write([]byte(cmd + "\r\n")) + d.uartMu.Unlock() + + if err != nil { + return + } + + t := time.NewTicker(time.Duration(timeout) * time.Millisecond) + defer t.Stop() + + select { + case <-t.C: + return errors.New("Timed out") + case <-d.ok: + return + case err = <-d.err: + return + } +} + +func (d *device) send(cmd string, timeout int) (err error) { + logDebug("EXECUTE " + cmd) + + d.uartMu.Lock() + _, err = d.uart.Write([]byte(cmd + "\r\n")) + d.uartMu.Unlock() + + if err != nil { + return + } + + t := time.NewTicker(time.Duration(timeout) * time.Millisecond) + defer t.Stop() + + select { + case <-t.C: + return errors.New("Timed out") + case <-d.txReady: + return + case err = <-d.err: + return + } +} + +func (d *device) findSocket(id string) (*socket, error) { + for _, s := range d.sockets { + if s.id == id { + return s, nil + } + } + return nil, errors.New("Socket not found with id: " + id) +} + +func (d *device) getSocket(sockfd int) (*socket, error) { + if sockfd < 0 || sockfd+1 > len(d.sockets) { + return nil, netdev.ErrInvalidSocketFd + } + if d.sockets[sockfd] == nil { + return nil, netdev.ErrInvalidSocketFd + } + return d.sockets[sockfd], nil +} + +func (d *device) handle(event []byte) { + logDebug("GOT EVENT " + string(event)) + switch { + + // SocketDisconnect, + case bytes.HasPrefix(event, []byte("SocketDisconnect")): + id := split(event, 1, ",", "SocketDisconnect") + s, err := d.findSocket(id) + if err == nil { + close(s.rx) // Sends io.EOF + } + + // SocketSeed,, + case bytes.HasPrefix(event, []byte("SocketSeed,2,1")): + //d.uart.Write([]byte("AT+SOCKET?" + "\r\n")) + } +} + +func (d *device) processUART() { + + if d.pos == 1 && d.buf[0] == '>' { + d.pos = 0 + logDebug("GOT >") + d.txReady <- true + } + + sofar := d.buf[:d.pos] + + if !bytes.HasSuffix(sofar, []byte("\r\n")) { + return + } + + // Strip CR/LF off end + sofar = sofar[:len(sofar)-2] + + switch { + + case bytes.HasPrefix(sofar, []byte("+EVENT:SocketDown")): + // +EVENT:SocketDown,,, + parts := bytes.SplitN(sofar, []byte(","), 4) + if len(parts) != 4 { + logError("Error parsing +EVENT:SocketDown: " + string(sofar)) + d.pos = 0 + return + } + id := string(parts[1]) + length, err := strconv.Atoi(string(parts[2])) + if err != nil { + logError("Error parsing length from: " + string(parts[2])) + d.pos = 0 + return + } + if length != len(parts[3]) { + // This can happen if actually contains a CR/LF. + // Return without resetting d.pos to continue reading + // in the full . + return + } + s, err := d.findSocket(id) + if err != nil { + logError(err.Error()) + d.pos = 0 + return + } + logDebug("GOT +EVENT:SocketDown," + id + "," + string(parts[2])) + d.pos = 0 + data := make([]byte, len(parts[3])) + copy(data, parts[3]) + s.rx <- data + + case bytes.HasPrefix(sofar, []byte("OK")): + d.pos = 0 + logDebug("GOT OK") + d.ok <- true + + case bytes.HasPrefix(sofar, []byte("ERROR")): + d.pos = 0 + logDebug("GOT ERROR") + errStr := getErrStr(d.last) + d.err <- errors.New(errStr) + + case bytes.HasPrefix(sofar, []byte("+EVENT:")): + d.pos = 0 + event := sofar[len("+EVENT:"):] + d.handle(event) + + default: + // Catch everything else and store in d.last + d.pos = 0 + size := len(sofar) + if size > 0 { + d.last = make([]byte, size) + copy(d.last, sofar[:size]) + logDebug("GOT LINE " + string(d.last)) + } + } +} + +func (d *device) serviceUART() { + for { + d.uartMu.Lock() + for d.uart.Buffered() > 0 { + if d.pos >= len(d.buf) { + println("Trying to write past buffer") + d.pos = 0 + break + } + var err error + d.buf[d.pos], err = d.uart.ReadByte() + if err == nil { + d.pos++ + d.processUART() + } + } + d.uartMu.Unlock() + time.Sleep(10 * time.Millisecond) + } +} + +func (d *device) NetConnect(params *netlink.ConnectParams) error { + + d.Lock() + defer d.Unlock() + + d.uart = d.cfg.Uart + d.uart.Configure(machine.UARTConfig{ + BaudRate: d.cfg.BaudRate, + TX: d.cfg.Tx, + RX: d.cfg.Rx, + }) + + go d.serviceUART() + + fmt.Printf("\r\n") + fmt.Printf("TinyGo Combo-AT WiFi network device driver\r\n") + + fmt.Printf("\r\n") + fmt.Printf("Driver version : %s\r\n", drivers.Version) + + if len(params.Ssid) == 0 { + return netlink.ErrMissingSSID + } + + // AT Test to see if device is alive + if err := d.execute("AT", 1000); err != nil { + return err + } + + // Disable echo + if err := d.execute("ATE0", 1000); err != nil { + return err + } + + // Get FW version + if err := d.execute("AT+GMR", 1000); err != nil { + return err + } + fmt.Printf("Combo-AT firmware version : %s\r\n", d.getFWVersion()) + + // Get/save MAC addresses + if err := d.execute("AT+CIPSTAMAC_DEF?", 1000); err != nil { + return err + } + d.saveMAC() + fmt.Printf("MAC address : %s\r\n", d.mac.String()) + + // Set country code US + if err := d.execute("AT+WCOUNTRY=4", 1000); err != nil { + return err + } + + // Get country code + if err := d.execute("AT+WCOUNTRY?", 1000); err != nil { + return err + } + fmt.Printf("WiFi country code : %s\r\n", d.getCountry()) + + // Set Wi-Fi working mode to STA and save to flash + if err := d.execute("AT+WMODE=1,1", 1000); err != nil { + return err + } + + // Connect to Wifi AP (keep trying until connected) + fmt.Printf("\r\n") + cmd := "AT+WJAP=" + params.Ssid + "," + params.Passphrase + + for { + fmt.Printf("Connecting to WiFi SSID '%s'...", params.Ssid) + if err := d.execute(cmd, 20000); err != nil { + fmt.Printf("FAILED (%s)\r\n", err.Error()) + continue + } + break + } + + fmt.Printf("CONNECTED\r\n") + + // Automatically reconnect to Wi-Fi after power on + if err := d.execute("AT+WAUTOCONN=1", 1000); err != nil { + return err + } + + // Get/save IP/gateway addresses + if err := d.execute("AT+WJAP?", 1000); err != nil { + return err + } + d.saveIP() + + fmt.Printf("\r\n") + fmt.Printf("DHCP-assigned IP : %s\r\n", d.ip) + fmt.Printf("DHCP-assigned gateway : %s\r\n", d.gateway) + fmt.Printf("\r\n") + + // Set socket receiving mode to active + if err := d.execute("AT+SOCKETRECVCFG=1", 1000); err != nil { + return err + } + + return nil +} + +func (d *device) NetDisconnect() { + d.Lock() + defer d.Unlock() + // Disconnect from WiFi AP + d.execute("AT+WDISCONNECT", 1000) +} + +func (d *device) NetNotify(cb func(netlink.Event)) { + fmt.Printf("\r\n%s\r\n", netlink.ErrNotSupported) +} + +func (d *device) GetHardwareAddr() (net.HardwareAddr, error) { + return d.mac, nil +} + +func (d *device) _getHostByName(name string) (ip netip.Addr, err error) { + if err = d.execute("AT+WDOMAIN="+name, 1000); err != nil { + return + } + ipStr := split(d.last, 1, ":", "host by name") + return netip.ParseAddr(ipStr) +} + +func (d *device) GetHostByName(name string) (ip netip.Addr, err error) { + + // If it's already a dotted-network address, and not a host name, + // return it + ip, err = netip.ParseAddr(name) + if err == nil { + return + } + + d.Lock() + defer d.Unlock() + + return d._getHostByName(name) +} + +func (d *device) Addr() (netip.Addr, error) { + return d.ip, nil +} + +func (d *device) Socket(domain, stype, protocol int) (int, error) { + + switch domain { + case netdev.AF_INET: + default: + return -1, netdev.ErrFamilyNotSupported + } + + switch { + case protocol == netdev.IPPROTO_TCP && stype == netdev.SOCK_STREAM: + case protocol == netdev.IPPROTO_TLS && stype == netdev.SOCK_STREAM: + case protocol == netdev.IPPROTO_UDP && stype == netdev.SOCK_DGRAM: + default: + return -1, netdev.ErrProtocolNotSupported + } + + d.Lock() + defer d.Unlock() + + // Search for empty slot in sockets array + for fd, s := range d.sockets { + if s == nil { + // Found one + d.sockets[fd] = &socket{ + protocol: protocol, + rx: make(chan []byte, 10), + } + return fd, nil + } + } + + return -1, netdev.ErrNoMoreSockets +} + +func (d *device) Bind(sockfd int, ip netip.AddrPort) error { + + d.Lock() + defer d.Unlock() + + s, err := d.getSocket(sockfd) + if err != nil { + return err + } + + s.laddr = ip + return nil +} + +func (d *device) Connect(sockfd int, host string, ip netip.AddrPort) error { + + var addr string + var cmd string + + d.Lock() + defer d.Unlock() + + s, err := d.getSocket(sockfd) + if err != nil { + return err + } + + if host == "" { + addr = ip.Addr().String() + } else { + ip, err := d._getHostByName(host) + if err != nil { + return err + } + addr = ip.String() + } + port := strconv.Itoa(int(ip.Port())) + + switch s.protocol { + case netdev.IPPROTO_UDP: + cmd = "AT+SOCKET=2," + addr + "," + port + case netdev.IPPROTO_TCP: + cmd = "AT+SOCKET=4," + addr + "," + port + case netdev.IPPROTO_TLS: + cmd = "AT+SOCKET=7," + addr + "," + port + } + + if cmd == "" { + return netdev.ErrProtocolNotSupported + } + + if err := d.execute(cmd, 20000); err != nil { + return err + } + + s.id = split(d.last, 1, "=", "connection ID") + + return nil +} + +func (d *device) Listen(sockfd, backlog int) error { + + // TODO Creating a TCP server socket isn't working when in STA mode, + // TODO returning error "Socket bind error". + // TODO The reference example shows a TCP server example in AP mode. + + /* + var cmd string + + d.Lock() + defer d.Unlock() + + s, err := d.getSocket(sockfd) + if err != nil { + return err + } + + port := strconv.Itoa(int(s.laddr.Port())) + + switch s.protocol { + case netdev.IPPROTO_UDP: + cmd = "AT+SOCKET=1," + port + case netdev.IPPROTO_TCP: + cmd = "AT+SOCKET=3," + port + } + + if cmd == "" { + return netdev.ErrProtocolNotSupported + } + + if err := d.execute(cmd, 20000); err != nil { + return err + } + + s.id = split(d.last, 1, "=", "connection ID") + */ + + return netdev.ErrNotSupported +} + +func (d *device) Accept(sockfd int) (int, netip.AddrPort, error) { + return 0, netip.AddrPort{}, netdev.ErrNotSupported +} + +func (d *device) Send(sockfd int, buf []byte, flags int, deadline time.Time) (int, error) { + + d.Lock() + defer d.Unlock() + + s, err := d.getSocket(sockfd) + if err != nil { + return 0, err + } + + cmd := fmt.Sprintf("AT+SOCKETSEND=%s,%d", s.id, len(buf)) + + if err := d.send(cmd, 1000); err != nil { + return 0, err + } + + // AT+SOCKETSEND will sub-packet send data into 1024-byte chunks, + // automatically, so send the full buffer in one shot, even if it's + // bigger than 1024 bytes. + + d.uartMu.Lock() + n, err := d.uart.Write(buf) + d.uartMu.Unlock() + + if err != nil { + return 0, err + } + + // Expecting "OK" after good send, or "ERROR" + + t := time.NewTicker(time.Duration(1000) * time.Millisecond) + defer t.Stop() + + select { + case <-t.C: + return 0, errors.New("Timed out") + case <-d.ok: + return n, nil + case err = <-d.err: + return 0, err + } +} + +func (d *device) Recv(sockfd int, buf []byte, flags int, deadline time.Time) (int, error) { + + d.Lock() + defer d.Unlock() + + s, err := d.getSocket(sockfd) + if err != nil { + return 0, err + } + + // 1. Use leftover data first + if len(s.remainder) > 0 { + n := copy(buf, s.remainder) + s.remainder = s.remainder[n:] + return n, nil + } + + // 2. Get new data from the channel + data, ok := <-s.rx + if !ok { + // Socket closed, return EOF + return 0, io.EOF + } + + // 3. Copy data, handle leftovers + n := copy(buf, data) + if n < len(data) { + s.remainder = data[n:] + } + + return n, nil +} + +func (d *device) Close(sockfd int) error { + + d.Lock() + defer d.Unlock() + + s, err := d.getSocket(sockfd) + if err != nil { + return err + } + + // Delete socket only if connection was successful (s.id is set) + if s.id != "" { + cmd := fmt.Sprintf("AT+SOCKETDEL=%s", s.id) + if err = d.execute(cmd, 1000); err != nil { + return err + } + } + + d.sockets[sockfd] = nil + + return nil +} + +func (d *device) SetSockOpt(sockfd, level, opt int, value interface{}) error { + return netdev.ErrNotSupported +} diff --git a/comboat/errors.go b/comboat/errors.go new file mode 100644 index 0000000..5b1bf6d --- /dev/null +++ b/comboat/errors.go @@ -0,0 +1,86 @@ +package comboat + +import ( + "bytes" + "strconv" +) + +var errStrings = map[int]string{ + + // System framework related error codes + + 0: "success", + 1: "The command is not supported (the combo framework contains the command but the current platform has not transplanted or adapted to support it)", + 2: "The command parameters contain unsupported operations (the current platform only supports some operations for this command)", + 3: "The instruction format is incorrect (this refers to the wrong number of parameters, for example, two parameters are required, but only one parameter is entered)", + 4: "Parameter error (the content of the parameter is wrong, for example, a number between 0 and 9 is required, but 10 or xyz is passed in, which is a parameter error)", + 5: "Parameter length error (command length exceeds the maximum supported length)", + 31: "The current command has not ended and needs to report the status asynchronously. This value is used by the state machine to determine the use of the command and no message is returned.", + 32: "Unknown error (or unhandled error type)", + + // Common error codes + + 33: "malloc error", + 34: "Failed to read buf", + 35: "Failed to write buf", + 36: "Configuration error (configuration error loaded from memory, for example, we set port -1 for OTA upgrade, and check port error when executing AT+OTA, then configuration error will be reported)", + 37: "Failed to create task", + 38: "Flash read and write failure", + 39: "Serial port configuration error, unsupported baud rate", + 40: "Serial port configuration error, unsupported data bits", + 41: "Serial port configuration error, unsupported stop bit", + 42: "Serial port configuration error, unsupported parity bit", + 43: "Serial port configuration error, unsupported flow control", + 44: "Serial port configuration failed", + 45: "Wrong username/password", + 46: "Low power mode error or unsupported low power mode", + 47: "Uninitialized configuration data error (including io mapping data)", + 63: "General error code (without other information)", + + // Wi-Fi related error codes + + 64: "Wi-Fi not initialized or initialization failed", + 65: "Wi-Fi mode error (unable to connect to Wi-Fi in single AP mode)", + 66: "Wi-Fi connection failed", + 67: "Wi-Fi connection successful, error in obtaining IP (DHCP)", + 68: "Failed to obtain encryption method", + 69: "The specified AP was not found.", + 70: "Wi-Fi scan start failed", + 71: "Wi-Fi scan timeout", + 72: "Failed to enable AP hotspot", + 73: "Failed to obtain the Wi-Fi information of the router or the AP information that you enabled yourself", + 74: "The network card (STA/AP) is not running", + 75: "Wi-Fi country code error (unsupported Wi-Fi country code)", + 76: "The current network configuration mode is wrong.", + 95: "Wi-Fi connection unknown error", + + // Socket related error codes + + 96: "Failed to create socket", + 97: "Socket connection failed", + 98: "DNS Failure", + 99: "The socket status is wrong (for example, TCP is not connected yet)", + 100: "Socket type error", + 101: "Socket send failed", + 102: "Socket receive failed", + 103: "Socket monitoring thread creation failed", + 104: "Socket bind error", + 105: "The current connection cannot be transparently linked (wrong socket type or number)", + 106: "PING test failed (all packets lost)", + 107: "Wi-Fi country code error (unsupported Wi-Fi country code)", + 108: "SSL Config Error", + 109: "SSL verification error (usually caused by unsupported SSL encryption type or certificate error)", + 127: "Unknown socket error", +} + +func getErrStr(errLine []byte) (errStr string) { + errStr = "Can't parse ERROR response" + tokens := bytes.Split(errLine, []byte(":")) + if len(tokens) > 1 { + errCode, err := strconv.Atoi(string(tokens[1])) + if err == nil { + errStr = errStrings[errCode] + } + } + return +} diff --git a/examples/net/http-get/main.go b/examples/net/http-get/main.go index 4359f8d..aa51acc 100644 --- a/examples/net/http-get/main.go +++ b/examples/net/http-get/main.go @@ -9,7 +9,7 @@ // examples/net/webclient (for HTTP) // examples/net/tlsclient (for HTTPS) -//go:build ninafw || wioterminal +//go:build ninafw || wioterminal || comboat_fw package main diff --git a/examples/net/http-head/main.go b/examples/net/http-head/main.go index 355e9d4..fe90397 100644 --- a/examples/net/http-head/main.go +++ b/examples/net/http-head/main.go @@ -9,7 +9,7 @@ // examples/net/webclient (for HTTP) // examples/net/tlsclient (for HTTPS) -//go:build ninafw || wioterminal +//go:build ninafw || wioterminal || comboat_fw package main diff --git a/examples/net/http-post/main.go b/examples/net/http-post/main.go index 8af7516..b5f55b1 100644 --- a/examples/net/http-post/main.go +++ b/examples/net/http-post/main.go @@ -9,7 +9,7 @@ // examples/net/webclient (for HTTP) // examples/net/tlsclient (for HTTPS) -//go:build ninafw || wioterminal +//go:build ninafw || wioterminal || comboat_fw package main diff --git a/examples/net/http-postform/main.go b/examples/net/http-postform/main.go index 03f87f9..c5757fa 100644 --- a/examples/net/http-postform/main.go +++ b/examples/net/http-postform/main.go @@ -9,7 +9,7 @@ // examples/net/webclient (for HTTP) // examples/net/tlsclient (for HTTPS) -//go:build ninafw || wioterminal +//go:build ninafw || wioterminal || comboat_fw package main diff --git a/examples/net/mqttclient/natiu/main.go b/examples/net/mqttclient/natiu/main.go index 5c82811..84111f5 100644 --- a/examples/net/mqttclient/natiu/main.go +++ b/examples/net/mqttclient/natiu/main.go @@ -4,7 +4,7 @@ // Note: It may be necessary to increase the stack size when using // paho.mqtt.golang. Use the -stack-size=4KB command line option. -//go:build ninafw || wioterminal || challenger_rp2040 +//go:build ninafw || wioterminal || challenger_rp2040 || comboat_fw package main diff --git a/examples/net/mqttclient/paho/main.go b/examples/net/mqttclient/paho/main.go index ac39a2c..a080b0d 100644 --- a/examples/net/mqttclient/paho/main.go +++ b/examples/net/mqttclient/paho/main.go @@ -4,7 +4,7 @@ // Note: It may be necessary to increase the stack size when using // paho.mqtt.golang. Use the -stack-size=4KB command line option. -//go:build ninafw || wioterminal || challenger_rp2040 +//go:build ninafw || wioterminal || challenger_rp2040 || comboat_fw package main diff --git a/examples/net/ntpclient/main.go b/examples/net/ntpclient/main.go index da54c5d..9ea079d 100644 --- a/examples/net/ntpclient/main.go +++ b/examples/net/ntpclient/main.go @@ -3,7 +3,7 @@ // It creates a UDP connection to request the current time and parse the // response from a NTP server. The system time is set to NTP time. -//go:build ninafw || wioterminal || challenger_rp2040 +//go:build ninafw || wioterminal || challenger_rp2040 || comboat_fw package main diff --git a/examples/net/snake/init.go b/examples/net/snake/init.go index a00a814..641ecd5 100644 --- a/examples/net/snake/init.go +++ b/examples/net/snake/init.go @@ -1,4 +1,4 @@ -//go:build ninafw || wioterminal +//go:build ninafw || wioterminal || comboat_fw package main diff --git a/examples/net/snake/main.go b/examples/net/snake/main.go index 40bfc65..a261fc1 100644 --- a/examples/net/snake/main.go +++ b/examples/net/snake/main.go @@ -31,7 +31,7 @@ // func. This forces segments to connect and run concurrently, which is a good // test of the underlying driver's ability to handle concurrent connections. -//go:build ninafw || wioterminal +//go:build ninafw || wioterminal || comboat_fw package main diff --git a/examples/net/socket/main.go b/examples/net/socket/main.go index f55b053..2e867fd 100644 --- a/examples/net/socket/main.go +++ b/examples/net/socket/main.go @@ -4,7 +4,7 @@ // // nc -lk 8080 -//go:build ninafw || wioterminal || challenger_rp2040 +//go:build ninafw || wioterminal || challenger_rp2040 || comboat_fw package main diff --git a/examples/net/tcpclient/main.go b/examples/net/tcpclient/main.go index 9ef9820..24f0775 100644 --- a/examples/net/tcpclient/main.go +++ b/examples/net/tcpclient/main.go @@ -5,7 +5,7 @@ // // nc -lk 8080 -//go:build ninafw || wioterminal || challenger_rp2040 || pico +//go:build ninafw || wioterminal || challenger_rp2040 || comboat_fw package main diff --git a/examples/net/tlsclient/main.go b/examples/net/tlsclient/main.go index 46cd110..adef220 100644 --- a/examples/net/tlsclient/main.go +++ b/examples/net/tlsclient/main.go @@ -5,7 +5,7 @@ // // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security -//go:build ninafw || wioterminal +//go:build ninafw || wioterminal || comboat_fw package main diff --git a/examples/net/webclient/main.go b/examples/net/webclient/main.go index 43096bf..6b28783 100644 --- a/examples/net/webclient/main.go +++ b/examples/net/webclient/main.go @@ -17,7 +17,7 @@ // } // --------------------------------------------------------------------------- -//go:build ninafw || wioterminal +//go:build ninafw || wioterminal || comboat_fw package main diff --git a/examples/net/websocket/dial/main.go b/examples/net/websocket/dial/main.go index 4aa4b8e..02e17da 100644 --- a/examples/net/websocket/dial/main.go +++ b/examples/net/websocket/dial/main.go @@ -6,7 +6,7 @@ // Note: It may be necessary to increase the stack size when using // "golang.org/x/net/websocket". Use the -stack-size=4KB command line option. -//go:build ninafw || wioterminal +//go:build ninafw || wioterminal || comboat_fw package main diff --git a/netlink/probe/comboat.go b/netlink/probe/comboat.go new file mode 100644 index 0000000..c1ed007 --- /dev/null +++ b/netlink/probe/comboat.go @@ -0,0 +1,26 @@ +//go:build comboat_fw + +package probe + +import ( + "machine" + + "tinygo.org/x/drivers/comboat" + "tinygo.org/x/drivers/netdev" + "tinygo.org/x/drivers/netlink" +) + +func Probe() (netlink.Netlinker, netdev.Netdever) { + + cfg := comboat.Config{ + BaudRate: 115200, + Uart: machine.UART1, + Tx: machine.UART1_TX_PIN, + Rx: machine.UART1_RX_PIN, + } + + combo := comboat.NewDevice(&cfg) + netdev.UseNetdev(combo) + + return combo, combo +} diff --git a/smoketest.sh b/smoketest.sh index eec70b1..f21e2be 100755 --- a/smoketest.sh +++ b/smoketest.sh @@ -154,3 +154,6 @@ tinygo build -size short -o ./build/test.hex -target=nano-rp2040 -stack-size 8kb tinygo build -size short -o ./build/test.hex -target=wioterminal -stack-size 8kb ./examples/net/webclient/ tinygo build -size short -o ./build/test.hex -target=wioterminal -stack-size 8kb ./examples/net/webserver/ tinygo build -size short -o ./build/test.hex -target=wioterminal -stack-size 8kb ./examples/net/mqttclient/paho/ +# network examples (comboat) +tinygo build -size short -o ./build/test.hex -target=elecrow-rp2040 -stack-size 8kb ./examples/net/tlsclient/ +tinygo build -size short -o ./build/test.hex -target=elecrow-rp2350 -stack-size 8kb ./examples/net/ntpclient/