mirror of
https://github.com/tinygo-org/drivers.git
synced 2026-08-06 07:53:41 +00:00
espat: add support for esp8266/esp32 Wifi adaptor AT command set with net-compatible UDP support
Signed-off-by: Ron Evans <ron@hybridgroup.com>
This commit is contained in:
committed by
Ayke van Laethem
parent
ae53ea7d81
commit
0c2924cd77
@@ -0,0 +1,104 @@
|
||||
package espat
|
||||
|
||||
// Basic AT commands
|
||||
const (
|
||||
// Test that the device is working.
|
||||
Test = ""
|
||||
|
||||
// Restart module
|
||||
Restart = "+RST"
|
||||
|
||||
// Version show info about the current software version.
|
||||
Version = "+GMR"
|
||||
|
||||
// Enter deep-sleep mode
|
||||
Sleep = "+GSLP"
|
||||
|
||||
// Configure echo.
|
||||
EchoConfig = "E"
|
||||
|
||||
// EchoConfigOn
|
||||
EchoConfigOn = EchoConfig + "1"
|
||||
|
||||
// EchoConfigOff
|
||||
EchoConfigOff = EchoConfig + "0"
|
||||
|
||||
// Configure UART
|
||||
UARTConfig = "+UART"
|
||||
)
|
||||
|
||||
// WiFi commands.
|
||||
const (
|
||||
// WiFi mode (sta/AP/sta+AP)
|
||||
WifiMode = "+CWMODE"
|
||||
|
||||
// Connect to an access point.
|
||||
ConnectAP = "+CWJAP"
|
||||
|
||||
// List available AP's
|
||||
ListAP = "+CWLAP"
|
||||
|
||||
// Disconnect from the current AP
|
||||
Disconnect = "+CWQAP"
|
||||
|
||||
// Set softAP configuration. This also activates the ESP8266/ESP32 to act as an access point.
|
||||
// The settings will not be saved in flash memory, so they will be forgotten on next reset.
|
||||
SoftAPConfigCurrent = "+CWSAP_CUR"
|
||||
|
||||
// Set softAP configuration as saved in flash. This also activates the ESP8266/ESP32 to act as an
|
||||
// access point. The settings will be saved in flash memory, so they will be used on next reset.
|
||||
SoftAPConfigFlash = "+CWSAP_DEF"
|
||||
|
||||
// List station IP's connected to softAP
|
||||
ListConnectedIP = "+CWLIF"
|
||||
|
||||
// Enable/disable DHCP
|
||||
DHCPConfig = "+CWDHCP"
|
||||
|
||||
// Set MAC address of station
|
||||
SetStationMACAddress = "+CIPSTAMAC"
|
||||
|
||||
// Set MAC address of softAP
|
||||
SetAPMACAddress = "+CIPAPMAC"
|
||||
|
||||
// Set IP address of ESP8266/ESP32 station
|
||||
SetStationIP = "+CIPSTA"
|
||||
|
||||
// Set IP address of ESP8266/ESP32 when acting as access point.
|
||||
// The IP address will not be saved in flash memory, so it will be forgotten on next reset.
|
||||
SetSoftAPIPCurrent = "+CIPAP_CUR"
|
||||
|
||||
// Set IP address of ESP8266/ESP32 when acting as access point.
|
||||
// The IP address will be saved in flash memory, so they will be used on next reset.
|
||||
SetSoftAPIPFlash = "+CIPAP_DEF"
|
||||
)
|
||||
|
||||
// TCP/IP commands
|
||||
const (
|
||||
// Get connection status
|
||||
TCPStatus = "+CIPSTATUS"
|
||||
|
||||
// Establish TCP connection or register UDP port
|
||||
TCPConnect = "+CIPSTART"
|
||||
|
||||
// Send Data
|
||||
TCPSend = "+CIPSEND"
|
||||
|
||||
// Close TCP/UDP connection
|
||||
TCPClose = "+CIPCLOSE"
|
||||
|
||||
// Get local IP address
|
||||
GetLocalIP = "+CIFSR"
|
||||
|
||||
// Set multiple connections mode
|
||||
TCPMultiple = "+CIPMUX"
|
||||
|
||||
// Configure as server
|
||||
ServerConfig = "+CIPSERVER"
|
||||
|
||||
// Set transmission mode
|
||||
TransmissionMode = "+CIPMODE"
|
||||
|
||||
// Set timeout when ESP8266/ESP32 runs as TCP server
|
||||
SetServerTimeout = "+CIPSTO"
|
||||
)
|
||||
+252
@@ -0,0 +1,252 @@
|
||||
// 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 (
|
||||
"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
|
||||
}
|
||||
+143
@@ -0,0 +1,143 @@
|
||||
package espat
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
// DialUDP makes a UDP network connection. raadr is the port that the messages will
|
||||
// be sent to, and laddr is the port that will be listened to in order to
|
||||
// receive incoming messages.
|
||||
func (d Device) DialUDP(network string, laddr, raddr *UDPAddr) (*SerialConn, error) {
|
||||
addr := raddr.IP.String()
|
||||
sendport := strconv.Itoa(raddr.Port)
|
||||
listenport := strconv.Itoa(laddr.Port)
|
||||
|
||||
// disconnect any old socket
|
||||
d.DisconnectSocket()
|
||||
|
||||
// connect new socket
|
||||
d.ConnectUDPSocket(addr, sendport, listenport)
|
||||
|
||||
return &SerialConn{Adaptor: &d, laddr: laddr, raddr: raddr}, nil
|
||||
}
|
||||
|
||||
// ListenUDP listens for UDP connections on the port listed in laddr.
|
||||
func (d Device) ListenUDP(network string, laddr *UDPAddr) (*SerialConn, error) {
|
||||
addr := "0"
|
||||
sendport := "0"
|
||||
listenport := strconv.Itoa(laddr.Port)
|
||||
|
||||
// disconnect any old socket
|
||||
d.DisconnectSocket()
|
||||
|
||||
// connect new socket
|
||||
d.ConnectUDPSocket(addr, sendport, listenport)
|
||||
|
||||
return &SerialConn{Adaptor: &d, laddr: laddr}, nil
|
||||
}
|
||||
|
||||
// SerialConn is a loosely net.Conn compatible intended to support
|
||||
// TCP/UDP over serial.
|
||||
type SerialConn struct {
|
||||
Adaptor *Device
|
||||
laddr *UDPAddr
|
||||
raddr *UDPAddr
|
||||
}
|
||||
|
||||
// Read reads data from the connection.
|
||||
// TODO: implement the full method functionality:
|
||||
// Read can be made to time out and return an Error with Timeout() == true
|
||||
// after a fixed time limit; see SetDeadline and SetReadDeadline.
|
||||
func (c *SerialConn) Read(b []byte) (n int, err error) {
|
||||
// read only the data that has been received via "+IPD" socket
|
||||
return c.Adaptor.ReadSocket(b)
|
||||
}
|
||||
|
||||
// Write writes data to the connection.
|
||||
// TODO: implement the full method functionality for timeouts.
|
||||
// Write can be made to time out and return an Error with Timeout() == true
|
||||
// after a fixed time limit; see SetDeadline and SetWriteDeadline.
|
||||
func (c *SerialConn) Write(b []byte) (n int, err error) {
|
||||
// specify that is a data transfer to the
|
||||
// currently open socket, not commands to the ESP8266/ESP32.
|
||||
c.Adaptor.StartSocketSend(len(b))
|
||||
return c.Adaptor.Write(b)
|
||||
}
|
||||
|
||||
// Close closes the connection.
|
||||
// Currently only supports a single Read or Write operations without blocking.
|
||||
func (c *SerialConn) Close() error {
|
||||
c.Adaptor.DisconnectSocket()
|
||||
return nil
|
||||
}
|
||||
|
||||
// LocalAddr returns the local network address.
|
||||
func (c *SerialConn) LocalAddr() UDPAddr {
|
||||
return *c.laddr
|
||||
}
|
||||
|
||||
// RemoteAddr returns the remote network address.
|
||||
func (c *SerialConn) RemoteAddr() UDPAddr {
|
||||
return *c.laddr
|
||||
}
|
||||
|
||||
// SetDeadline sets the read and write deadlines associated
|
||||
// with the connection. It is equivalent to calling both
|
||||
// SetReadDeadline and SetWriteDeadline.
|
||||
//
|
||||
// A deadline is an absolute time after which I/O operations
|
||||
// fail with a timeout (see type Error) instead of
|
||||
// blocking. The deadline applies to all future and pending
|
||||
// I/O, not just the immediately following call to Read or
|
||||
// Write. After a deadline has been exceeded, the connection
|
||||
// can be refreshed by setting a deadline in the future.
|
||||
//
|
||||
// An idle timeout can be implemented by repeatedly extending
|
||||
// the deadline after successful Read or Write calls.
|
||||
//
|
||||
// A zero value for t means I/O operations will not time out.
|
||||
func (c *SerialConn) SetDeadline(t time.Time) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetReadDeadline sets the deadline for future Read calls
|
||||
// and any currently-blocked Read call.
|
||||
// A zero value for t means Read will not time out.
|
||||
func (c *SerialConn) SetReadDeadline(t time.Time) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetWriteDeadline sets the deadline for future Write calls
|
||||
// and any currently-blocked Write call.
|
||||
// Even if write times out, it may return n > 0, indicating that
|
||||
// some of the data was successfully written.
|
||||
// A zero value for t means Write will not time out.
|
||||
func (c *SerialConn) SetWriteDeadline(t time.Time) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// The following definitions are here to support a Golang standard package
|
||||
// net-compatible interface for IP until TinyGo can compile the net package.
|
||||
|
||||
// IP is an IP address. Unlike the standard implementation, it is only
|
||||
// a buffer of bytes that contains the string form of the IP address, not the
|
||||
// full byte format used by the Go standard .
|
||||
type IP []byte
|
||||
|
||||
// UDPAddr here to serve as compatible type. until TinyGo can compile the net package.
|
||||
type UDPAddr struct {
|
||||
IP IP
|
||||
Port int
|
||||
Zone string // IPv6 scoped addressing zone; added in Go 1.1
|
||||
}
|
||||
|
||||
// ParseIP parses s as an IP address, returning the result.
|
||||
func ParseIP(s string) IP {
|
||||
return IP([]byte(s))
|
||||
}
|
||||
|
||||
// String returns the string form of the IP address ip.
|
||||
func (ip IP) String() string {
|
||||
return string(ip)
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
package espat
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
TCPMuxSingle = 0
|
||||
TCPMuxMultiple = 1
|
||||
|
||||
TCPTransferModeNormal = 0
|
||||
TCPTransferModeUnvarnished = 1
|
||||
)
|
||||
|
||||
// ConnectTCPSocket creates a new TCP socket connection for the ESP8266/ESP32.
|
||||
// Currently only supports single connection mode.
|
||||
func (d *Device) ConnectTCPSocket(addr, port string) error {
|
||||
protocol := "TCP"
|
||||
val := "\"" + protocol + "\",\"" + addr + "\"," + port
|
||||
d.Set(TCPConnect, val)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
d.Response()
|
||||
return nil
|
||||
}
|
||||
|
||||
// ConnectUDPSocket creates a new UDP connection for the ESP8266/ESP32.
|
||||
func (d *Device) ConnectUDPSocket(addr, sendport, listenport string) error {
|
||||
protocol := "UDP"
|
||||
val := "\"" + protocol + "\",\"" + addr + "\"," + sendport + "," + listenport + ",2"
|
||||
d.Set(TCPConnect, val)
|
||||
time.Sleep(pause * time.Millisecond)
|
||||
d.Response()
|
||||
return nil
|
||||
}
|
||||
|
||||
// DisconnectSocket disconnects the ESP8266/ESP32 from the current TCP/UDP connection.
|
||||
func (d *Device) DisconnectSocket() error {
|
||||
d.Execute(TCPClose)
|
||||
time.Sleep(pause * time.Millisecond)
|
||||
d.Response()
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetMux sets the ESP8266/ESP32 current client TCP/UDP configuration for concurrent connections
|
||||
// either single TCPMuxSingle or multiple TCPMuxMultiple (up to 4).
|
||||
func (d *Device) SetMux(mode int) error {
|
||||
val := strconv.Itoa(mode)
|
||||
d.Set(TCPMultiple, val)
|
||||
time.Sleep(pause * time.Millisecond)
|
||||
d.Response()
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetMux returns the ESP8266/ESP32 current client TCP/UDP configuration for concurrent connections.
|
||||
func (d *Device) GetMux() ([]byte, error) {
|
||||
d.Query(TCPMultiple)
|
||||
return d.Response(), nil
|
||||
}
|
||||
|
||||
// SetTCPTransferMode sets the ESP8266/ESP32 current client TCP/UDP transfer mode.
|
||||
// Either TCPTransferModeNormal or TCPTransferModeUnvarnished.
|
||||
func (d *Device) SetTCPTransferMode(mode int) error {
|
||||
val := strconv.Itoa(mode)
|
||||
d.Set(TransmissionMode, val)
|
||||
time.Sleep(pause * time.Millisecond)
|
||||
d.Response()
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetTCPTransferMode returns the ESP8266/ESP32 current client TCP/UDP transfer mode.
|
||||
func (d *Device) GetTCPTransferMode() []byte {
|
||||
d.Query(TransmissionMode)
|
||||
return d.Response()
|
||||
}
|
||||
|
||||
// StartSocketSend gets the ESP8266/ESP32 ready to receive TCP/UDP socket data.
|
||||
func (d *Device) StartSocketSend(size int) error {
|
||||
val := strconv.Itoa(size)
|
||||
d.Set(TCPSend, val)
|
||||
|
||||
// TODO: wait until ">" is received, which indicates
|
||||
// ready to receive data
|
||||
d.Response()
|
||||
return nil
|
||||
}
|
||||
|
||||
// EndSocketSend tell the ESP8266/ESP32 the TCP/UDP socket data sending is complete,
|
||||
// and to return to command mode. This is only used in "unvarnished" raw mode.
|
||||
func (d *Device) EndSocketSend() error {
|
||||
d.Write([]byte("+++"))
|
||||
|
||||
// TODO: wait until ">" is received, which indicates
|
||||
// ready to receive data
|
||||
d.Response()
|
||||
return nil
|
||||
}
|
||||
+154
@@ -0,0 +1,154 @@
|
||||
package espat
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
WifiModeClient = 1
|
||||
WifiModeAP = 2
|
||||
WifiModeDual = 3
|
||||
|
||||
WifiAPSecurityOpen = 1
|
||||
WifiAPSecurityWPA_PSK = 2
|
||||
WifiAPSecurityWPA2_PSK = 3
|
||||
WifiAPSecurityWPA_WPA2_PSK = 4
|
||||
)
|
||||
|
||||
// GetWifiMode returns the ESP8266/ESP32 wifi mode.
|
||||
func (d *Device) GetWifiMode() []byte {
|
||||
d.Query(WifiMode)
|
||||
return d.Response()
|
||||
}
|
||||
|
||||
// SetWifiMode sets the ESP8266/ESP32 wifi mode.
|
||||
func (d *Device) SetWifiMode(mode int) error {
|
||||
val := strconv.Itoa(mode)
|
||||
d.Set(WifiMode, val)
|
||||
time.Sleep(pause * time.Millisecond)
|
||||
d.Response()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Wifi Client
|
||||
|
||||
// GetConnectedAP returns the ESP8266/ESP32 is currently connected to as a client.
|
||||
func (d *Device) GetConnectedAP() []byte {
|
||||
d.Query(ConnectAP)
|
||||
return d.Response()
|
||||
}
|
||||
|
||||
// ConnectToAP connects the ESP8266/ESP32 to an access point.
|
||||
// ws is the number of seconds to wait for connection.
|
||||
func (d *Device) ConnectToAP(ssid, pwd string, ws int) error {
|
||||
val := "\"" + ssid + "\",\"" + pwd + "\""
|
||||
d.Set(ConnectAP, val)
|
||||
// TODO: a better way to wait for connect and check for up to ws seconds.
|
||||
time.Sleep(time.Duration(ws) * time.Second)
|
||||
d.Response()
|
||||
return nil
|
||||
}
|
||||
|
||||
// DisconnectFromAP disconnects the ESP8266/ESP32 from the current access point.
|
||||
func (d *Device) DisconnectFromAP() error {
|
||||
d.Execute(Disconnect)
|
||||
time.Sleep(1000 * time.Millisecond)
|
||||
d.Response()
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetClientIP returns the ESP8266/ESP32 current client IP addess when connected to an Access Point.
|
||||
func (d *Device) GetClientIP() string {
|
||||
d.Query(SetStationIP)
|
||||
return string(d.Response())
|
||||
}
|
||||
|
||||
// SetClientIP sets the ESP8266/ESP32 current client IP addess when connected to an Access Point.
|
||||
func (d *Device) SetClientIP(ipaddr string) []byte {
|
||||
val := "\"" + ipaddr + "\""
|
||||
d.Set(ConnectAP, val)
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
d.Response()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Access Point
|
||||
|
||||
// GetAPConfig returns the ESP8266/ESP32 current configuration when acting as an Access Point.
|
||||
func (d *Device) GetAPConfig() string {
|
||||
d.Query(SoftAPConfigCurrent)
|
||||
return string(d.Response())
|
||||
}
|
||||
|
||||
// SetAPConfig sets the ESP8266/ESP32 current configuration when acting as an Access Point.
|
||||
// ch indicates which radiochannel to use. security should be one of the const values
|
||||
// such as WifiAPSecurityOpen etc.
|
||||
func (d *Device) SetAPConfig(ssid, pwd string, ch, security int) error {
|
||||
chval := strconv.Itoa(ch)
|
||||
ecnval := strconv.Itoa(security)
|
||||
val := "\"" + ssid + "\",\"" + pwd + "\"," + chval + "," + ecnval
|
||||
d.Set(SoftAPConfigCurrent, val)
|
||||
time.Sleep(1000 * time.Millisecond)
|
||||
d.Response()
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetAPClients returns the ESP8266/ESP32 current clients when acting as an Access Point.
|
||||
func (d *Device) GetAPClients() string {
|
||||
d.Query(ListConnectedIP)
|
||||
return string(d.Response())
|
||||
}
|
||||
|
||||
// GetAPIP returns the ESP8266/ESP32 current IP addess when configured as an Access Point.
|
||||
func (d *Device) GetAPIP() string {
|
||||
d.Query(SetSoftAPIPCurrent)
|
||||
return string(d.Response())
|
||||
}
|
||||
|
||||
// SetAPIP sets the ESP8266/ESP32 current IP addess when configured as an Access Point.
|
||||
func (d *Device) SetAPIP(ipaddr string) error {
|
||||
val := "\"" + ipaddr + "\""
|
||||
d.Set(SetSoftAPIPCurrent, val)
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
d.Response()
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetAPConfigFlash returns the ESP8266/ESP32 current configuration acting as an Access Point
|
||||
// from flash storage. These settings are those used after a reset.
|
||||
func (d *Device) GetAPConfigFlash() string {
|
||||
d.Query(SoftAPConfigFlash)
|
||||
return string(d.Response())
|
||||
}
|
||||
|
||||
// SetAPConfigFlash sets the ESP8266/ESP32 current configuration acting as an Access Point,
|
||||
// and saves them to flash storage. These settings will be used after a reset.
|
||||
// ch indicates which radiochannel to use. security should be one of the const values
|
||||
// such as WifiAPSecurityOpen etc.
|
||||
func (d *Device) SetAPConfigFlash(ssid, pwd string, ch, security int) error {
|
||||
chval := strconv.Itoa(ch)
|
||||
ecnval := strconv.Itoa(security)
|
||||
val := "\"" + ssid + "\",\"" + pwd + "\"," + chval + "," + ecnval
|
||||
d.Set(SoftAPConfigFlash, val)
|
||||
time.Sleep(1000 * time.Millisecond)
|
||||
d.Response()
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetAPIPFlash returns the ESP8266/ESP32 IP address as saved to flash storage.
|
||||
// This is the IP address that will be used after a reset.
|
||||
func (d *Device) GetAPIPFlash() string {
|
||||
d.Query(SetSoftAPIPFlash)
|
||||
return string(d.Response())
|
||||
}
|
||||
|
||||
// SetAPIPFlash sets the ESP8266/ESP32 current IP addess when configured as an Access Point.
|
||||
// The IP will be saved to flash storage, and will be used after a reset.
|
||||
func (d *Device) SetAPIPFlash(ipaddr string) error {
|
||||
val := "\"" + ipaddr + "\""
|
||||
d.Set(SetSoftAPIPFlash, val)
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
d.Response()
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
// This is a console to a ESP8266/ESP32 running on the device UART1.
|
||||
// Allows you to type AT commands from your computer via the microcontroller.
|
||||
//
|
||||
// In other words:
|
||||
// Your computer <--> UART0 <--> MCU <--> UART1 <--> ESP8266 <--> INTERNET
|
||||
//
|
||||
// More information on the Espressif AT command set at:
|
||||
// https://www.espressif.com/sites/default/files/documentation/4a-esp8266_at_instruction_set_en.pdf
|
||||
//
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
"time"
|
||||
|
||||
"github.com/tinygo-org/drivers/espat"
|
||||
)
|
||||
|
||||
// change actAsAP to true to act as an access point instead of connecting to one.
|
||||
const actAsAP = false
|
||||
|
||||
// access point info
|
||||
const ssid = "YOURSSID"
|
||||
const pass = "YOURPASS"
|
||||
|
||||
// change these to connect to a different UART or pins for the ESP8266/ESP32
|
||||
var (
|
||||
uart = machine.UART1
|
||||
tx uint8 = machine.D10
|
||||
rx uint8 = machine.D11
|
||||
|
||||
console = machine.UART0
|
||||
|
||||
adaptor *espat.Device
|
||||
)
|
||||
|
||||
func main() {
|
||||
uart.Configure(machine.UARTConfig{TX: tx, RX: rx})
|
||||
|
||||
// Init esp8266
|
||||
adaptor = espat.New(uart)
|
||||
adaptor.Configure()
|
||||
|
||||
// first check if connected
|
||||
if adaptor.Connected() {
|
||||
adaptor.Echo(false)
|
||||
console.Write([]byte("\r\n"))
|
||||
console.Write([]byte("ESP-AT console enabled.\r\n"))
|
||||
console.Write([]byte("Firmware version:\r\n"))
|
||||
console.Write(adaptor.Version())
|
||||
console.Write([]byte("\r\n"))
|
||||
|
||||
if actAsAP {
|
||||
provideAP()
|
||||
} else {
|
||||
connectToAP()
|
||||
}
|
||||
|
||||
console.Write([]byte("Type an AT command then press enter:\r\n"))
|
||||
prompt()
|
||||
} else {
|
||||
console.Write([]byte("\r\n"))
|
||||
console.Write([]byte("Unable to connect to wifi adaptor.\r\n"))
|
||||
return
|
||||
}
|
||||
|
||||
input := make([]byte, 64)
|
||||
i := 0
|
||||
for {
|
||||
if console.Buffered() > 0 {
|
||||
data, _ := console.ReadByte()
|
||||
|
||||
switch data {
|
||||
case 13:
|
||||
// return key
|
||||
console.Write([]byte("\r\n"))
|
||||
|
||||
// send command to ESP8266
|
||||
input[i] = byte('\r')
|
||||
input[i+1] = byte('\n')
|
||||
adaptor.Write(input[:i+2])
|
||||
|
||||
// give the ESP8266 a chance to respond.
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
|
||||
// display response
|
||||
console.Write(adaptor.Response())
|
||||
|
||||
// prompt
|
||||
prompt()
|
||||
|
||||
i = 0
|
||||
continue
|
||||
default:
|
||||
// just echo the character
|
||||
console.WriteByte(data)
|
||||
input[i] = data
|
||||
i++
|
||||
}
|
||||
}
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
|
||||
func prompt() {
|
||||
console.Write([]byte("ESPAT>"))
|
||||
}
|
||||
|
||||
// connect to access point
|
||||
func connectToAP() {
|
||||
console.Write([]byte("Connecting to wifi network...\r\n"))
|
||||
adaptor.SetWifiMode(espat.WifiModeClient)
|
||||
adaptor.ConnectToAP(ssid, pass, 10)
|
||||
console.Write([]byte("Connected.\r\n"))
|
||||
console.Write([]byte(adaptor.GetClientIP()))
|
||||
console.Write([]byte("\r\n"))
|
||||
}
|
||||
|
||||
// provide access point
|
||||
func provideAP() {
|
||||
console.Write([]byte("Starting wifi network as access point '"))
|
||||
console.Write([]byte(ssid))
|
||||
console.Write([]byte("'...\r\n"))
|
||||
adaptor.SetWifiMode(espat.WifiModeAP)
|
||||
adaptor.SetAPConfig(ssid, pass, 7, espat.WifiAPSecurityWPA2_PSK)
|
||||
console.Write([]byte("Ready.\r\n"))
|
||||
console.Write([]byte(adaptor.GetAPIP()))
|
||||
console.Write([]byte("\r\n"))
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
// This is a sensor hub that uses a ESP8266/ESP32 running on the device UART1.
|
||||
// It creates a UDP "server" you can use to get info to/from your computer via the microcontroller.
|
||||
//
|
||||
// In other words:
|
||||
// Your computer <--> UART0 <--> MCU <--> UART1 <--> ESP8266 <--> INTERNET
|
||||
//
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
"time"
|
||||
|
||||
"github.com/tinygo-org/drivers/espat"
|
||||
)
|
||||
|
||||
// change actAsAP to true to act as an access point instead of connecting to one.
|
||||
const actAsAP = false
|
||||
|
||||
// access point info
|
||||
const ssid = "YOURSSID"
|
||||
const pass = "YOURPASS"
|
||||
|
||||
// change these to connect to a different UART or pins for the ESP8266/ESP32
|
||||
var (
|
||||
uart = machine.UART1
|
||||
tx uint8 = machine.D10
|
||||
rx uint8 = machine.D11
|
||||
|
||||
console = machine.UART0
|
||||
|
||||
adaptor *espat.Device
|
||||
)
|
||||
|
||||
func main() {
|
||||
uart.Configure(machine.UARTConfig{TX: tx, RX: rx})
|
||||
|
||||
// Init esp8266
|
||||
adaptor = espat.New(uart)
|
||||
adaptor.Configure()
|
||||
|
||||
readyled := machine.GPIO{machine.LED}
|
||||
readyled.Configure(machine.GPIOConfig{Mode: machine.GPIO_OUTPUT})
|
||||
readyled.High()
|
||||
|
||||
// first check if connected
|
||||
if adaptor.Connected() {
|
||||
console.Write([]byte("Connected to wifi adaptor.\r\n"))
|
||||
adaptor.Echo(false)
|
||||
|
||||
if actAsAP {
|
||||
provideAP()
|
||||
} else {
|
||||
connectToAP()
|
||||
}
|
||||
} else {
|
||||
console.Write([]byte("\r\n"))
|
||||
console.Write([]byte("Unable to connect to wifi adaptor.\r\n"))
|
||||
return
|
||||
}
|
||||
|
||||
// now make UDP connection
|
||||
laddr := &espat.UDPAddr{Port: 2222}
|
||||
console.Write([]byte("Loading UDP listener...\r\n"))
|
||||
conn, _ := adaptor.ListenUDP("UDP", laddr)
|
||||
|
||||
console.Write([]byte("Waiting for data...\r\n"))
|
||||
data := make([]byte, 50)
|
||||
blink := true
|
||||
for {
|
||||
n, _ := conn.Read(data)
|
||||
if n > 0 {
|
||||
console.Write(data[:n])
|
||||
console.Write([]byte("\r\n"))
|
||||
conn.Write([]byte("hello back\r\n"))
|
||||
}
|
||||
blink = !blink
|
||||
if blink {
|
||||
readyled.High()
|
||||
} else {
|
||||
readyled.Low()
|
||||
}
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
}
|
||||
|
||||
// Right now this code is never reached. Need a way to trigger it...
|
||||
console.Write([]byte("Disconnecting UDP...\r\n"))
|
||||
conn.Close()
|
||||
console.Write([]byte("Done.\r\n"))
|
||||
}
|
||||
|
||||
// connect to access point
|
||||
func connectToAP() {
|
||||
console.Write([]byte("Connecting to wifi network...\r\n"))
|
||||
adaptor.SetWifiMode(espat.WifiModeClient)
|
||||
adaptor.ConnectToAP(ssid, pass, 10)
|
||||
console.Write([]byte("Connected.\r\n"))
|
||||
console.Write([]byte(adaptor.GetClientIP()))
|
||||
console.Write([]byte("\r\n"))
|
||||
}
|
||||
|
||||
// provide access point
|
||||
func provideAP() {
|
||||
console.Write([]byte("Starting wifi network as access point '"))
|
||||
console.Write([]byte(ssid))
|
||||
console.Write([]byte("'...\r\n"))
|
||||
adaptor.SetWifiMode(espat.WifiModeAP)
|
||||
adaptor.SetAPConfig(ssid, pass, 7, espat.WifiAPSecurityWPA2_PSK)
|
||||
console.Write([]byte("Ready.\r\n"))
|
||||
console.Write([]byte(adaptor.GetAPIP()))
|
||||
console.Write([]byte("\r\n"))
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
// This is a sensor station that uses a ESP8266 or ESP32 running on the device UART1.
|
||||
// It creates a UDP connection you can use to get info to/from your computer via the microcontroller.
|
||||
//
|
||||
// In other words:
|
||||
// Your computer <--> UART0 <--> MCU <--> UART1 <--> ESP8266
|
||||
//
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
"time"
|
||||
|
||||
"github.com/tinygo-org/drivers/espat"
|
||||
)
|
||||
|
||||
// access point info
|
||||
const ssid = "YOURSSID"
|
||||
const pass = "YOURPASS"
|
||||
|
||||
// IP address of the listener aka "hub". Replace with your own info.
|
||||
const hubIP = "0.0.0.0"
|
||||
|
||||
// change these to connect to a different UART or pins for the ESP8266/ESP32
|
||||
var (
|
||||
uart = machine.UART1
|
||||
tx uint8 = machine.D10
|
||||
rx uint8 = machine.D11
|
||||
|
||||
console = machine.UART0
|
||||
|
||||
adaptor *espat.Device
|
||||
)
|
||||
|
||||
func main() {
|
||||
uart.Configure(machine.UARTConfig{TX: tx, RX: rx})
|
||||
|
||||
// Init esp8266/esp32
|
||||
adaptor = espat.New(uart)
|
||||
adaptor.Configure()
|
||||
|
||||
// first check if connected
|
||||
if adaptor.Connected() {
|
||||
console.Write([]byte("Connected to wifi adaptor.\r\n"))
|
||||
adaptor.Echo(false)
|
||||
|
||||
connectToAP()
|
||||
} else {
|
||||
console.Write([]byte("\r\n"))
|
||||
console.Write([]byte("Unable to connect to wifi adaptor.\r\n"))
|
||||
return
|
||||
}
|
||||
|
||||
// now make UDP connection
|
||||
ip := espat.ParseIP(hubIP)
|
||||
raddr := &espat.UDPAddr{IP: ip, Port: 2222}
|
||||
laddr := &espat.UDPAddr{Port: 2222}
|
||||
|
||||
console.Write([]byte("Dialing UDP connection...\r\n"))
|
||||
conn, _ := adaptor.DialUDP("udp", laddr, raddr)
|
||||
|
||||
for {
|
||||
// send data
|
||||
console.Write([]byte("Sending data...\r\n"))
|
||||
conn.Write([]byte("hello\r\n"))
|
||||
|
||||
time.Sleep(1000 * time.Millisecond)
|
||||
}
|
||||
|
||||
// Right now this code is never reached. Need a way to trigger it...
|
||||
console.Write([]byte("Disconnecting UDP...\r\n"))
|
||||
conn.Close()
|
||||
console.Write([]byte("Done.\r\n"))
|
||||
}
|
||||
|
||||
// connect to access point
|
||||
func connectToAP() {
|
||||
console.Write([]byte("Connecting to wifi network...\r\n"))
|
||||
adaptor.SetWifiMode(espat.WifiModeClient)
|
||||
adaptor.ConnectToAP(ssid, pass, 10)
|
||||
console.Write([]byte("Connected.\r\n"))
|
||||
console.Write([]byte(adaptor.GetClientIP()))
|
||||
console.Write([]byte("\r\n"))
|
||||
}
|
||||
Reference in New Issue
Block a user