espat: refactor net and tls interface compatible code into separate sub-packages

Signed-off-by: Ron Evans <ron@hybridgroup.com>
This commit is contained in:
Ron Evans
2019-07-03 13:27:19 +02:00
parent 7e78e2c998
commit 50633f3e86
8 changed files with 135 additions and 115 deletions
+4
View File
@@ -36,6 +36,9 @@ type Device struct {
socketdata []byte socketdata []byte
} }
// ActiveDevice is the currently configured Device in use. There can only be one.
var ActiveDevice *Device
// New returns a new espat driver. Pass in a fully configured UART bus. // New returns a new espat driver. Pass in a fully configured UART bus.
func New(b machine.UART) *Device { func New(b machine.UART) *Device {
return &Device{bus: b, response: make([]byte, 512), socketdata: make([]byte, 0, 1024)} return &Device{bus: b, response: make([]byte, 512), socketdata: make([]byte, 0, 1024)}
@@ -43,6 +46,7 @@ func New(b machine.UART) *Device {
// Configure sets up the device for communication. // Configure sets up the device for communication.
func (d Device) Configure() { func (d Device) Configure() {
ActiveDevice = &d
} }
// Connected checks if there is communication with the ESP8266/ESP32. // Connected checks if there is communication with the ESP8266/ESP32.
+5 -3
View File
@@ -7,6 +7,8 @@ import (
"github.com/eclipse/paho.mqtt.golang/packets" "github.com/eclipse/paho.mqtt.golang/packets"
"tinygo.org/x/drivers/espat" "tinygo.org/x/drivers/espat"
"tinygo.org/x/drivers/espat/net"
"tinygo.org/x/drivers/espat/tls"
) )
// NewClient will create an MQTT v3.1.1 client with all of the options specified // NewClient will create an MQTT v3.1.1 client with all of the options specified
@@ -20,7 +22,7 @@ func NewClient(o *ClientOptions) Client {
type mqttclient struct { type mqttclient struct {
adaptor *espat.Device adaptor *espat.Device
conn espat.Conn conn net.Conn
connected bool connected bool
opts *ClientOptions opts *ClientOptions
mid uint16 mid uint16
@@ -52,13 +54,13 @@ func (c *mqttclient) Connect() Token {
// make connection // make connection
if strings.Contains(c.opts.Servers, "ssl://") { if strings.Contains(c.opts.Servers, "ssl://") {
url := strings.TrimPrefix(c.opts.Servers, "ssl://") url := strings.TrimPrefix(c.opts.Servers, "ssl://")
c.conn, err = c.adaptor.DialTLS("tcp", url, nil) c.conn, err = tls.Dial("tcp", url, nil)
if err != nil { if err != nil {
return &mqtttoken{err: err} return &mqtttoken{err: err}
} }
} else if strings.Contains(c.opts.Servers, "tcp://") { } else if strings.Contains(c.opts.Servers, "tcp://") {
url := strings.TrimPrefix(c.opts.Servers, "tcp://") url := strings.TrimPrefix(c.opts.Servers, "tcp://")
c.conn, err = c.adaptor.Dial("tcp", url) c.conn, err = net.Dial("tcp", url)
if err != nil { if err != nil {
return &mqtttoken{err: err} return &mqtttoken{err: err}
} }
+39 -52
View File
@@ -1,113 +1,93 @@
package espat // package net is intended to provide compatible interfaces with the
// Go standard library's net package.
package net
import ( import (
"errors" "errors"
"strconv" "strconv"
"strings" "strings"
"time" "time"
"tinygo.org/x/drivers/espat"
) )
// DialUDP makes a UDP network connection. raadr is the port that the messages will // 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 // be sent to, and laddr is the port that will be listened to in order to
// receive incoming messages. // receive incoming messages.
func (d *Device) DialUDP(network string, laddr, raddr *UDPAddr) (*UDPSerialConn, error) { func DialUDP(network string, laddr, raddr *UDPAddr) (*UDPSerialConn, error) {
addr := raddr.IP.String() addr := raddr.IP.String()
sendport := strconv.Itoa(raddr.Port) sendport := strconv.Itoa(raddr.Port)
listenport := strconv.Itoa(laddr.Port) listenport := strconv.Itoa(laddr.Port)
// disconnect any old socket // disconnect any old socket
d.DisconnectSocket() espat.ActiveDevice.DisconnectSocket()
// connect new socket // connect new socket
d.ConnectUDPSocket(addr, sendport, listenport) espat.ActiveDevice.ConnectUDPSocket(addr, sendport, listenport)
return &UDPSerialConn{SerialConn: SerialConn{Adaptor: d}, laddr: laddr, raddr: raddr}, nil return &UDPSerialConn{SerialConn: SerialConn{Adaptor: espat.ActiveDevice}, laddr: laddr, raddr: raddr}, nil
} }
// ListenUDP listens for UDP connections on the port listed in laddr. // ListenUDP listens for UDP connections on the port listed in laddr.
func (d *Device) ListenUDP(network string, laddr *UDPAddr) (*UDPSerialConn, error) { func ListenUDP(network string, laddr *UDPAddr) (*UDPSerialConn, error) {
addr := "0" addr := "0"
sendport := "0" sendport := "0"
listenport := strconv.Itoa(laddr.Port) listenport := strconv.Itoa(laddr.Port)
// disconnect any old socket // disconnect any old socket
d.DisconnectSocket() espat.ActiveDevice.DisconnectSocket()
// connect new socket // connect new socket
d.ConnectUDPSocket(addr, sendport, listenport) espat.ActiveDevice.ConnectUDPSocket(addr, sendport, listenport)
return &UDPSerialConn{SerialConn: SerialConn{Adaptor: d}, laddr: laddr}, nil return &UDPSerialConn{SerialConn: SerialConn{Adaptor: espat.ActiveDevice}, laddr: laddr}, nil
} }
// DialTCP makes a TCP network connection. raadr is the port that the messages will // DialTCP makes a TCP 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 // be sent to, and laddr is the port that will be listened to in order to
// receive incoming messages. // receive incoming messages.
func (d *Device) DialTCP(network string, laddr, raddr *TCPAddr) (*TCPSerialConn, error) { func DialTCP(network string, laddr, raddr *TCPAddr) (*TCPSerialConn, error) {
addr := raddr.IP.String() addr := raddr.IP.String()
sendport := strconv.Itoa(raddr.Port) sendport := strconv.Itoa(raddr.Port)
// disconnect any old socket // disconnect any old socket
d.DisconnectSocket() espat.ActiveDevice.DisconnectSocket()
// connect new socket // connect new socket
d.ConnectTCPSocket(addr, sendport) espat.ActiveDevice.ConnectTCPSocket(addr, sendport)
return &TCPSerialConn{SerialConn: SerialConn{Adaptor: d}, laddr: laddr, raddr: raddr}, nil return &TCPSerialConn{SerialConn: SerialConn{Adaptor: espat.ActiveDevice}, laddr: laddr, raddr: raddr}, nil
} }
// Dial connects to the address on the named network. // Dial connects to the address on the named network.
// It tries to provide a mostly compatible interface // It tries to provide a mostly compatible interface
// to net.Dial(). // to net.Dial().
func (d *Device) Dial(network, address string) (Conn, error) { func Dial(network, address string) (Conn, error) {
switch network { switch network {
case "tcp": case "tcp":
raddr, err := d.ResolveTCPAddr(network, address) raddr, err := ResolveTCPAddr(network, address)
if err != nil { if err != nil {
return nil, err return nil, err
} }
c, e := d.DialTCP(network, &TCPAddr{}, raddr) c, e := DialTCP(network, &TCPAddr{}, raddr)
return c.opConn(), e return c.opConn(), e
case "udp": case "udp":
raddr, err := d.ResolveUDPAddr(network, address) raddr, err := ResolveUDPAddr(network, address)
if err != nil { if err != nil {
return nil, err return nil, err
} }
c, e := d.DialUDP(network, &UDPAddr{}, raddr) c, e := DialUDP(network, &UDPAddr{}, raddr)
return c.opConn(), e return c.opConn(), e
default: default:
return nil, errors.New("invalid network for dial") return nil, errors.New("invalid network for dial")
} }
} }
// DialTLS makes a TLS network connection. It tries to provide a mostly compatible interface
// to tls.Dial().
// DialTLS connects to the given network address.
func (d *Device) DialTLS(network, address string, config *TLSConfig) (*TCPSerialConn, error) {
raddr, err := d.ResolveTCPAddr(network, address)
if err != nil {
return nil, err
}
addr := raddr.IP.String()
sendport := strconv.Itoa(raddr.Port)
// disconnect any old socket
d.DisconnectSocket()
// connect new socket
err = d.ConnectSSLSocket(addr, sendport)
if err != nil {
return nil, err
}
return &TCPSerialConn{SerialConn: SerialConn{Adaptor: d}, raddr: raddr}, nil
}
// SerialConn is a loosely net.Conn compatible implementation // SerialConn is a loosely net.Conn compatible implementation
type SerialConn struct { type SerialConn struct {
Adaptor *Device Adaptor *espat.Device
} }
// UDPSerialConn is a loosely net.Conn compatible intended to support // UDPSerialConn is a loosely net.Conn compatible intended to support
@@ -118,6 +98,11 @@ type UDPSerialConn struct {
raddr *UDPAddr raddr *UDPAddr
} }
// NewUDPSerialConn returns a new UDPSerialConn/
func NewUDPSerialConn(c SerialConn, laddr, raddr *UDPAddr) *UDPSerialConn {
return &UDPSerialConn{SerialConn: c, raddr: raddr}
}
// TCPSerialConn is a loosely net.Conn compatible intended to support // TCPSerialConn is a loosely net.Conn compatible intended to support
// TCP over serial. // TCP over serial.
type TCPSerialConn struct { type TCPSerialConn struct {
@@ -126,6 +111,11 @@ type TCPSerialConn struct {
raddr *TCPAddr raddr *TCPAddr
} }
// NewTCPSerialConn returns a new TCPSerialConn/
func NewTCPSerialConn(c SerialConn, laddr, raddr *TCPAddr) *TCPSerialConn {
return &TCPSerialConn{SerialConn: c, raddr: raddr}
}
// Read reads data from the connection. // Read reads data from the connection.
// TODO: implement the full method functionality: // TODO: implement the full method functionality:
// Read can be made to time out and return an Error with Timeout() == true // Read can be made to time out and return an Error with Timeout() == true
@@ -226,14 +216,15 @@ func (c *SerialConn) SetWriteDeadline(t time.Time) error {
// //
// The network must be a TCP network name. // The network must be a TCP network name.
// //
func (d *Device) ResolveTCPAddr(network, address string) (*TCPAddr, error) { func ResolveTCPAddr(network, address string) (*TCPAddr, error) {
// TODO: make sure network is 'tcp' // TODO: make sure network is 'tcp'
// separate domain from port, if any // separate domain from port, if any
r := strings.Split(address, ":") r := strings.Split(address, ":")
ip, err := d.GetDNS(r[0]) addr, err := espat.ActiveDevice.GetDNS(r[0])
if err != nil { if err != nil {
return nil, err return nil, err
} }
ip := IP(addr)
if len(r) > 1 { if len(r) > 1 {
port, e := strconv.Atoi(r[1]) port, e := strconv.Atoi(r[1])
if e != nil { if e != nil {
@@ -248,14 +239,15 @@ func (d *Device) ResolveTCPAddr(network, address string) (*TCPAddr, error) {
// //
// The network must be a UDP network name. // The network must be a UDP network name.
// //
func (d *Device) ResolveUDPAddr(network, address string) (*UDPAddr, error) { func ResolveUDPAddr(network, address string) (*UDPAddr, error) {
// TODO: make sure network is 'udp' // TODO: make sure network is 'udp'
// separate domain from port, if any // separate domain from port, if any
r := strings.Split(address, ":") r := strings.Split(address, ":")
ip, err := d.GetDNS(r[0]) addr, err := espat.ActiveDevice.GetDNS(r[0])
if err != nil { if err != nil {
return nil, err return nil, err
} }
ip := IP(addr)
if len(r) > 1 { if len(r) > 1 {
port, e := strconv.Atoi(r[1]) port, e := strconv.Atoi(r[1])
if e != nil { if e != nil {
@@ -339,11 +331,6 @@ func (ip IP) String() string {
return string(ip) return string(ip)
} }
// TLSConfig is a placeholder for future compatibility with
// tls.Config.
type TLSConfig struct {
}
// Conn is a generic stream-oriented network connection. // Conn is a generic stream-oriented network connection.
// This interface is from the Go standard library. // This interface is from the Go standard library.
type Conn interface { type Conn interface {
+3 -3
View File
@@ -15,14 +15,14 @@ const (
) )
// GetDNS returns the IP address for a domain name. // GetDNS returns the IP address for a domain name.
func (d *Device) GetDNS(domain string) (IP, error) { func (d *Device) GetDNS(domain string) (string, error) {
d.Set(TCPDNSLookup, "\""+domain+"\"") d.Set(TCPDNSLookup, "\""+domain+"\"")
r := strings.Split(string(d.Response(1000)), ":") r := strings.Split(string(d.Response(1000)), ":")
if len(r) != 2 { if len(r) != 2 {
return nil, errors.New("Invalid domain lookup result") return "", errors.New("Invalid domain lookup result")
} }
res := strings.Split(r[1], "\r\n") res := strings.Split(r[1], "\r\n")
return IP(res[0]), nil return res[0], nil
} }
// ConnectTCPSocket creates a new TCP socket connection for the ESP8266/ESP32. // ConnectTCPSocket creates a new TCP socket connection for the ESP8266/ESP32.
+39
View File
@@ -0,0 +1,39 @@
// Package tls is intended to provide a minimal set of compatible interfaces with the
// Go standard library's tls package.
package tls
import (
"strconv"
"tinygo.org/x/drivers/espat"
"tinygo.org/x/drivers/espat/net"
)
// Dial makes a TLS network connection. It tries to provide a mostly compatible interface
// to tls.Dial().
// Dial connects to the given network address.
func Dial(network, address string, config *Config) (*net.TCPSerialConn, error) {
raddr, err := net.ResolveTCPAddr(network, address)
if err != nil {
return nil, err
}
addr := raddr.IP.String()
sendport := strconv.Itoa(raddr.Port)
// disconnect any old socket
espat.ActiveDevice.DisconnectSocket()
// connect new socket
err = espat.ActiveDevice.ConnectSSLSocket(addr, sendport)
if err != nil {
return nil, err
}
return net.NewTCPSerialConn(net.SerialConn{Adaptor: espat.ActiveDevice}, nil, raddr), nil
}
// Config is a placeholder for future compatibility with
// tls.Config.
type Config struct {
}
+17 -23
View File
@@ -11,6 +11,7 @@ import (
"time" "time"
"tinygo.org/x/drivers/espat" "tinygo.org/x/drivers/espat"
"tinygo.org/x/drivers/espat/net"
) )
// change actAsAP to true to act as an access point instead of connecting to one. // change actAsAP to true to act as an access point instead of connecting to one.
@@ -26,8 +27,6 @@ var (
tx = machine.D10 tx = machine.D10
rx = machine.D11 rx = machine.D11
console = machine.UART0
adaptor *espat.Device adaptor *espat.Device
) )
@@ -44,7 +43,7 @@ func main() {
// first check if connected // first check if connected
if adaptor.Connected() { if adaptor.Connected() {
console.Write([]byte("Connected to wifi adaptor.\r\n")) println("Connected to wifi adaptor.")
adaptor.Echo(false) adaptor.Echo(false)
if actAsAP { if actAsAP {
@@ -53,24 +52,22 @@ func main() {
connectToAP() connectToAP()
} }
} else { } else {
console.Write([]byte("\r\n")) println("Unable to connect to wifi adaptor.")
console.Write([]byte("Unable to connect to wifi adaptor.\r\n"))
return return
} }
// now make UDP connection // now make UDP connection
laddr := &espat.UDPAddr{Port: 2222} laddr := &net.UDPAddr{Port: 2222}
console.Write([]byte("Loading UDP listener...\r\n")) println("Loading UDP listener...")
conn, _ := adaptor.ListenUDP("UDP", laddr) conn, _ := net.ListenUDP("UDP", laddr)
console.Write([]byte("Waiting for data...\r\n")) println("Waiting for data...")
data := make([]byte, 50) data := make([]byte, 50)
blink := true blink := true
for { for {
n, _ := conn.Read(data) n, _ := conn.Read(data)
if n > 0 { if n > 0 {
console.Write(data[:n]) println(string(data[:n]))
console.Write([]byte("\r\n"))
conn.Write([]byte("hello back\r\n")) conn.Write([]byte("hello back\r\n"))
} }
blink = !blink blink = !blink
@@ -83,29 +80,26 @@ func main() {
} }
// Right now this code is never reached. Need a way to trigger it... // Right now this code is never reached. Need a way to trigger it...
console.Write([]byte("Disconnecting UDP...\r\n")) println("Disconnecting UDP...")
conn.Close() conn.Close()
console.Write([]byte("Done.\r\n")) println("Done.")
} }
// connect to access point // connect to access point
func connectToAP() { func connectToAP() {
console.Write([]byte("Connecting to wifi network...\r\n")) println("Connecting to wifi network...")
adaptor.SetWifiMode(espat.WifiModeClient) adaptor.SetWifiMode(espat.WifiModeClient)
adaptor.ConnectToAP(ssid, pass, 10) adaptor.ConnectToAP(ssid, pass, 10)
console.Write([]byte("Connected.\r\n")) println("Connected.")
console.Write([]byte(adaptor.GetClientIP())) println(adaptor.GetClientIP())
console.Write([]byte("\r\n"))
} }
// provide access point // provide access point
func provideAP() { func provideAP() {
console.Write([]byte("Starting wifi network as access point '")) println("Starting wifi network as access point:")
console.Write([]byte(ssid)) println(ssid)
console.Write([]byte("'...\r\n"))
adaptor.SetWifiMode(espat.WifiModeAP) adaptor.SetWifiMode(espat.WifiModeAP)
adaptor.SetAPConfig(ssid, pass, 7, espat.WifiAPSecurityWPA2_PSK) adaptor.SetAPConfig(ssid, pass, 7, espat.WifiAPSecurityWPA2_PSK)
console.Write([]byte("Ready.\r\n")) println("Ready.")
console.Write([]byte(adaptor.GetAPIP())) println(adaptor.GetAPIP())
console.Write([]byte("\r\n"))
} }
+14 -17
View File
@@ -11,6 +11,7 @@ import (
"time" "time"
"tinygo.org/x/drivers/espat" "tinygo.org/x/drivers/espat"
"tinygo.org/x/drivers/espat/net"
) )
// access point info // access point info
@@ -26,8 +27,6 @@ var (
tx = machine.D10 tx = machine.D10
rx = machine.D11 rx = machine.D11
console = machine.UART0
adaptor *espat.Device adaptor *espat.Device
) )
@@ -40,44 +39,42 @@ func main() {
// first check if connected // first check if connected
if adaptor.Connected() { if adaptor.Connected() {
console.Write([]byte("Connected to wifi adaptor.\r\n")) println("Connected to wifi adaptor.")
adaptor.Echo(false) adaptor.Echo(false)
connectToAP() connectToAP()
} else { } else {
console.Write([]byte("\r\n")) println("Unable to connect to wifi adaptor.")
console.Write([]byte("Unable to connect to wifi adaptor.\r\n"))
return return
} }
// now make UDP connection // now make UDP connection
ip := espat.ParseIP(hubIP) ip := net.ParseIP(hubIP)
raddr := &espat.UDPAddr{IP: ip, Port: 2222} raddr := &net.UDPAddr{IP: ip, Port: 2222}
laddr := &espat.UDPAddr{Port: 2222} laddr := &net.UDPAddr{Port: 2222}
console.Write([]byte("Dialing UDP connection...\r\n")) println("Dialing UDP connection...")
conn, _ := adaptor.DialUDP("udp", laddr, raddr) conn, _ := net.DialUDP("udp", laddr, raddr)
for { for {
// send data // send data
console.Write([]byte("Sending data...\r\n")) println("Sending data...")
conn.Write([]byte("hello\r\n")) conn.Write([]byte("hello\r\n"))
time.Sleep(1000 * time.Millisecond) time.Sleep(1000 * time.Millisecond)
} }
// Right now this code is never reached. Need a way to trigger it... // Right now this code is never reached. Need a way to trigger it...
console.Write([]byte("Disconnecting UDP...\r\n")) println("Disconnecting UDP...")
conn.Close() conn.Close()
console.Write([]byte("Done.\r\n")) println("Done.")
} }
// connect to access point // connect to access point
func connectToAP() { func connectToAP() {
console.Write([]byte("Connecting to wifi network...\r\n")) println("Connecting to wifi network...")
adaptor.SetWifiMode(espat.WifiModeClient) adaptor.SetWifiMode(espat.WifiModeClient)
adaptor.ConnectToAP(ssid, pass, 10) adaptor.ConnectToAP(ssid, pass, 10)
console.Write([]byte("Connected.\r\n")) println("Connected.")
console.Write([]byte(adaptor.GetClientIP())) println(adaptor.GetClientIP())
console.Write([]byte("\r\n"))
} }
+14 -17
View File
@@ -11,6 +11,7 @@ import (
"time" "time"
"tinygo.org/x/drivers/espat" "tinygo.org/x/drivers/espat"
"tinygo.org/x/drivers/espat/net"
) )
// access point info // access point info
@@ -26,8 +27,6 @@ var (
tx = machine.PA22 tx = machine.PA22
rx = machine.PA23 rx = machine.PA23
console = machine.UART0
adaptor *espat.Device adaptor *espat.Device
) )
@@ -40,44 +39,42 @@ func main() {
// first check if connected // first check if connected
if adaptor.Connected() { if adaptor.Connected() {
console.Write([]byte("Connected to wifi adaptor.\r\n")) println("Connected to wifi adaptor.")
adaptor.Echo(false) adaptor.Echo(false)
connectToAP() connectToAP()
} else { } else {
console.Write([]byte("\r\n")) println("Unable to connect to wifi adaptor.")
console.Write([]byte("Unable to connect to wifi adaptor.\r\n"))
return return
} }
// now make TCP connection // now make TCP connection
ip := espat.ParseIP(serverIP) ip := net.ParseIP(serverIP)
raddr := &espat.TCPAddr{IP: ip, Port: 8080} raddr := &net.TCPAddr{IP: ip, Port: 8080}
laddr := &espat.TCPAddr{Port: 8080} laddr := &net.TCPAddr{Port: 8080}
console.Write([]byte("Dialing TCP connection...\r\n")) println("Dialing TCP connection...")
conn, _ := adaptor.DialTCP("tcp", laddr, raddr) conn, _ := net.DialTCP("tcp", laddr, raddr)
for { for {
// send data // send data
console.Write([]byte("Sending data...\r\n")) println("Sending data...")
conn.Write([]byte("hello\r\n")) conn.Write([]byte("hello\r\n"))
time.Sleep(1000 * time.Millisecond) time.Sleep(1000 * time.Millisecond)
} }
// Right now this code is never reached. Need a way to trigger it... // Right now this code is never reached. Need a way to trigger it...
console.Write([]byte("Disconnecting TCP...\r\n")) println("Disconnecting TCP...")
conn.Close() conn.Close()
console.Write([]byte("Done.\r\n")) println("Done.")
} }
// connect to access point // connect to access point
func connectToAP() { func connectToAP() {
console.Write([]byte("Connecting to wifi network...\r\n")) println("Connecting to wifi network...")
adaptor.SetWifiMode(espat.WifiModeClient) adaptor.SetWifiMode(espat.WifiModeClient)
adaptor.ConnectToAP(ssid, pass, 10) adaptor.ConnectToAP(ssid, pass, 10)
console.Write([]byte("Connected.\r\n")) println("Connected.")
console.Write([]byte(adaptor.GetClientIP())) println(adaptor.GetClientIP())
console.Write([]byte("\r\n"))
} }