Files
drivers/espat/espat.go
T
2019-05-29 13:33:50 +02:00

253 lines
5.8 KiB
Go

// Package espat implements TCP/UDP wireless communication over serial
// with a separate ESP8266 or ESP32 board using the Espressif AT command set
// across a UART interface.
//
// In order to use this driver, the ESP8266/ESP32 must be flashed with firmware
// supporting the AT command set. Many ESP8266/ESP32 chips already have this firmware
// installed by default. You will need to install this firmware if you have an
// ESP8266 that has been flashed with NodeMCU (Lua) or Arduino firmware.
//
// AT Command Core repository:
// https://github.com/espressif/esp32-at
//
// Datasheet:
// https://www.espressif.com/sites/default/files/documentation/0a-esp8266ex_datasheet_en.pdf
//
// AT command set:
// https://www.espressif.com/sites/default/files/documentation/4a-esp8266_at_instruction_set_en.pdf
//
package espat // import "tinygo.org/x/drivers/espat"
import (
"machine"
"strconv"
"strings"
"time"
)
// Device wraps UART connection to the ESP8266/ESP32.
type Device struct {
bus machine.UART
// command responses that come back from the ESP8266/ESP32
response []byte
// data received from a TCP/UDP connection forwarded by the ESP8266/ESP32
socketdata []byte
}
// New returns a new espat driver. Pass in a fully configured UART bus.
func New(b machine.UART) *Device {
return &Device{bus: b, response: make([]byte, 512), socketdata: make([]byte, 0, 1024)}
}
// Configure sets up the device for communication.
func (d Device) Configure() {
}
// Connected checks if there is communication with the ESP8266/ESP32.
func (d *Device) Connected() bool {
d.Execute(Test)
// handle response here, should include "OK"
r := d.Response()
if strings.Contains(string(r), "OK") {
return true
}
return false
}
// Write raw bytes to the UART.
func (d *Device) Write(b []byte) (n int, err error) {
return d.bus.Write(b)
}
// Read raw bytes from the UART.
func (d *Device) Read(b []byte) (n int, err error) {
return d.bus.Read(b)
}
// how long in milliseconds to pause after sending AT commands
const pause = 100
// Execute sends an AT command to the ESP8266/ESP32.
func (d Device) Execute(cmd string) error {
_, err := d.Write([]byte("AT" + cmd + "\r\n"))
return err
}
// Query sends an AT command to the ESP8266/ESP32 that returns the
// current value for some configuration parameter.
func (d Device) Query(cmd string) (string, error) {
_, err := d.Write([]byte("AT" + cmd + "?\r\n"))
return "", err
}
// Set sends an AT command with params to the ESP8266/ESP32 for a
// configuration value to be set.
func (d Device) Set(cmd, params string) error {
_, err := d.Write([]byte("AT" + cmd + "=" + params + "\r\n"))
return err
}
// Version returns the ESP8266/ESP32 firmware version info.
func (d Device) Version() []byte {
d.Execute(Version)
return d.Response()
}
// Echo sets the ESP8266/ESP32 echo setting.
func (d Device) Echo(set bool) {
if set {
d.Execute(EchoConfigOn)
} else {
d.Execute(EchoConfigOff)
}
// TODO: check for success
d.Response()
}
// Reset restarts the ESP8266/ESP32 firmware. Due to how the baud rate changes,
// this messes up communication with the ESP8266/ESP32 module. So make sure you know
// what you are doing when you call this.
func (d Device) Reset() {
d.Execute(Restart)
d.Response()
}
// ReadSocket returns the data that has already been read in from the responses.
func (d *Device) ReadSocket(b []byte) (n int, err error) {
// make sure no data in buffer
d.Response()
count := len(b)
if len(b) >= len(d.socketdata) {
// copy it all, then clear socket data
count = len(d.socketdata)
copy(b, d.socketdata[:count])
d.socketdata = d.socketdata[:0]
} else {
// copy all we can, then keep the remaining socket data around
copy(b, d.socketdata[:count])
copy(d.socketdata, d.socketdata[count:])
d.socketdata = d.socketdata[:len(d.socketdata)-count]
}
return count, nil
}
// Response gets the next response bytes from the ESP8266/ESP32.
func (d *Device) Response() []byte {
var i, retries int
header := make([]byte, 2)
for {
for d.bus.Buffered() > 0 {
// get the first 2 bytes
header[0], _ = d.bus.ReadByte()
header[1], _ = d.bus.ReadByte()
if d.isLeadingCRLF(header) {
// skip it
header[0], _ = d.bus.ReadByte()
header[1], _ = d.bus.ReadByte()
}
if d.isIPD(header) {
// is socket data packet
d.parseIPD()
} else {
// no, so put into response
d.response[i] = header[0]
i++
d.response[i] = header[1]
i++
}
// read the rest of normal command response
for d.bus.Buffered() > 0 {
data, err := d.bus.ReadByte()
if err != nil {
return nil
}
d.response[i] = data
i++
}
}
retries++
if retries > 2 {
break
}
// pause to make sure is no more data to be read
time.Sleep(10 * time.Millisecond)
}
return d.response[:i]
}
func (d *Device) isLeadingCRLF(b []byte) bool {
if len(b) < 2 {
return false
}
if b[0] == 13 && b[1] == 10 {
return true
}
return false
}
func (d *Device) isIPD(b []byte) bool {
if len(b) < 2 {
return false
}
if b[0] == '+' && b[1] == 'I' {
return true
}
return false
}
func (d *Device) parseIPD() bool {
data, _ := d.bus.ReadByte()
if data != 'P' {
// error
return false
}
data, _ = d.bus.ReadByte()
if data != 'D' {
// error
return false
}
data, _ = d.bus.ReadByte()
if data != ',' {
// error
return false
}
// get the expected data length
// skip remaining header up to the ":"
buf := []byte{}
data, _ = d.bus.ReadByte()
for data != ':' {
// put into the buffer with int value here
buf = append(buf, data)
// read next value
data, _ = d.bus.ReadByte()
}
val := string(buf)
count, err := strconv.Atoi(val)
if err != nil {
// not expected data here. what to do?
return false
}
// load up the socket data
// only read the expected amount of data
for m := 0; m < count; m++ {
data, _ = d.bus.ReadByte()
d.socketdata = append(d.socketdata, data)
}
return true
}