Files
drivers/wifinina/wifinina.go
T
2023-12-06 20:09:39 +01:00

1898 lines
41 KiB
Go

// Package wifinina implements TCP wireless communication over SPI with an
// attached separate ESP32 SoC using the Arduino WiFiNINA protocol.
//
// In order to use this driver, the ESP32 must be flashed with specific
// firmware from Arduino. For more information:
// https://github.com/arduino/nina-fw
//
// 12/2022 sfeldma@gmail.com Heavily modified to use netdev interface
package wifinina // import "tinygo.org/x/drivers/wifinina"
import (
"encoding/binary"
"encoding/hex"
"fmt"
"io"
"machine"
"math/bits"
"net"
"sync"
"time"
"tinygo.org/x/drivers"
"tinygo.org/x/drivers/netdev"
"tinygo.org/x/drivers/netlink"
)
var _debug debug = debugBasic
//var _debug debug = debugBasic | debugNetdev
//var _debug debug = debugBasic | debugNetdev | debugCmd
//var _debug debug = debugBasic | debugNetdev | debugCmd | debugDetail
var (
version = "0.0.1"
driverName = "Tinygo ESP32 Wifi network device driver (WiFiNINA)"
)
const (
maxNetworks = 10
statusNoShield connectionStatus = 255
statusIdle connectionStatus = 0
statusNoSSIDAvail connectionStatus = 1
statusScanCompleted connectionStatus = 2
statusConnected connectionStatus = 3
statusConnectFailed connectionStatus = 4
statusConnectionLost connectionStatus = 5
statusDisconnected connectionStatus = 6
encTypeTKIP encryptionType = 2
encTypeCCMP encryptionType = 4
encTypeWEP encryptionType = 5
encTypeNone encryptionType = 7
encTypeAuto encryptionType = 8
tcpStateClosed = 0
tcpStateListen = 1
tcpStateSynSent = 2
tcpStateSynRcvd = 3
tcpStateEstablished = 4
tcpStateFinWait1 = 5
tcpStateFinWait2 = 6
tcpStateCloseWait = 7
tcpStateClosing = 8
tcpStateLastACK = 9
tcpStateTimeWait = 10
flagCmd = 0
flagReply = 1 << 7
flagData = 0x40
cmdStart = 0xE0
cmdEnd = 0xEE
cmdErr = 0xEF
dummyData = 0xFF
cmdSetNet = 0x10
cmdSetPassphrase = 0x11
cmdSetKey = 0x12
cmdSetIPConfig = 0x14
cmdSetDNSConfig = 0x15
cmdSetHostname = 0x16
cmdSetPowerMode = 0x17
cmdSetAPNet = 0x18
cmdSetAPPassphrase = 0x19
cmdSetDebug = 0x1A
cmdGetTemperature = 0x1B
cmdGetReasonCode = 0x1F
cmdGetConnStatus = 0x20
cmdGetIPAddr = 0x21
cmdGetMACAddr = 0x22
cmdGetCurrSSID = 0x23
cmdGetCurrBSSID = 0x24
cmdGetCurrRSSI = 0x25
cmdGetCurrEncrType = 0x26
cmdScanNetworks = 0x27
cmdStartServerTCP = 0x28
cmdGetStateTCP = 0x29
cmdDataSentTCP = 0x2A
cmdAvailDataTCP = 0x2B
cmdGetDataTCP = 0x2C
cmdStartClientTCP = 0x2D
cmdStopClientTCP = 0x2E
cmdGetClientStateTCP = 0x2F
cmdDisconnect = 0x30
cmdGetIdxRSSI = 0x32
cmdGetIdxEncrType = 0x33
cmdReqHostByName = 0x34
cmdGetHostByName = 0x35
cmdStartScanNetworks = 0x36
cmdGetFwVersion = 0x37
cmdSendDataUDP = 0x39
cmdGetRemoteData = 0x3A
cmdGetTime = 0x3B
cmdGetIdxBSSID = 0x3C
cmdGetIdxChannel = 0x3D
cmdPing = 0x3E
cmdGetSocket = 0x3F
// All commands with DATA_FLAG 0x4x send a 16bit Len
cmdSendDataTCP = 0x44
cmdGetDatabufTCP = 0x45
cmdInsertDataBuf = 0x46
// Regular format commands
cmdSetPinMode = 0x50
cmdSetDigitalWrite = 0x51
cmdSetAnalogWrite = 0x52
errTimeoutChipReady hwerr = 0x01
errTimeoutChipSelect hwerr = 0x02
errCheckStartCmd hwerr = 0x03
errWaitRsp hwerr = 0x04
errUnexpectedLength hwerr = 0xE0
errNoParamsReturned hwerr = 0xE1
errIncorrectSentinel hwerr = 0xE2
errCmdErrorReceived hwerr = 0xEF
errNotImplemented hwerr = 0xF0
errUnknownHost hwerr = 0xF1
errSocketAlreadySet hwerr = 0xF2
errConnectionTimeout hwerr = 0xF3
errNoData hwerr = 0xF4
errDataNotWritten hwerr = 0xF5
errCheckDataError hwerr = 0xF6
errBufferTooSmall hwerr = 0xF7
errNoSocketAvail hwerr = 0xFF
noSocketAvail sock = 0xFF
)
const (
protoModeTCP = iota
protoModeUDP
protoModeTLS
protoModeMul
)
type connectionStatus uint8
type encryptionType uint8
type sock uint8
type hwerr uint8
type socket struct {
protocol int
ip net.IP
port int
inuse bool
}
type Config struct {
// SPI config
Spi drivers.SPI
Freq uint32
Sdo machine.Pin
Sdi machine.Pin
Sck machine.Pin
// Device config
Cs machine.Pin
Ack machine.Pin
Gpio0 machine.Pin
Resetn machine.Pin
// ResetIsHigh controls if the RESET signal to the processor should be
// High or Low (the default). Set this to true for boards such as the
// Arduino MKR 1010, where the reset signal needs to go high instead of
// low.
ResetIsHigh bool
}
type wifinina struct {
cfg *Config
notifyCb func(netlink.Event)
mu sync.Mutex
spi drivers.SPI
cs machine.Pin
ack machine.Pin
gpio0 machine.Pin
resetn machine.Pin
buf [64]byte
ssids [maxNetworks]string
params *netlink.ConnectParams
netConnected bool
driverShown bool
deviceShown bool
spiSetup bool
killWatchdog chan bool
fault error
sockets map[sock]*socket // keyed by sock as returned by getSocket()
}
func newSocket(protocol int) *socket {
return &socket{protocol: protocol, inuse: true}
}
func New(cfg *Config) *wifinina {
w := wifinina{
cfg: cfg,
sockets: make(map[sock]*socket),
killWatchdog: make(chan bool),
cs: cfg.Cs,
ack: cfg.Ack,
gpio0: cfg.Gpio0,
resetn: cfg.Resetn,
}
return &w
}
func (err hwerr) Error() string {
return "[wifinina] error: 0x" + hex.EncodeToString([]byte{uint8(err)})
}
func (w *wifinina) reason() string {
reason := w.getReasonCode()
switch reason {
case 0:
return "unknown failure"
case 201:
return "no AP found"
case 202:
return "auth failed"
}
return fmt.Sprintf("%d", reason)
}
func (w *wifinina) connectToAP() error {
timeout := w.params.ConnectTimeout
if timeout == 0 {
timeout = netlink.DefaultConnectTimeout
}
if len(w.params.Ssid) == 0 {
return netlink.ErrMissingSSID
}
if debugging(debugBasic) {
fmt.Printf("Connecting to Wifi SSID '%s'...", w.params.Ssid)
}
start := time.Now()
// Start the connection process
w.setPassphrase(w.params.Ssid, w.params.Passphrase)
// Check if we connected
for {
status := w.getConnectionStatus()
switch status {
case statusConnected:
if debugging(debugBasic) {
fmt.Printf("CONNECTED\r\n")
}
if w.notifyCb != nil {
w.notifyCb(netlink.EventNetUp)
}
return nil
case statusConnectFailed:
if debugging(debugBasic) {
fmt.Printf("FAILED (%s)\r\n", w.reason())
}
return netlink.ErrConnectFailed
}
if time.Since(start) > timeout {
break
}
time.Sleep(1 * time.Second)
}
if debugging(debugBasic) {
fmt.Printf("FAILED (timed out)\r\n")
}
return netlink.ErrConnectTimeout
}
func (w *wifinina) netDisconnect() {
w.disconnect()
}
func (w *wifinina) showDriver() {
if w.driverShown {
return
}
if debugging(debugBasic) {
fmt.Printf("\r\n")
fmt.Printf("%s\r\n\r\n", driverName)
fmt.Printf("Driver version : %s\r\n", version)
}
w.driverShown = true
}
func (w *wifinina) setupSPI() {
if w.spiSetup {
return
}
spi := machine.NINA_SPI
spi.Configure(machine.SPIConfig{
Frequency: w.cfg.Freq,
SDO: w.cfg.Sdo,
SDI: w.cfg.Sdi,
SCK: w.cfg.Sck,
})
w.spi = spi
w.spiSetup = true
}
func (w *wifinina) start() {
pinUseDevice(w)
w.cs.Configure(machine.PinConfig{Mode: machine.PinOutput})
w.ack.Configure(machine.PinConfig{Mode: machine.PinInput})
w.resetn.Configure(machine.PinConfig{Mode: machine.PinOutput})
w.gpio0.Configure(machine.PinConfig{Mode: machine.PinOutput})
w.gpio0.High()
w.cs.High()
w.resetn.Set(w.cfg.ResetIsHigh)
time.Sleep(10 * time.Millisecond)
w.resetn.Set(!w.cfg.ResetIsHigh)
time.Sleep(750 * time.Millisecond)
w.gpio0.Low()
w.gpio0.Configure(machine.PinConfig{Mode: machine.PinInput})
}
func (w *wifinina) stop() {
w.resetn.Low()
w.cs.Configure(machine.PinConfig{Mode: machine.PinInput})
}
func (w *wifinina) showDevice() {
if w.deviceShown {
return
}
if debugging(debugBasic) {
fmt.Printf("ESP32 firmware version : %s\r\n", w.getFwVersion())
mac := w.getMACAddr()
fmt.Printf("MAC address : %s\r\n", mac.String())
fmt.Printf("\r\n")
}
w.deviceShown = true
}
func (w *wifinina) showIP() {
if debugging(debugBasic) {
ip, subnet, gateway := w.getIP()
fmt.Printf("\r\n")
fmt.Printf("DHCP-assigned IP : %s\r\n", ip.String())
fmt.Printf("DHCP-assigned subnet : %s\r\n", subnet.String())
fmt.Printf("DHCP-assigned gateway : %s\r\n", gateway.String())
fmt.Printf("\r\n")
}
}
func (w *wifinina) networkDown() bool {
return w.getConnectionStatus() != statusConnected
}
func (w *wifinina) watchdog() {
ticker := time.NewTicker(w.params.WatchdogTimeout)
for {
select {
case <-w.killWatchdog:
return
case <-ticker.C:
w.mu.Lock()
if w.fault != nil {
if debugging(debugBasic) {
fmt.Printf("Watchdog: FAULT: %s\r\n", w.fault)
}
w.netDisconnect()
w.netConnect(true)
w.fault = nil
} else if w.networkDown() {
if debugging(debugBasic) {
fmt.Printf("Watchdog: Wifi NOT CONNECTED, trying again...\r\n")
}
if w.notifyCb != nil {
w.notifyCb(netlink.EventNetDown)
}
w.netConnect(false)
}
w.mu.Unlock()
}
}
}
func (w *wifinina) netConnect(reset bool) error {
if reset {
w.start()
}
w.showDevice()
for i := 0; w.params.Retries == 0 || i < w.params.Retries; i++ {
if err := w.connectToAP(); err != nil {
switch err {
case netlink.ErrConnectTimeout, netlink.ErrConnectFailed:
continue
}
return err
}
break
}
if w.networkDown() {
return netlink.ErrConnectFailed
}
w.showIP()
return nil
}
func (w *wifinina) NetConnect(params *netlink.ConnectParams) error {
w.mu.Lock()
defer w.mu.Unlock()
if w.netConnected {
return netlink.ErrConnected
}
w.params = params
w.showDriver()
w.setupSPI()
if err := w.netConnect(true); err != nil {
return err
}
w.netConnected = true
if w.params.WatchdogTimeout != 0 {
go w.watchdog()
}
return nil
}
func (w *wifinina) NetDisconnect() {
w.mu.Lock()
defer w.mu.Unlock()
if !w.netConnected {
return
}
if w.params.WatchdogTimeout != 0 {
w.killWatchdog <- true
}
w.netDisconnect()
w.stop()
w.netConnected = false
if debugging(debugBasic) {
fmt.Printf("\r\nDisconnected from Wifi SSID '%s'\r\n\r\n", w.params.Ssid)
}
if w.notifyCb != nil {
w.notifyCb(netlink.EventNetDown)
}
}
func (w *wifinina) NetNotify(cb func(netlink.Event)) {
w.notifyCb = cb
}
func (w *wifinina) GetHostByName(name string) (net.IP, error) {
if debugging(debugNetdev) {
fmt.Printf("[GetHostByName] name: %s\r\n", name)
}
w.mu.Lock()
defer w.mu.Unlock()
ip := w.getHostByName(name)
if ip == "" {
return net.IP{}, netdev.ErrHostUnknown
}
return net.IP([]byte(ip)), nil
}
func (w *wifinina) GetHardwareAddr() (net.HardwareAddr, error) {
if debugging(debugNetdev) {
fmt.Printf("[GetHardwareAddr]\r\n")
}
w.mu.Lock()
defer w.mu.Unlock()
return w.getMACAddr(), nil
}
func (w *wifinina) GetIPAddr() (net.IP, error) {
if debugging(debugNetdev) {
fmt.Printf("[GetIPAddr]\r\n")
}
w.mu.Lock()
defer w.mu.Unlock()
ip, _, _ := w.getIP()
return net.IP(ip), nil
}
// See man socket(2) for standard Berkely sockets for Socket, Bind, etc.
// The driver strives to meet the function and semantics of socket(2).
func (w *wifinina) Socket(domain int, stype int, protocol int) (int, error) {
if debugging(debugNetdev) {
fmt.Printf("[Socket] domain: %d, type: %d, protocol: %d\r\n",
domain, stype, protocol)
}
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
}
w.mu.Lock()
defer w.mu.Unlock()
sock := w.getSocket()
if sock == noSocketAvail {
return -1, netdev.ErrNoMoreSockets
}
socket := newSocket(protocol)
w.sockets[sock] = socket
return int(sock), nil
}
func (w *wifinina) Bind(sockfd int, ip net.IP, port int) error {
if debugging(debugNetdev) {
fmt.Printf("[Bind] sockfd: %d, addr: %s:%d\r\n", sockfd, ip, port)
}
w.mu.Lock()
defer w.mu.Unlock()
var sock = sock(sockfd)
var socket = w.sockets[sock]
switch socket.protocol {
case netdev.IPPROTO_TCP:
case netdev.IPPROTO_TLS:
case netdev.IPPROTO_UDP:
w.startServer(sock, uint16(port), protoModeUDP)
}
socket.ip, socket.port = ip, port
return nil
}
func toUint32(ip net.IP) uint32 {
ip = ip.To4()
return uint32(ip[0])<<24 |
uint32(ip[1])<<16 |
uint32(ip[2])<<8 |
uint32(ip[3])
}
func (w *wifinina) Connect(sockfd int, host string, ip net.IP, port int) error {
if debugging(debugNetdev) {
if host == "" {
fmt.Printf("[Connect] sockfd: %d, addr: %s:%d\r\n", sockfd, ip, port)
} else {
fmt.Printf("[Connect] sockfd: %d, host: %s:%d\r\n", sockfd, host, port)
}
}
w.mu.Lock()
defer w.mu.Unlock()
var sock = sock(sockfd)
var socket = w.sockets[sock]
// Start the connection
switch socket.protocol {
case netdev.IPPROTO_TCP:
w.startClient(sock, "", toUint32(ip), uint16(port), protoModeTCP)
case netdev.IPPROTO_TLS:
w.startClient(sock, host, 0, uint16(port), protoModeTLS)
case netdev.IPPROTO_UDP:
w.startClient(sock, "", toUint32(ip), uint16(port), protoModeUDP)
return nil
}
if w.getClientState(sock) == tcpStateEstablished {
return nil
}
if host == "" {
return fmt.Errorf("Connect to %s:%d failed", ip, port)
} else {
return fmt.Errorf("Connect to %s:%d failed", host, port)
}
}
func (w *wifinina) Listen(sockfd int, backlog int) error {
if debugging(debugNetdev) {
fmt.Printf("[Listen] sockfd: %d\r\n", sockfd)
}
w.mu.Lock()
defer w.mu.Unlock()
var sock = sock(sockfd)
var socket = w.sockets[sock]
switch socket.protocol {
case netdev.IPPROTO_TCP:
w.startServer(sock, uint16(socket.port), protoModeTCP)
case netdev.IPPROTO_UDP:
default:
return netdev.ErrProtocolNotSupported
}
return nil
}
func (w *wifinina) Accept(sockfd int, ip net.IP, port int) (int, error) {
if debugging(debugNetdev) {
fmt.Printf("[Accept] sockfd: %d, peer: %s:%d\r\n", sockfd, ip, port)
}
w.mu.Lock()
defer w.mu.Unlock()
var client sock
var sock = sock(sockfd)
var socket = w.sockets[sock]
switch socket.protocol {
case netdev.IPPROTO_TCP:
default:
return -1, netdev.ErrProtocolNotSupported
}
for {
// Accept() will be sleeping most of the time, checking for a
// new clients every 1/10 sec.
w.mu.Unlock()
time.Sleep(100 * time.Millisecond)
w.mu.Lock()
// Check if we've faulted
if w.fault != nil {
return -1, w.fault
}
// TODO: BUG: Currently, a sock that is 100% busy will always be
// TODO: returned by w.accept(sock), starving other socks
// TODO: from begin serviced. Need to figure out how to
// TODO: service socks fairly (round-robin?) so no one sock
// TODO: can dominate.
// Check if a client has data
client = w.accept(sock)
if client == noSocketAvail {
// None ready
continue
}
// If we've already seen this socket, we can reuse
// the socket and return it. But, only if the socket
// is closed. If it's not closed, we'll just come back
// later to reuse it.
clientSocket, ok := w.sockets[client]
if ok {
// Wait for client to Close
if clientSocket.inuse {
continue
}
// Reuse client socket
return int(client), nil
}
// Create new socket for client and return fd
w.sockets[client] = newSocket(socket.protocol)
return int(client), nil
}
}
func (w *wifinina) sockDown(sock sock) bool {
var socket = w.sockets[sock]
if socket.protocol == netdev.IPPROTO_UDP {
return false
}
return w.getClientState(sock) != tcpStateEstablished
}
func (w *wifinina) sendTCP(sock sock, buf []byte, deadline time.Time) (int, error) {
var timeoutDataSent = 25
// Send it
n := int(w.sendData(sock, buf))
if n == 0 {
return -1, io.EOF
}
// Check if data was sent
for i := 0; i < timeoutDataSent; i++ {
sent := w.checkDataSent(sock)
if sent {
return n, nil
}
// Check if we've timed out
if !deadline.IsZero() {
if time.Now().After(deadline) {
return -1, netdev.ErrTimeout
}
}
// Check if socket went down
if w.sockDown(sock) {
return -1, io.EOF
}
// Check if we've faulted
if w.fault != nil {
return -1, w.fault
}
// Unlock while we sleep, so others can make progress
w.mu.Unlock()
time.Sleep(100 * time.Millisecond)
w.mu.Lock()
}
return -1, netdev.ErrTimeout
}
func (w *wifinina) sendUDP(sock sock, buf []byte, deadline time.Time) (int, error) {
// Queue it
ok := w.insertDataBuf(sock, buf)
if !ok {
return -1, fmt.Errorf("Insert UDP data failed, len(buf)=%d", len(buf))
}
// Send it
ok = w.sendUDPData(sock)
if !ok {
return -1, fmt.Errorf("Send UDP data failed, len(buf)=%d", len(buf))
}
return len(buf), nil
}
func (w *wifinina) sendChunk(sockfd int, buf []byte, deadline time.Time) (int, error) {
var sock = sock(sockfd)
var socket = w.sockets[sock]
// Check if we've timed out
if !deadline.IsZero() {
if time.Now().After(deadline) {
return -1, netdev.ErrTimeout
}
}
switch socket.protocol {
case netdev.IPPROTO_TCP, netdev.IPPROTO_TLS:
return w.sendTCP(sock, buf, deadline)
case netdev.IPPROTO_UDP:
return w.sendUDP(sock, buf, deadline)
}
return -1, netdev.ErrProtocolNotSupported
}
func (w *wifinina) Send(sockfd int, buf []byte, flags int,
deadline time.Time) (int, error) {
if debugging(debugNetdev) {
fmt.Printf("[Send] sockfd: %d, len(buf): %d, flags: %d\r\n",
sockfd, len(buf), flags)
}
w.mu.Lock()
defer w.mu.Unlock()
// Break large bufs into chunks so we don't overrun the hw queue
chunkSize := 1436
for i := 0; i < len(buf); i += chunkSize {
end := i + chunkSize
if end > len(buf) {
end = len(buf)
}
_, err := w.sendChunk(sockfd, buf[i:end], deadline)
if err != nil {
return -1, err
}
}
return len(buf), nil
}
func (w *wifinina) Recv(sockfd int, buf []byte, flags int,
deadline time.Time) (int, error) {
if debugging(debugNetdev) {
fmt.Printf("[Recv] sockfd: %d, len(buf): %d, flags: %d\r\n",
sockfd, len(buf), flags)
}
w.mu.Lock()
defer w.mu.Unlock()
var sock = sock(sockfd)
// Limit max read size to chunk large read requests
var max = len(buf)
if max > 1436 {
max = 1436
}
for {
// Check if we've timed out
if !deadline.IsZero() {
if time.Now().After(deadline) {
return -1, netdev.ErrTimeout
}
}
// Receive into buf, if any data available. It's ok if no data
// is available, we'll just sleep a bit and recheck. Recv()
// doesn't return unless there is data, even a single byte, or
// on error such as timeout or EOF.
n := int(w.getDataBuf(sock, buf[:max]))
if n > 0 {
if debugging(debugNetdev) {
fmt.Printf("[<--Recv] sockfd: %d, n: %d\r\n",
sock, n)
}
return n, nil
}
// Check if socket went down
if w.sockDown(sock) {
// Get any last bytes
n = int(w.getDataBuf(sock, buf[:max]))
if debugging(debugNetdev) {
fmt.Printf("[<--Recv] sockfd: %d, n: %d, EOF\r\n",
sock, n)
}
if n > 0 {
return n, io.EOF
}
return -1, io.EOF
}
// Check if we've faulted
if w.fault != nil {
return -1, w.fault
}
// Unlock while we sleep, so others can make progress
w.mu.Unlock()
time.Sleep(100 * time.Millisecond)
w.mu.Lock()
}
}
func (w *wifinina) Close(sockfd int) error {
if debugging(debugNetdev) {
fmt.Printf("[Close] sockfd: %d\r\n", sockfd)
}
w.mu.Lock()
defer w.mu.Unlock()
var sock = sock(sockfd)
var socket = w.sockets[sock]
if !socket.inuse {
return nil
}
w.stopClient(sock)
if socket.protocol == netdev.IPPROTO_UDP {
socket.inuse = false
return nil
}
start := time.Now()
for time.Since(start) < 5*time.Second {
if w.getClientState(sock) == tcpStateClosed {
socket.inuse = false
return nil
}
w.mu.Unlock()
time.Sleep(100 * time.Millisecond)
w.mu.Lock()
}
return netdev.ErrClosingSocket
}
func (w *wifinina) SetSockOpt(sockfd int, level int, opt int, value interface{}) error {
if debugging(debugNetdev) {
fmt.Printf("[SetSockOpt] sockfd: %d\r\n", sockfd)
}
return netdev.ErrNotSupported
}
func (w *wifinina) startClient(sock sock, hostname string, addr uint32, port uint16, mode uint8) {
if debugging(debugCmd) {
fmt.Printf(" [cmdStartClientTCP] sock: %d, hostname: \"%s\", addr: % 02X, port: %d, mode: %d\r\n",
sock, hostname, addr, port, mode)
}
w.waitForChipReady()
w.spiChipSelect()
if len(hostname) > 0 {
w.sendCmd(cmdStartClientTCP, 5)
w.sendParamStr(hostname, false)
} else {
w.sendCmd(cmdStartClientTCP, 4)
}
w.sendParam32(addr, false)
w.sendParam16(port, false)
w.sendParam8(uint8(sock), false)
w.sendParam8(mode, true)
if len(hostname) > 0 {
w.padTo4(17 + len(hostname))
}
w.spiChipDeselect()
w.waitRspCmd1(cmdStartClientTCP)
}
func (w *wifinina) getSocket() sock {
if debugging(debugCmd) {
fmt.Printf(" [cmdGetSocket]\r\n")
}
return sock(w.getUint8(w.req0(cmdGetSocket)))
}
func (w *wifinina) getClientState(sock sock) uint8 {
if debugging(debugCmd) {
fmt.Printf(" [cmdGetClientStateTCP] sock: %d\r\n", sock)
}
return w.getUint8(w.reqUint8(cmdGetClientStateTCP, uint8(sock)))
}
func (w *wifinina) sendData(sock sock, buf []byte) uint16 {
if debugging(debugCmd) {
fmt.Printf(" [cmdSendDataTCP] sock: %d, len(buf): %d\r\n",
sock, len(buf))
}
w.waitForChipReady()
w.spiChipSelect()
l := w.sendCmd(cmdSendDataTCP, 2)
l += w.sendParamBuf([]byte{uint8(sock)}, false)
l += w.sendParamBuf(buf, true)
w.addPadding(l)
w.spiChipDeselect()
sent := w.getUint16(w.waitRspCmd1(cmdSendDataTCP))
return bits.RotateLeft16(sent, 8)
}
func (w *wifinina) checkDataSent(sock sock) bool {
if debugging(debugCmd) {
fmt.Printf(" [cmdDataSentTCP] sock: %d\r\n", sock)
}
sent := w.getUint8(w.reqUint8(cmdDataSentTCP, uint8(sock)))
return sent > 0
}
func (w *wifinina) getDataBuf(sock sock, buf []byte) uint16 {
if debugging(debugCmd) {
fmt.Printf(" [cmdGetDatabufTCP] sock: %d, len(buf): %d\r\n",
sock, len(buf))
}
w.waitForChipReady()
w.spiChipSelect()
p := uint16(len(buf))
l := w.sendCmd(cmdGetDatabufTCP, 2)
l += w.sendParamBuf([]byte{uint8(sock)}, false)
l += w.sendParamBuf([]byte{uint8(p & 0x00FF), uint8((p) >> 8)}, true)
w.addPadding(l)
w.spiChipDeselect()
w.waitForChipReady()
w.spiChipSelect()
n := w.waitRspBuf16(cmdGetDatabufTCP, buf)
w.spiChipDeselect()
if n > 0 {
if debugging(debugCmd) {
fmt.Printf(" [<--cmdGetDatabufTCP] sock: %d, got n: %d\r\n",
sock, n)
}
}
return n
}
func (w *wifinina) stopClient(sock sock) {
if debugging(debugCmd) {
fmt.Printf(" [cmdStopClientTCP] sock: %d\r\n", sock)
}
w.getUint8(w.reqUint8(cmdStopClientTCP, uint8(sock)))
}
func (w *wifinina) startServer(sock sock, port uint16, mode uint8) {
if debugging(debugCmd) {
fmt.Printf(" [cmdStartServerTCP] sock: %d, port: %d, mode: %d\r\n",
sock, port, mode)
}
w.waitForChipReady()
w.spiChipSelect()
l := w.sendCmd(cmdStartServerTCP, 3)
l += w.sendParam16(port, false)
l += w.sendParam8(uint8(sock), false)
l += w.sendParam8(mode, true)
w.addPadding(l)
w.spiChipDeselect()
w.waitRspCmd1(cmdStartServerTCP)
}
func (w *wifinina) accept(s sock) sock {
if debugging(debugCmd) {
fmt.Printf(" [cmdAvailDataTCP] sock: %d\r\n", s)
}
w.waitForChipReady()
w.spiChipSelect()
l := w.sendCmd(cmdAvailDataTCP, 1)
l += w.sendParam8(uint8(s), true)
w.addPadding(l)
w.spiChipDeselect()
newsock16 := w.getUint16(w.waitRspCmd1(cmdAvailDataTCP))
newsock := sock(uint8(bits.RotateLeft16(newsock16, 8)))
if newsock != noSocketAvail {
if debugging(debugCmd) {
fmt.Printf(" [cmdAvailDataTCP-->] sock: %d, got sock: %d\r\n",
s, newsock)
}
}
return newsock
}
// insertDataBuf adds data to the buffer used for sending UDP data
func (w *wifinina) insertDataBuf(sock sock, buf []byte) bool {
if debugging(debugCmd) {
fmt.Printf(" [cmdInsertDataBuf] sock: %d, len(buf): %d\r\n",
sock, len(buf))
}
w.waitForChipReady()
w.spiChipSelect()
l := w.sendCmd(cmdInsertDataBuf, 2)
l += w.sendParamBuf([]byte{uint8(sock)}, false)
l += w.sendParamBuf(buf, true)
w.addPadding(l)
w.spiChipDeselect()
n := w.getUint8(w.waitRspCmd1(cmdInsertDataBuf))
return n == 1
}
// sendUDPData sends the data previously added to the UDP buffer
func (w *wifinina) sendUDPData(sock sock) bool {
if debugging(debugCmd) {
fmt.Printf(" [cmdSendDataUDP] sock: %d\r\n", sock)
}
w.waitForChipReady()
w.spiChipSelect()
l := w.sendCmd(cmdSendDataUDP, 1)
l += w.sendParam8(uint8(sock), true)
w.addPadding(l)
w.spiChipDeselect()
n := w.getUint8(w.waitRspCmd1(cmdSendDataUDP))
return n == 1
}
func (w *wifinina) disconnect() {
if debugging(debugCmd) {
fmt.Printf(" [cmdDisconnect]\r\n")
}
w.req1(cmdDisconnect)
}
func (w *wifinina) getFwVersion() string {
if debugging(debugCmd) {
fmt.Printf(" [cmdGetFwVersion]\r\n")
}
return w.getString(w.req0(cmdGetFwVersion))
}
func (w *wifinina) getConnectionStatus() connectionStatus {
if debugging(debugCmd) {
fmt.Printf(" [cmdGetConnStatus]\r\n")
}
status := w.getUint8(w.req0(cmdGetConnStatus))
return connectionStatus(status)
}
func (w *wifinina) getCurrentencryptionType() encryptionType {
enctype := w.getUint8(w.req1(cmdGetCurrEncrType))
return encryptionType(enctype)
}
func (w *wifinina) getCurrentBSSID() net.HardwareAddr {
return w.getMACAddress(w.req1(cmdGetCurrBSSID))
}
func (w *wifinina) getCurrentRSSI() int32 {
return w.getInt32(w.req1(cmdGetCurrRSSI))
}
func (w *wifinina) getCurrentSSID() string {
return w.getString(w.req1(cmdGetCurrSSID))
}
func (w *wifinina) getMACAddr() net.HardwareAddr {
if debugging(debugCmd) {
fmt.Printf(" [cmdGetMACAddr]\r\n")
}
return w.getMACAddress(w.req1(cmdGetMACAddr))
}
func (w *wifinina) faultf(f string, args ...any) {
// Only record the first fault
if w.fault == nil {
w.fault = fmt.Errorf(f, args...)
}
}
func (w *wifinina) getIP() (ip, subnet, gateway net.IP) {
if debugging(debugCmd) {
fmt.Printf(" [cmdGetIPAddr]\r\n")
}
sl := make([]string, 3)
l := w.reqRspStr1(cmdGetIPAddr, dummyData, sl)
if l != 3 {
w.faultf("getIP wanted l=3, got l=%d", l)
return
}
ip, subnet, gateway = make([]byte, 4), make([]byte, 4), make([]byte, 4)
copy(ip[:], sl[0])
copy(subnet[:], sl[1])
copy(gateway[:], sl[2])
return
}
func (w *wifinina) getHostByName(name string) string {
if debugging(debugCmd) {
fmt.Printf(" [cmdGetHostByName]\r\n")
}
ok := w.getUint8(w.reqStr(cmdReqHostByName, name))
if ok != 1 {
return ""
}
return w.getString(w.req0(cmdGetHostByName))
}
func (w *wifinina) getNetworkBSSID(idx int) net.HardwareAddr {
if idx < 0 || idx >= maxNetworks {
return net.HardwareAddr{}
}
return w.getMACAddress(w.reqUint8(cmdGetIdxBSSID, uint8(idx)))
}
func (w *wifinina) getNetworkChannel(idx int) uint8 {
if idx < 0 || idx >= maxNetworks {
return 0
}
return w.getUint8(w.reqUint8(cmdGetIdxChannel, uint8(idx)))
}
func (w *wifinina) getNetworkEncrType(idx int) encryptionType {
if idx < 0 || idx >= maxNetworks {
return 0
}
enctype := w.getUint8(w.reqUint8(cmdGetIdxEncrType, uint8(idx)))
return encryptionType(enctype)
}
func (w *wifinina) getNetworkRSSI(idx int) int32 {
if idx < 0 || idx >= maxNetworks {
return 0
}
return w.getInt32(w.reqUint8(cmdGetIdxRSSI, uint8(idx)))
}
func (w *wifinina) getNetworkSSID(idx int) string {
if idx < 0 || idx >= maxNetworks {
return ""
}
return w.ssids[idx]
}
func (w *wifinina) getReasonCode() uint8 {
return w.getUint8(w.req0(cmdGetReasonCode))
}
// getTime is the time as a Unix timestamp
func (w *wifinina) getTime() uint32 {
return w.getUint32(w.req0(cmdGetTime))
}
func (w *wifinina) getTemperature() float32 {
return w.getFloat32(w.req0(cmdGetTemperature))
}
func (w *wifinina) setDebug(on bool) {
var v uint8
if on {
v = 1
}
w.reqUint8(cmdSetDebug, v)
}
func (w *wifinina) setNetwork(ssid string) {
w.reqStr(cmdSetNet, ssid)
}
func (w *wifinina) setPassphrase(ssid string, passphrase string) {
if debugging(debugCmd) {
fmt.Printf(" [cmdSetPassphrase] ssid: %s, passphrase: ******\r\n",
ssid)
}
// Dont' show passphrase in debug output
saveDebug := _debug
_debug = _debug & ^debugDetail
w.reqStr2(cmdSetPassphrase, ssid, passphrase)
_debug = saveDebug
}
func (w *wifinina) setKey(ssid string, index uint8, key string) {
w.waitForChipReady()
w.spiChipSelect()
w.sendCmd(cmdSetKey, 3)
w.sendParamStr(ssid, false)
w.sendParam8(index, false)
w.sendParamStr(key, true)
w.padTo4(8 + len(ssid) + len(key))
w.spiChipDeselect()
w.waitRspCmd1(cmdSetKey)
}
func (w *wifinina) setNetworkForAP(ssid string) {
w.reqStr(cmdSetAPNet, ssid)
}
func (w *wifinina) setPassphraseForAP(ssid string, passphrase string) {
w.reqStr2(cmdSetAPPassphrase, ssid, passphrase)
}
func (w *wifinina) setDNS(which uint8, dns1 uint32, dns2 uint32) {
w.waitForChipReady()
w.spiChipSelect()
w.sendCmd(cmdSetDNSConfig, 3)
w.sendParam8(which, false)
w.sendParam32(dns1, false)
w.sendParam32(dns2, true)
//pad??
w.spiChipDeselect()
w.waitRspCmd1(cmdSetDNSConfig)
}
func (w *wifinina) setHostname(hostname string) {
w.waitForChipReady()
w.spiChipSelect()
w.sendCmd(cmdSetHostname, 3)
w.sendParamStr(hostname, true)
w.padTo4(5 + len(hostname))
w.spiChipDeselect()
w.waitRspCmd1(cmdSetHostname)
}
func (w *wifinina) setPowerMode(mode uint8) {
w.reqUint8(cmdSetPowerMode, mode)
}
func (w *wifinina) scanNetworks() uint8 {
return w.reqRspStr0(cmdScanNetworks, w.ssids[:])
}
func (w *wifinina) startScanNetworks() uint8 {
return w.getUint8(w.req0(cmdStartScanNetworks))
}
func (w *wifinina) PinMode(pin uint8, mode uint8) {
if debugging(debugCmd) {
fmt.Printf(" [cmdSetPinMode] pin: %d, mode: %d\r\n", pin, mode)
}
w.req2Uint8(cmdSetPinMode, pin, mode)
}
func (w *wifinina) DigitalWrite(pin uint8, value uint8) {
if debugging(debugCmd) {
fmt.Printf(" [cmdSetDigitialWrite] pin: %d, value: %d\r\n", pin, value)
}
w.req2Uint8(cmdSetDigitalWrite, pin, value)
}
func (w *wifinina) AnalogWrite(pin uint8, value uint8) {
w.req2Uint8(cmdSetAnalogWrite, pin, value)
}
func (w *wifinina) getString(l uint8) string {
return string(w.buf[0:l])
}
func (w *wifinina) getUint8(l uint8) uint8 {
if l == 1 {
return w.buf[0]
}
w.faultf("expected length 1, was actually %d", l)
return 0
}
func (w *wifinina) getUint16(l uint8) uint16 {
if l == 2 {
return binary.BigEndian.Uint16(w.buf[0:2])
}
w.faultf("expected length 2, was actually %d", l)
return 0
}
func (w *wifinina) getUint32(l uint8) uint32 {
if l == 4 {
return binary.BigEndian.Uint32(w.buf[0:4])
}
w.faultf("expected length 4, was actually %d", l)
return 0
}
func (w *wifinina) getInt32(l uint8) int32 {
return int32(w.getUint32(l))
}
func (w *wifinina) getFloat32(l uint8) float32 {
return float32(w.getUint32(l))
}
func (w *wifinina) getMACAddress(l uint8) net.HardwareAddr {
if l == 6 {
mac := w.buf[0:6]
// Reverse the bytes
for i, j := 0, len(mac)-1; i < j; i, j = i+1, j-1 {
mac[i], mac[j] = mac[j], mac[i]
}
return mac
}
w.faultf("expected length 6, was actually %d", l)
return net.HardwareAddr{}
}
func (w *wifinina) transfer(b byte) byte {
v, err := w.spi.Transfer(b)
if err != nil {
w.faultf("SPI.Transfer")
return 0
}
return v
}
// Cmd Struct Message */
// ._______________________________________________________________________.
// | START CMD | C/R | CMD | N.PARAM | PARAM LEN | PARAM | .. | END CMD |
// |___________|______|______|_________|___________|________|____|_________|
// | 8 bit | 1bit | 7bit | 8bit | 8bit | nbytes | .. | 8bit |
// |___________|______|______|_________|___________|________|____|_________|
// req0 sends a command to the device with no request parameters
func (w *wifinina) req0(cmd uint8) uint8 {
w.sendCmd0(cmd)
return w.waitRspCmd1(cmd)
}
// req1 sends a command to the device with a single dummy parameters of 0xFF
func (w *wifinina) req1(cmd uint8) uint8 {
return w.reqUint8(cmd, dummyData)
}
// reqUint8 sends a command to the device with a single uint8 parameter
func (w *wifinina) reqUint8(cmd uint8, data uint8) uint8 {
w.sendCmdPadded1(cmd, data)
return w.waitRspCmd1(cmd)
}
// req2Uint8 sends a command to the device with two uint8 parameters
func (w *wifinina) req2Uint8(cmd, p1, p2 uint8) uint8 {
w.sendCmdPadded2(cmd, p1, p2)
return w.waitRspCmd1(cmd)
}
// reqStr sends a command to the device with a single string parameter
func (w *wifinina) reqStr(cmd uint8, p1 string) uint8 {
w.sendCmdStr(cmd, p1)
return w.waitRspCmd1(cmd)
}
// reqStr2 sends a command to the device with 2 string parameters
func (w *wifinina) reqStr2(cmd uint8, p1 string, p2 string) {
w.sendCmdStr2(cmd, p1, p2)
w.waitRspCmd1(cmd)
}
// reqStrRsp0 sends a command passing a string slice for the response
func (w *wifinina) reqRspStr0(cmd uint8, sl []string) (l uint8) {
w.sendCmd0(cmd)
w.waitForChipReady()
w.spiChipSelect()
l = w.waitRspStr(cmd, sl)
w.spiChipDeselect()
return
}
// reqStrRsp1 sends a command with a uint8 param and a string slice for the response
func (w *wifinina) reqRspStr1(cmd uint8, data uint8, sl []string) uint8 {
w.sendCmdPadded1(cmd, data)
w.waitForChipReady()
w.spiChipSelect()
l := w.waitRspStr(cmd, sl)
w.spiChipDeselect()
return l
}
func (w *wifinina) sendCmd0(cmd uint8) {
w.waitForChipReady()
w.spiChipSelect()
w.sendCmd(cmd, 0)
w.spiChipDeselect()
}
func (w *wifinina) sendCmdPadded1(cmd uint8, data uint8) {
w.waitForChipReady()
w.spiChipSelect()
w.sendCmd(cmd, 1)
w.sendParam8(data, true)
w.transfer(dummyData)
w.transfer(dummyData)
w.spiChipDeselect()
return
}
func (w *wifinina) sendCmdPadded2(cmd, data1, data2 uint8) {
w.waitForChipReady()
w.spiChipSelect()
w.sendCmd(cmd, 1)
w.sendParam8(data1, false)
w.sendParam8(data2, true)
w.transfer(dummyData)
w.spiChipDeselect()
}
func (w *wifinina) sendCmdStr(cmd uint8, p1 string) {
w.waitForChipReady()
w.spiChipSelect()
w.sendCmd(cmd, 1)
w.sendParamStr(p1, true)
w.padTo4(5 + len(p1))
w.spiChipDeselect()
}
func (w *wifinina) sendCmdStr2(cmd uint8, p1 string, p2 string) {
w.waitForChipReady()
w.spiChipSelect()
w.sendCmd(cmd, 2)
w.sendParamStr(p1, false)
w.sendParamStr(p2, true)
w.padTo4(6 + len(p1) + len(p2))
w.spiChipDeselect()
}
func (w *wifinina) waitRspCmd1(cmd uint8) uint8 {
w.waitForChipReady()
w.spiChipSelect()
l := w.waitRspCmd(cmd, 1)
w.spiChipDeselect()
return l
}
func (w *wifinina) sendCmd(cmd uint8, numParam uint8) (l int) {
if debugging(debugDetail) {
fmt.Printf(" sendCmd: %02X %02X %02X",
cmdStart, cmd & ^(uint8(flagReply)), numParam)
}
l = 3
w.transfer(cmdStart)
w.transfer(cmd & ^(uint8(flagReply)))
w.transfer(numParam)
if numParam == 0 {
w.transfer(cmdEnd)
l += 1
if debugging(debugDetail) {
fmt.Printf(" %02X", cmdEnd)
}
}
if debugging(debugDetail) {
fmt.Printf(" (%d)\r\n", l)
}
return
}
func (w *wifinina) sendParamLen16(p uint16) (l int) {
w.transfer(uint8(p >> 8))
w.transfer(uint8(p & 0xFF))
if debugging(debugDetail) {
fmt.Printf(" %02X %02X", uint8(p>>8), uint8(p&0xFF))
}
return 2
}
func (w *wifinina) sendParamBuf(p []byte, isLastParam bool) (l int) {
if debugging(debugDetail) {
fmt.Printf(" sendParamBuf:")
}
l += w.sendParamLen16(uint16(len(p)))
for _, b := range p {
if debugging(debugDetail) {
fmt.Printf(" %02X", b)
}
w.transfer(b)
l += 1
}
if isLastParam {
if debugging(debugDetail) {
fmt.Printf(" %02X", cmdEnd)
}
w.transfer(cmdEnd)
l += 1
}
if debugging(debugDetail) {
fmt.Printf(" (%d) \r\n", l)
}
return
}
func (w *wifinina) sendParamStr(p string, isLastParam bool) (l int) {
if debugging(debugDetail) {
fmt.Printf(" sendParamStr: p: %s, lastParam: %t\r\n", p, isLastParam)
}
l = len(p)
w.transfer(uint8(l))
if l > 0 {
w.spi.Tx([]byte(p), nil)
}
if isLastParam {
w.transfer(cmdEnd)
l += 1
}
return
}
func (w *wifinina) sendParam8(p uint8, isLastParam bool) (l int) {
if debugging(debugDetail) {
fmt.Printf(" sendParam8: p: %d, lastParam: %t\r\n", p, isLastParam)
}
l = 2
w.transfer(1)
w.transfer(p)
if isLastParam {
w.transfer(cmdEnd)
l += 1
}
return
}
func (w *wifinina) sendParam16(p uint16, isLastParam bool) (l int) {
if debugging(debugDetail) {
fmt.Printf(" sendParam16: p: %d, lastParam: %t\r\n", p, isLastParam)
}
l = 3
w.transfer(2)
w.transfer(uint8(p >> 8))
w.transfer(uint8(p & 0xFF))
if isLastParam {
w.transfer(cmdEnd)
l += 1
}
return
}
func (w *wifinina) sendParam32(p uint32, isLastParam bool) (l int) {
if debugging(debugDetail) {
fmt.Printf(" sendParam32: p: %d, lastParam: %t\r\n", p, isLastParam)
}
l = 5
w.transfer(4)
w.transfer(uint8(p >> 24))
w.transfer(uint8(p >> 16))
w.transfer(uint8(p >> 8))
w.transfer(uint8(p & 0xFF))
if isLastParam {
w.transfer(cmdEnd)
l += 1
}
return
}
func (w *wifinina) waitForChipReady() {
if debugging(debugDetail) {
fmt.Printf(" waitForChipReady\r\n")
}
for i := 0; w.ack.Get(); i++ {
time.Sleep(1 * time.Millisecond)
if i == 10000 {
w.faultf("hung in waitForChipReady")
return
}
}
}
func (w *wifinina) spiChipSelect() {
if debugging(debugDetail) {
fmt.Printf(" spiChipSelect\r\n")
}
w.cs.Low()
start := time.Now()
for time.Since(start) < 10*time.Millisecond {
if w.ack.Get() {
return
}
time.Sleep(100 * time.Microsecond)
}
w.faultf("hung in spiChipSelect")
}
func (w *wifinina) spiChipDeselect() {
if debugging(debugDetail) {
fmt.Printf(" spiChipDeselect\r\n")
}
w.cs.High()
}
func (w *wifinina) waitSpiChar(desired byte) {
if debugging(debugDetail) {
fmt.Printf(" waitSpiChar: desired: %02X\r\n", desired)
}
var read byte
for i := 0; i < 10; i++ {
w.readParam(&read)
switch read {
case cmdErr:
w.faultf("cmdErr received, waiting for %d", desired)
return
case desired:
return
}
time.Sleep(10 * time.Millisecond)
}
w.faultf("timeout waiting for SPI char %02X\r\n", desired)
}
func (w *wifinina) waitRspCmd(cmd uint8, np uint8) (l uint8) {
if debugging(debugDetail) {
fmt.Printf(" waitRspCmd: cmd: %02X, np: %d\r\n", cmd, np)
}
var data byte
w.waitSpiChar(cmdStart)
if !w.readAndCheckByte(cmd|flagReply, &data) {
w.faultf("expected cmd %02X, read %02X", cmd, data)
return
}
if w.readAndCheckByte(np, &data) {
w.readParam(&l)
for i := uint8(0); i < l; i++ {
w.readParam(&w.buf[i])
}
if !w.readAndCheckByte(cmdEnd, &data) {
w.faultf("expected cmdEnd, read %02X", data)
}
}
return
}
func (w *wifinina) waitRspBuf16(cmd uint8, buf []byte) (l uint16) {
if debugging(debugDetail) {
fmt.Printf(" waitRspBuf16: cmd: %02X, len(buf): %d\r\n", cmd, len(buf))
}
var data byte
w.waitSpiChar(cmdStart)
if !w.readAndCheckByte(cmd|flagReply, &data) {
w.faultf("expected cmd %02X, read %02X", cmd, data)
return
}
if w.readAndCheckByte(1, &data) {
l = w.readParamLen16()
for i := uint16(0); i < l; i++ {
w.readParam(&buf[i])
}
if !w.readAndCheckByte(cmdEnd, &data) {
w.faultf("expected cmdEnd, read %02X", data)
}
}
return
}
func (w *wifinina) waitRspStr(cmd uint8, sl []string) (numRead uint8) {
if debugging(debugDetail) {
fmt.Printf(" waitRspStr: cmd: %02X, len(sl): %d\r\n", cmd, len(sl))
}
var data byte
w.waitSpiChar(cmdStart)
if !w.readAndCheckByte(cmd|flagReply, &data) {
w.faultf("expected cmd %02X, read %02X", cmd, data)
return
}
numRead = w.transfer(dummyData)
if numRead == 0 {
w.faultf("waitRspStr numRead == 0")
return
}
maxNumRead := uint8(len(sl))
for j, l := uint8(0), uint8(0); j < numRead; j++ {
w.readParam(&l)
for i := uint8(0); i < l; i++ {
w.readParam(&w.buf[i])
}
if j < maxNumRead {
sl[j] = string(w.buf[0:l])
if debugging(debugDetail) {
fmt.Printf(" str: %d (%d) - %08X\r\n", j, l, []byte(sl[j]))
}
}
}
for j := numRead; j < maxNumRead; j++ {
if debugging(debugDetail) {
fmt.Printf(" str: ", j, "\"\"\r")
}
sl[j] = ""
}
if !w.readAndCheckByte(cmdEnd, &data) {
w.faultf("expected cmdEnd, read %02X", data)
return
}
if numRead > maxNumRead {
numRead = maxNumRead
}
return
}
func (w *wifinina) readAndCheckByte(check byte, read *byte) bool {
w.readParam(read)
return *read == check
}
// readParamLen16 reads 2 bytes from the SPI bus (MSB first), returning uint16
func (w *wifinina) readParamLen16() (v uint16) {
b := w.transfer(0xFF)
v = uint16(b) << 8
b = w.transfer(0xFF)
v |= uint16(b)
return
}
func (w *wifinina) readParam(b *byte) {
*b = w.transfer(0xFF)
}
func (w *wifinina) addPadding(l int) {
if debugging(debugDetail) {
fmt.Printf(" addPadding: l: %d\r\n", l)
}
for i := (4 - (l % 4)) & 3; i > 0; i-- {
if debugging(debugDetail) {
fmt.Printf(" padding\r\n")
}
w.transfer(dummyData)
}
}
func (w *wifinina) padTo4(l int) {
if debugging(debugDetail) {
fmt.Printf(" padTo4: l: %d\r\n", l)
}
for l%4 != 0 {
if debugging(debugDetail) {
fmt.Printf(" padding\r\n")
}
w.transfer(dummyData)
l++
}
}