mirror of
https://github.com/tinygo-org/drivers.git
synced 2026-07-26 10:38:41 +00:00
46c9ba9595
Signed-off-by: deadprogram <ron@hybridgroup.com>
1095 lines
25 KiB
Go
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)
|
|
}
|
|
}
|