Files
drivers/wifinina/wifinina.go
T

1095 lines
25 KiB
Go

// Package wifinina implements TCP wireless communication over SPI
// with an attached separate ESP32 board 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
//
package wifinina // import "tinygo.org/x/drivers/wifinina"
import (
"encoding/binary"
"fmt"
"time"
"machine"
"tinygo.org/x/drivers"
"tinygo.org/x/drivers/net"
)
const _debug = false
const (
MaxSockets = 4
MaxNetworks = 10
MaxAttempts = 10
MaxLengthSSID = 32
MaxLengthWPAKey = 63
MaxLengthWEPKey = 13
LengthMacAddress = 6
LengthIPV4 = 4
WlFailure = -1
WlSuccess = 1
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
/*
// Default state value for Wifi state field
#define NA_STATE -1
*/
FlagCmd = 0
FlagReply = 1 << 7
FlagData = 0x40
NinaCmdPos = 1
NinaParamLenPos = 2
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
// TEST_CMD = 0x13
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
// GET_IDX_SSID_CMD = 0x31,
// GET_TEST_CMD = 0x38
// All command with DATA_FLAG 0x40 send a 16bit Len
CmdSendDataTCP = 0x44
CmdGetDatabufTCP = 0x45
CmdInsertDataBuf = 0x46
// regular format commands
CmdSetPinMode = 0x50
CmdSetDigitalWrite = 0x51
CmdSetAnalogWrite = 0x52
ErrTimeoutChipReady Error = 0x01
ErrTimeoutChipSelect Error = 0x02
ErrCheckStartCmd Error = 0x03
ErrWaitRsp Error = 0x04
ErrUnexpectedLength Error = 0xE0
ErrNoParamsReturned Error = 0xE1
ErrIncorrectSentinel Error = 0xE2
ErrCmdErrorReceived Error = 0xEF
ErrNotImplemented Error = 0xF0
ErrUnknownHost Error = 0xF1
ErrSocketAlreadySet Error = 0xF2
ErrConnectionTimeout Error = 0xF3
ErrNoData Error = 0xF4
ErrDataNotWritten Error = 0xF5
ErrCheckDataError Error = 0xF6
ErrBufferTooSmall Error = 0xF7
ErrNoSocketAvail Error = 0xFF
NoSocketAvail uint8 = 0xFF
)
const (
ProtoModeTCP = iota
ProtoModeUDP
ProtoModeTLS
ProtoModeMul
)
type ConnectionStatus uint8
func (c ConnectionStatus) String() string {
switch c {
case StatusIdle:
return "Idle"
case StatusNoSSIDAvail:
return "No SSID Available"
case StatusScanCompleted:
return "Scan Completed"
case StatusConnected:
return "Connected"
case StatusConnectFailed:
return "Connect Failed"
case StatusConnectionLost:
return "Connection Lost"
case StatusDisconnected:
return "Disconnected"
case StatusNoShield:
return "No Shield"
default:
return "Unknown"
}
}
type EncryptionType uint8
func (e EncryptionType) String() string {
switch e {
case EncTypeTKIP:
return "TKIP"
case EncTypeCCMP:
return "WPA2"
case EncTypeWEP:
return "WEP"
case EncTypeNone:
return "None"
case EncTypeAuto:
return "Auto"
default:
return "Unknown"
}
}
type IPAddress string // TODO: does WiFiNINA support ipv6???
func (addr IPAddress) String() string {
if len(addr) < 4 {
return ""
}
return fmt.Sprintf("%d.%d.%d.%d", addr[0], addr[1], addr[2], addr[3])
}
func ParseIPv4(s string) (IPAddress, error) {
var v0, v1, v2, v3 uint8
if _, err := fmt.Sscanf(s, "%d.%d.%d.%d", &v0, &v1, &v2, &v3); err != nil {
return "", err
}
return IPAddress([]byte{v0, v1, v2, v3}), nil
}
func (addr IPAddress) AsUint32() uint32 {
if len(addr) < 4 {
return 0
}
b := []byte(string(addr))
return binary.BigEndian.Uint32(b[0:4])
}
type MACAddress uint64
func (addr MACAddress) String() string {
return fmt.Sprintf("%016X", uint64(addr))
}
type Error uint8
func (err Error) Error() string {
return fmt.Sprintf("wifinina error: 0x%02X", uint8(err))
}
// Cmd Struct Message */
// ._______________________________________________________________________.
// | START CMD | C/R | CMD | N.PARAM | PARAM LEN | PARAM | .. | END CMD |
// |___________|______|______|_________|___________|________|____|_________|
// | 8 bit | 1bit | 7bit | 8bit | 8bit | nbytes | .. | 8bit |
// |___________|______|______|_________|___________|________|____|_________|
//
type command struct {
cmd uint8
reply bool
params []int
paramData []byte
}
type Device struct {
SPI drivers.SPI
CS machine.Pin
ACK machine.Pin
GPIO0 machine.Pin
RESET machine.Pin
buf [64]byte
ssids [10]string
}
// New returns a new Wiifinina driver.
func New(bus drivers.SPI, csPin, ackPin, gpio0Pin, resetPin machine.Pin) *Device {
return &Device{
SPI: bus,
CS: csPin,
ACK: ackPin,
GPIO0: gpio0Pin,
RESET: resetPin,
}
}
func (d *Device) Configure() {
net.UseDriver(d.NewDriver())
d.CS.Configure(machine.PinConfig{machine.PinOutput})
d.ACK.Configure(machine.PinConfig{machine.PinInput})
d.RESET.Configure(machine.PinConfig{machine.PinOutput})
d.GPIO0.Configure(machine.PinConfig{machine.PinOutput})
d.GPIO0.High()
d.CS.High()
d.RESET.Low()
time.Sleep(1 * time.Millisecond)
d.RESET.High()
time.Sleep(1 * time.Millisecond)
d.GPIO0.Low()
d.GPIO0.Configure(machine.PinConfig{machine.PinInput})
}
// ----------- client methods (should this be a separate struct?) ------------
func (d *Device) StartClient(addr uint32, port uint16, sock uint8, mode uint8) error {
if _debug {
println("[StartClient] called StartClient()\r")
fmt.Printf("[StartClient] addr: % 02X, port: %d, sock: %d\r\n", addr, port, sock)
}
if err := d.waitForChipSelect(); err != nil {
d.spiChipDeselect()
return err
}
l := d.sendCmd(CmdStartClientTCP, 4)
l += d.sendParam32(addr, false)
l += d.sendParam16(port, false)
l += d.sendParam8(sock, false)
l += d.sendParam8(mode, true)
d.addPadding(l)
d.spiChipDeselect()
_, err := d.waitRspCmd1(CmdStartClientTCP)
return err
}
func (d *Device) GetSocket() (uint8, error) {
return d.getUint8(d.req0(CmdGetSocket))
}
func (d *Device) GetClientState(sock uint8) (uint8, error) {
return d.getUint8(d.reqUint8(CmdGetClientStateTCP, sock))
}
func (d *Device) SendData(buf []byte, sock uint8) (uint16, error) {
if err := d.waitForChipSelect(); err != nil {
d.spiChipDeselect()
return 0, err
}
l := d.sendCmd(CmdSendDataTCP, 2)
l += d.sendParamBuf([]byte{sock}, false)
l += d.sendParamBuf(buf, true)
d.addPadding(l)
d.spiChipDeselect()
return d.getUint16(d.waitRspCmd1(CmdSendDataTCP))
}
func (d *Device) CheckDataSent(sock uint8) (bool, error) {
var lastErr error
for timeout := 0; timeout < 10; timeout++ {
sent, err := d.getUint8(d.reqUint8(CmdDataSentTCP, sock))
if err != nil {
lastErr = err
}
if sent > 0 {
return true, nil
}
wait(100 * time.Microsecond)
}
return false, lastErr
}
func (d *Device) GetDataBuf(sock uint8, buf []byte) (int, error) {
if err := d.waitForChipSelect(); err != nil {
d.spiChipDeselect()
return 0, err
}
p := uint16(len(buf))
l := d.sendCmd(CmdGetDatabufTCP, 2)
l += d.sendParamBuf([]byte{sock}, false)
l += d.sendParamBuf([]byte{uint8(p & 0x00FF), uint8((p) >> 8)}, true)
d.addPadding(l)
d.spiChipDeselect()
if err := d.waitForChipSelect(); err != nil {
d.spiChipDeselect()
return 0, err
}
n, err := d.waitRspBuf16(CmdGetDatabufTCP, buf)
d.spiChipDeselect()
return int(n), err
}
func (d *Device) StopClient(sock uint8) error {
if _debug {
println("[StopClient] called StopClient()\r")
}
_, err := d.getUint8(d.reqUint8(CmdStopClientTCP, sock))
return err
}
func (d *Device) StartServer(port uint16, sock uint8, mode uint8) error {
if err := d.waitForChipSelect(); err != nil {
d.spiChipDeselect()
return err
}
l := d.sendCmd(CmdStartServerTCP, 3)
l += d.sendParam16(port, false)
l += d.sendParam8(sock, false)
l += d.sendParam8(mode, true)
d.addPadding(l)
d.spiChipDeselect()
_, err := d.waitRspCmd1(CmdStartClientTCP)
return err
}
// InsertDataBuf adds data to the buffer used for sending UDP data
func (d *Device) InsertDataBuf(buf []byte, sock uint8) (bool, error) {
if err := d.waitForChipSelect(); err != nil {
d.spiChipDeselect()
return false, err
}
l := d.sendCmd(CmdInsertDataBuf, 2)
l += d.sendParamBuf([]byte{sock}, false)
l += d.sendParamBuf(buf, true)
d.addPadding(l)
d.spiChipDeselect()
n, err := d.getUint8(d.waitRspCmd1(CmdInsertDataBuf))
return n == 1, err
}
// SendUDPData sends the data previously added to the UDP buffer
func (d *Device) SendUDPData(sock uint8) (bool, error) {
if err := d.waitForChipSelect(); err != nil {
d.spiChipDeselect()
return false, err
}
l := d.sendCmd(CmdSendDataUDP, 1)
l += d.sendParam8(sock, true)
d.addPadding(l)
d.spiChipDeselect()
n, err := d.getUint8(d.waitRspCmd1(CmdSendDataUDP))
return n == 1, err
}
// ---------- /client methods (should this be a separate struct?) ------------
/*
static bool startServer(uint16_t port, uint8_t sock);
static uint8_t getServerState(uint8_t sock);
static bool getData(uint8_t connId, uint8_t *data, bool peek, bool* connClose);
static int getDataBuf(uint8_t connId, uint8_t *buf, uint16_t bufSize);
static bool sendData(uint8_t sock, const uint8_t *data, uint16_t len);
static bool sendDataUdp(uint8_t sock, const char* host, uint16_t port, const uint8_t *data, uint16_t len);
static uint16_t availData(uint8_t connId);
static bool ping(const char *host);
static void reset();
static void getRemoteIpAddress(IPAddress& ip);
static uint16_t getRemotePort();
*/
func (d *Device) Disconnect() error {
_, err := d.req1(CmdDisconnect)
return err
}
func (d *Device) GetFwVersion() (string, error) {
return d.getString(d.req0(CmdGetFwVersion))
}
func (d *Device) GetConnectionStatus() (ConnectionStatus, error) {
status, err := d.getUint8(d.req0(CmdGetConnStatus))
return ConnectionStatus(status), err
}
func (d *Device) GetCurrentEncryptionType() (EncryptionType, error) {
enctype, err := d.getUint8(d.req1(CmdGetCurrEncrType))
return EncryptionType(enctype), err
}
func (d *Device) GetCurrentBSSID() (MACAddress, error) {
return d.getMACAddress(d.req1(CmdGetCurrBSSID))
}
func (d *Device) GetCurrentRSSI() (int32, error) {
return d.getInt32(d.req1(CmdGetCurrBSSID))
}
func (d *Device) GetCurrentSSID() (string, error) {
return d.getString(d.req1(CmdGetCurrSSID))
}
func (d *Device) GetMACAddress() (MACAddress, error) {
return d.getMACAddress(d.req1(CmdGetMACAddr))
}
func (d *Device) GetIP() (ip, subnet, gateway IPAddress, err error) {
sl := make([]string, 3)
if l, err := d.reqRspStr1(CmdGetIPAddr, dummyData, sl); err != nil {
return "", "", "", err
} else if l != 3 {
return "", "", "", ErrUnexpectedLength
}
return IPAddress(sl[0]), IPAddress(sl[1]), IPAddress(sl[2]), err
}
func (d *Device) GetHostByName(hostname string) (IPAddress, error) {
ok, err := d.getUint8(d.reqStr(CmdReqHostByName, hostname))
if err != nil {
return "", err
}
if ok != 1 {
return "", ErrUnknownHost
}
ip, err := d.getString(d.req0(CmdGetHostByName))
return IPAddress(ip), err
}
func (d *Device) GetNetworkBSSID(idx int) (MACAddress, error) {
if idx < 0 || idx >= MaxNetworks {
return 0, nil
}
return d.getMACAddress(d.reqUint8(CmdGetIdxBSSID, uint8(idx)))
}
func (d *Device) GetNetworkChannel(idx int) (uint8, error) {
if idx < 0 || idx >= MaxNetworks {
return 0, nil
}
return d.getUint8(d.reqUint8(CmdGetIdxChannel, uint8(idx)))
}
func (d *Device) GetNetworkEncrType(idx int) (EncryptionType, error) {
if idx < 0 || idx >= MaxNetworks {
return 0, nil
}
enctype, err := d.getUint8(d.reqUint8(CmdGetIdxEncrType, uint8(idx)))
return EncryptionType(enctype), err
}
func (d *Device) GetNetworkRSSI(idx int) (int32, error) {
if idx < 0 || idx >= MaxNetworks {
return 0, nil
}
return d.getInt32(d.reqUint8(CmdGetIdxRSSI, uint8(idx)))
}
func (d *Device) GetNetworkSSID(idx int) string {
if idx < 0 || idx >= MaxNetworks {
return ""
}
return d.ssids[idx]
}
func (d *Device) GetReasonCode() (uint8, error) {
return d.getUint8(d.req0(CmdGetReasonCode))
}
func (d *Device) GetTime() (string, error) {
return d.getString(d.req0(CmdGetTime))
}
func (d *Device) GetTemperature() (float32, error) {
return d.getFloat32(d.req0(CmdGetTemperature))
}
func (d *Device) Ping(ip IPAddress, ttl uint8) int16 {
return 0
}
func (d *Device) SetDebug(on bool) error {
var v uint8
if on {
v = 1
}
_, err := d.reqUint8(CmdSetDebug, v)
return err
}
func (d *Device) SetNetwork(ssid string) error {
_, err := d.reqStr(CmdSetNet, ssid)
return err
}
func (d *Device) SetPassphrase(ssid string, passphrase string) error {
_, err := d.reqStr2(CmdSetPassphrase, ssid, passphrase)
return err
}
func (d *Device) SetKey(ssid string, index uint8, key string) error {
return ErrNotImplemented
}
func (d *Device) SetNetworkForAP(ssid string) error {
_, err := d.reqStr(CmdSetAPNet, ssid)
return err
}
func (d *Device) SetPassphraseForAP(ssid string, passphrase string) error {
_, err := d.reqStr2(CmdSetAPPassphrase, ssid, passphrase)
return err
}
func (d *Device) SetIP(which uint8, ip uint32, gw uint32, subnet uint32) error {
return ErrNotImplemented
}
func (d *Device) SetDNS(which uint8, dns1 uint32, dns2 uint32) error {
return ErrNotImplemented
}
func (d *Device) SetHostname(hostname string) error {
return ErrNotImplemented
}
func (d *Device) SetPowerMode(mode uint8) error {
_, err := d.reqUint8(CmdSetPowerMode, mode)
return err
}
func (d *Device) ScanNetworks() (uint8, error) {
return d.reqRspStr0(CmdScanNetworks, d.ssids[:])
}
func (d *Device) StartScanNetworks() (uint8, error) {
return d.getUint8(d.req0(CmdStartScanNetworks))
}
func (d *Device) getString(l uint8, err error) (string, error) {
if err != nil {
return "", err
}
return string(d.buf[0:l]), err
}
func (d *Device) getUint8(l uint8, err error) (uint8, error) {
if err != nil {
return 0, err
}
if l != 1 {
if _debug {
println("expected length 1, was actually", l, "\r")
}
return 0, ErrUnexpectedLength
}
return d.buf[0], err
}
func (d *Device) getUint16(l uint8, err error) (uint16, error) {
if err != nil {
return 0, err
}
if l != 2 {
if _debug {
println("expected length 2, was actually", l, "\r")
}
return 0, ErrUnexpectedLength
}
return binary.BigEndian.Uint16(d.buf[0:2]), err
}
func (d *Device) getUint32(l uint8, err error) (uint32, error) {
if err != nil {
return 0, err
}
if l != 4 {
return 0, ErrUnexpectedLength
}
return binary.LittleEndian.Uint32(d.buf[0:4]), err
}
func (d *Device) getInt32(l uint8, err error) (int32, error) {
i, err := d.getUint32(l, err)
return int32(i), err
}
func (d *Device) getFloat32(l uint8, err error) (float32, error) {
i, err := d.getUint32(l, err)
return float32(i), err
}
func (d *Device) getMACAddress(l uint8, err error) (MACAddress, error) {
if err != nil {
return 0, err
}
if l != 6 {
return 0, ErrUnexpectedLength
}
return MACAddress(binary.LittleEndian.Uint64(d.buf[0:8]) >> 16), err
}
// req0 sends a command to the device with no request parameters
func (d *Device) req0(cmd uint8) (l uint8, err error) {
if err := d.sendCmd0(cmd); err != nil {
return 0, err
}
return d.waitRspCmd1(cmd)
}
// req1 sends a command to the device with a single dummy parameters of 0xFF
func (d *Device) req1(cmd uint8) (l uint8, err error) {
return d.reqUint8(cmd, dummyData)
}
// reqUint8 sends a command to the device with a single uint8 parameter
func (d *Device) reqUint8(cmd uint8, data uint8) (l uint8, err error) {
if err := d.sendCmdPadded1(cmd, data); err != nil {
return 0, err
}
return d.waitRspCmd1(cmd)
}
// reqStr sends a command to the device with a single string parameter
func (d *Device) reqStr(cmd uint8, p1 string) (uint8, error) {
if err := d.sendCmdStr(cmd, p1); err != nil {
return 0, err
}
return d.waitRspCmd1(cmd)
}
// reqStr sends a command to the device with 2 string parameters
func (d *Device) reqStr2(cmd uint8, p1 string, p2 string) (uint8, error) {
if err := d.sendCmdStr2(cmd, p1, p2); err != nil {
return 0, err
}
return d.waitRspCmd1(cmd)
}
// reqStrRsp0 sends a command passing a string slice for the response
func (d *Device) reqRspStr0(cmd uint8, sl []string) (l uint8, err error) {
if err := d.sendCmd0(cmd); err != nil {
return 0, err
}
defer d.spiChipDeselect()
if err = d.waitForChipSelect(); err != nil {
return
}
return d.waitRspStr(cmd, sl)
}
// reqStrRsp1 sends a command with a uint8 param and a string slice for the response
func (d *Device) reqRspStr1(cmd uint8, data uint8, sl []string) (uint8, error) {
if err := d.sendCmdPadded1(cmd, data); err != nil {
return 0, err
}
defer d.spiChipDeselect()
if err := d.waitForChipSelect(); err != nil {
return 0, err
}
return d.waitRspStr(cmd, sl)
}
func (d *Device) sendCmd0(cmd uint8) error {
defer d.spiChipDeselect()
if err := d.waitForChipSelect(); err != nil {
return err
}
d.sendCmd(cmd, 0)
return nil
}
func (d *Device) sendCmdPadded1(cmd uint8, data uint8) error {
defer d.spiChipDeselect()
if err := d.waitForChipSelect(); err != nil {
return err
}
d.sendCmd(cmd, 1)
d.sendParam8(data, true)
d.SPI.Transfer(dummyData)
d.SPI.Transfer(dummyData)
return nil
}
func (d *Device) sendCmdStr(cmd uint8, p1 string) (err error) {
defer d.spiChipDeselect()
if err := d.waitForChipSelect(); err != nil {
return err
}
l := d.sendCmd(cmd, 1)
l += d.sendParamStr(p1, false)
d.addPadding(l)
return nil
}
func (d *Device) sendCmdStr2(cmd uint8, p1 string, p2 string) (err error) {
defer d.spiChipDeselect()
if err := d.waitForChipSelect(); err != nil {
return err
}
l := d.sendCmd(cmd, 2)
l += d.sendParamStr(p1, false)
l += d.sendParamStr(p2, true)
d.addPadding(l)
return nil
}
func (d *Device) waitRspCmd1(cmd uint8) (l uint8, err error) {
defer d.spiChipDeselect()
if err = d.waitForChipSelect(); err != nil {
return
}
return d.waitRspCmd(cmd, 1)
}
func (d *Device) sendCmd(cmd uint8, numParam uint8) (l int) {
if _debug {
fmt.Printf(
"sendCmd: %02X %02X %02X",
CmdStart, cmd & ^(uint8(FlagReply)), numParam)
}
l = 3
d.SPI.Transfer(CmdStart)
d.SPI.Transfer(cmd & ^(uint8(FlagReply)))
d.SPI.Transfer(numParam)
if numParam == 0 {
d.SPI.Transfer(CmdEnd)
l += 1
if _debug {
fmt.Printf(" %02X", CmdEnd)
}
}
if _debug {
fmt.Printf(" (%d)\r\n", l)
}
return
}
func (d *Device) sendParamLen16(p uint16) (l int) {
d.SPI.Transfer(uint8(p >> 8))
d.SPI.Transfer(uint8(p & 0xFF))
if _debug {
fmt.Printf(" %02X %02X", uint8(p>>8), uint8(p&0xFF))
}
return 2
}
func (d *Device) sendParamBuf(p []byte, isLastParam bool) (l int) {
if _debug {
println("sendParamBuf:")
}
l += d.sendParamLen16(uint16(len(p)))
for _, b := range p {
if _debug {
fmt.Printf(" %02X", b)
}
d.SPI.Transfer(b)
l += 1
}
if isLastParam {
if _debug {
fmt.Printf(" %02X", CmdEnd)
}
d.SPI.Transfer(CmdEnd)
l += 1
}
if _debug {
fmt.Printf(" (%d) \r\n", l)
}
return
}
func (d *Device) sendParamStr(p string, isLastParam bool) (l int) {
l = len(p)
d.SPI.Transfer(uint8(l))
d.SPI.Tx([]byte(p), nil)
if isLastParam {
d.SPI.Transfer(CmdEnd)
l += 1
}
return
}
func (d *Device) sendParam8(p uint8, isLastParam bool) (l int) {
if _debug {
println("sendParam8:", p, "lastParam:", isLastParam, "\r")
}
l = 2
d.SPI.Transfer(1)
d.SPI.Transfer(p)
if isLastParam {
d.SPI.Transfer(CmdEnd)
l += 1
}
return
}
func (d *Device) sendParam16(p uint16, isLastParam bool) (l int) {
l = 3
d.SPI.Transfer(2)
d.SPI.Transfer(uint8(p >> 8))
d.SPI.Transfer(uint8(p & 0xFF))
if isLastParam {
d.SPI.Transfer(CmdEnd)
l += 1
}
return
}
func (d *Device) sendParam32(p uint32, isLastParam bool) (l int) {
l = 5
d.SPI.Transfer(4)
d.SPI.Transfer(uint8(p >> 24))
d.SPI.Transfer(uint8(p >> 16))
d.SPI.Transfer(uint8(p >> 8))
d.SPI.Transfer(uint8(p & 0xFF))
if isLastParam {
d.SPI.Transfer(CmdEnd)
l += 1
}
return
}
func (d *Device) checkStartCmd() (bool, error) {
check, err := d.waitSpiChar(CmdStart)
if err != nil {
return false, err
}
if !check {
return false, ErrCheckStartCmd
}
return true, nil
}
func (d *Device) waitForChipSelect() (err error) {
err = d.waitForChipReady()
if err == nil {
err = d.spiChipSelect()
}
return
}
func (d *Device) waitForChipReady() error {
if _debug {
println("waitForChipReady()\r")
}
for t := newTimer(10 * time.Second); !(d.ACK.Get() == false); {
if t.Expired() {
return ErrTimeoutChipReady
}
}
return nil
}
func (d *Device) spiChipSelect() error {
if _debug {
println("spiChipSelect()\r")
}
d.CS.Low()
for t := newTimer(5 * time.Millisecond); !t.Expired(); {
if d.ACK.Get() {
return nil
}
}
return ErrTimeoutChipSelect
}
func (d *Device) spiChipDeselect() {
if _debug {
println("spiChipDeselect\r")
}
d.CS.High()
}
func (d *Device) waitSpiChar(wait byte) (bool, error) {
var timeout = 1000
var read byte
for first := true; first || (timeout > 0 && read != wait); timeout-- {
first = false
d.readParam(&read)
if read == CmdErr {
return false, ErrCmdErrorReceived
}
}
if _debug && read != wait {
fmt.Printf("read: %02X, wait: %02X\r\n", read, wait)
}
return read == wait, nil
}
func (d *Device) waitRspCmd(cmd uint8, np uint8) (l uint8, err error) {
if _debug {
println("waitRspCmd")
}
var check bool
var data byte
if check, err = d.checkStartCmd(); !check {
return
}
if check = d.readAndCheckByte(cmd|FlagReply, &data); !check {
return
}
if check = d.readAndCheckByte(np, &data); check {
d.readParam(&l)
for i := uint8(0); i < l; i++ {
d.readParam(&d.buf[i])
}
}
if !d.readAndCheckByte(CmdEnd, &data) {
err = ErrIncorrectSentinel
}
return
}
func (d *Device) waitRspBuf16(cmd uint8, buf []byte) (l uint16, err error) {
if _debug {
println("waitRspBuf16")
}
var check bool
var data byte
if check, err = d.checkStartCmd(); !check {
return
}
if check = d.readAndCheckByte(cmd|FlagReply, &data); !check {
return
}
if check = d.readAndCheckByte(1, &data); check {
l, _ = d.readParamLen16()
for i := uint16(0); i < l; i++ {
d.readParam(&buf[i])
}
}
if !d.readAndCheckByte(CmdEnd, &data) {
err = ErrIncorrectSentinel
}
return
}
func (d *Device) waitRspStr(cmd uint8, sl []string) (numRead uint8, err error) {
if _debug {
println("waitRspStr")
}
var check bool
var data byte
if check, err = d.checkStartCmd(); !check {
return
}
if check = d.readAndCheckByte(cmd|FlagReply, &data); !check {
return
}
numRead, _ = d.SPI.Transfer(dummyData)
if numRead == 0 {
return 0, ErrNoParamsReturned
}
maxNumRead := uint8(len(sl))
for j, l := uint8(0), uint8(0); j < numRead; j++ {
d.readParam(&l)
for i := uint8(0); i < l; i++ {
d.readParam(&d.buf[i])
}
if j < maxNumRead {
sl[j] = string(d.buf[0:l])
if _debug {
fmt.Printf("str %d (%d) - %08X\r\n", j, l, []byte(sl[j]))
}
}
}
for j := numRead; j < maxNumRead; j++ {
if _debug {
println("str", j, "\"\"\r")
}
sl[j] = ""
}
if !d.readAndCheckByte(CmdEnd, &data) {
err = ErrIncorrectSentinel
}
if numRead > maxNumRead {
numRead = maxNumRead
}
return
}
func (d *Device) readAndCheckByte(check byte, read *byte) bool {
d.readParam(read)
return (*read == check)
}
// readParamLen16 reads 2 bytes from the SPI bus (MSB first), returning uint16
func (d *Device) readParamLen16() (v uint16, err error) {
if b, err := d.SPI.Transfer(0xFF); err == nil {
v |= uint16(b << 8)
if b, err = d.SPI.Transfer(0xFF); err == nil {
v |= uint16(b)
}
}
return
}
func (d *Device) readParam(b *byte) (err error) {
*b, err = d.SPI.Transfer(0xFF)
return
}
func (d *Device) addPadding(l int) {
if _debug {
println("addPadding", l, "\r")
}
for i := (4 - (l % 4)) & 3; i > 0; i-- {
if _debug {
println("padding\r")
}
d.SPI.Transfer(dummyData)
}
}