From 50633f3e861a2c78525aeb008199b4bd2512eb8a Mon Sep 17 00:00:00 2001 From: Ron Evans Date: Wed, 3 Jul 2019 13:27:19 +0200 Subject: [PATCH] espat: refactor net and tls interface compatible code into separate sub-packages Signed-off-by: Ron Evans --- espat/espat.go | 4 ++ espat/mqtt/mqtt.go | 8 ++- espat/{ => net}/net.go | 91 +++++++++++++------------------ espat/tcp.go | 6 +- espat/tls/tls.go | 39 +++++++++++++ examples/espat/esphub/main.go | 40 ++++++-------- examples/espat/espstation/main.go | 31 +++++------ examples/espat/tcpclient/main.go | 31 +++++------ 8 files changed, 135 insertions(+), 115 deletions(-) rename espat/{ => net}/net.go (82%) create mode 100644 espat/tls/tls.go diff --git a/espat/espat.go b/espat/espat.go index ec09d10..d8b7a50 100644 --- a/espat/espat.go +++ b/espat/espat.go @@ -36,6 +36,9 @@ type Device struct { 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. func New(b machine.UART) *Device { 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. func (d Device) Configure() { + ActiveDevice = &d } // Connected checks if there is communication with the ESP8266/ESP32. diff --git a/espat/mqtt/mqtt.go b/espat/mqtt/mqtt.go index bc16bf3..95cab72 100644 --- a/espat/mqtt/mqtt.go +++ b/espat/mqtt/mqtt.go @@ -7,6 +7,8 @@ import ( "github.com/eclipse/paho.mqtt.golang/packets" "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 @@ -20,7 +22,7 @@ func NewClient(o *ClientOptions) Client { type mqttclient struct { adaptor *espat.Device - conn espat.Conn + conn net.Conn connected bool opts *ClientOptions mid uint16 @@ -52,13 +54,13 @@ func (c *mqttclient) Connect() Token { // make connection if strings.Contains(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 { return &mqtttoken{err: err} } } else if strings.Contains(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 { return &mqtttoken{err: err} } diff --git a/espat/net.go b/espat/net/net.go similarity index 82% rename from espat/net.go rename to espat/net/net.go index 4a5d18a..58b12e7 100644 --- a/espat/net.go +++ b/espat/net/net.go @@ -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 ( "errors" "strconv" "strings" "time" + + "tinygo.org/x/drivers/espat" ) // 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) (*UDPSerialConn, error) { +func DialUDP(network string, laddr, raddr *UDPAddr) (*UDPSerialConn, error) { addr := raddr.IP.String() sendport := strconv.Itoa(raddr.Port) listenport := strconv.Itoa(laddr.Port) // disconnect any old socket - d.DisconnectSocket() + espat.ActiveDevice.DisconnectSocket() // 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. -func (d *Device) ListenUDP(network string, laddr *UDPAddr) (*UDPSerialConn, error) { +func ListenUDP(network string, laddr *UDPAddr) (*UDPSerialConn, error) { addr := "0" sendport := "0" listenport := strconv.Itoa(laddr.Port) // disconnect any old socket - d.DisconnectSocket() + espat.ActiveDevice.DisconnectSocket() // 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 // be sent to, and laddr is the port that will be listened to in order to // 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() sendport := strconv.Itoa(raddr.Port) // disconnect any old socket - d.DisconnectSocket() + espat.ActiveDevice.DisconnectSocket() // 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. // It tries to provide a mostly compatible interface // to net.Dial(). -func (d *Device) Dial(network, address string) (Conn, error) { +func Dial(network, address string) (Conn, error) { switch network { case "tcp": - raddr, err := d.ResolveTCPAddr(network, address) + raddr, err := ResolveTCPAddr(network, address) if err != nil { return nil, err } - c, e := d.DialTCP(network, &TCPAddr{}, raddr) + c, e := DialTCP(network, &TCPAddr{}, raddr) return c.opConn(), e case "udp": - raddr, err := d.ResolveUDPAddr(network, address) + raddr, err := ResolveUDPAddr(network, address) if err != nil { return nil, err } - c, e := d.DialUDP(network, &UDPAddr{}, raddr) + c, e := DialUDP(network, &UDPAddr{}, raddr) return c.opConn(), e default: 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 type SerialConn struct { - Adaptor *Device + Adaptor *espat.Device } // UDPSerialConn is a loosely net.Conn compatible intended to support @@ -118,6 +98,11 @@ type UDPSerialConn struct { 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 // TCP over serial. type TCPSerialConn struct { @@ -126,6 +111,11 @@ type TCPSerialConn struct { 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. // TODO: implement the full method functionality: // 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. // -func (d *Device) ResolveTCPAddr(network, address string) (*TCPAddr, error) { +func ResolveTCPAddr(network, address string) (*TCPAddr, error) { // TODO: make sure network is 'tcp' // separate domain from port, if any r := strings.Split(address, ":") - ip, err := d.GetDNS(r[0]) + addr, err := espat.ActiveDevice.GetDNS(r[0]) if err != nil { return nil, err } + ip := IP(addr) if len(r) > 1 { port, e := strconv.Atoi(r[1]) if e != nil { @@ -248,14 +239,15 @@ func (d *Device) ResolveTCPAddr(network, address string) (*TCPAddr, error) { // // 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' // separate domain from port, if any r := strings.Split(address, ":") - ip, err := d.GetDNS(r[0]) + addr, err := espat.ActiveDevice.GetDNS(r[0]) if err != nil { return nil, err } + ip := IP(addr) if len(r) > 1 { port, e := strconv.Atoi(r[1]) if e != nil { @@ -339,11 +331,6 @@ func (ip IP) String() string { return string(ip) } -// TLSConfig is a placeholder for future compatibility with -// tls.Config. -type TLSConfig struct { -} - // Conn is a generic stream-oriented network connection. // This interface is from the Go standard library. type Conn interface { diff --git a/espat/tcp.go b/espat/tcp.go index 50ae75e..463c895 100644 --- a/espat/tcp.go +++ b/espat/tcp.go @@ -15,14 +15,14 @@ const ( ) // 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+"\"") r := strings.Split(string(d.Response(1000)), ":") 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") - return IP(res[0]), nil + return res[0], nil } // ConnectTCPSocket creates a new TCP socket connection for the ESP8266/ESP32. diff --git a/espat/tls/tls.go b/espat/tls/tls.go new file mode 100644 index 0000000..3abd27f --- /dev/null +++ b/espat/tls/tls.go @@ -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 { +} diff --git a/examples/espat/esphub/main.go b/examples/espat/esphub/main.go index 7ed45bb..3f0c247 100644 --- a/examples/espat/esphub/main.go +++ b/examples/espat/esphub/main.go @@ -11,6 +11,7 @@ import ( "time" "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. @@ -26,8 +27,6 @@ var ( tx = machine.D10 rx = machine.D11 - console = machine.UART0 - adaptor *espat.Device ) @@ -44,7 +43,7 @@ func main() { // first check if connected if adaptor.Connected() { - console.Write([]byte("Connected to wifi adaptor.\r\n")) + println("Connected to wifi adaptor.") adaptor.Echo(false) if actAsAP { @@ -53,24 +52,22 @@ func main() { connectToAP() } } else { - console.Write([]byte("\r\n")) - console.Write([]byte("Unable to connect to wifi adaptor.\r\n")) + println("Unable to connect to wifi adaptor.") return } // now make UDP connection - laddr := &espat.UDPAddr{Port: 2222} - console.Write([]byte("Loading UDP listener...\r\n")) - conn, _ := adaptor.ListenUDP("UDP", laddr) + laddr := &net.UDPAddr{Port: 2222} + println("Loading UDP listener...") + conn, _ := net.ListenUDP("UDP", laddr) - console.Write([]byte("Waiting for data...\r\n")) + println("Waiting for data...") data := make([]byte, 50) blink := true for { n, _ := conn.Read(data) if n > 0 { - console.Write(data[:n]) - console.Write([]byte("\r\n")) + println(string(data[:n])) conn.Write([]byte("hello back\r\n")) } blink = !blink @@ -83,29 +80,26 @@ func main() { } // 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() - console.Write([]byte("Done.\r\n")) + println("Done.") } // connect to access point func connectToAP() { - console.Write([]byte("Connecting to wifi network...\r\n")) + println("Connecting to wifi network...") 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")) + println("Connected.") + println(adaptor.GetClientIP()) } // provide access point func provideAP() { - console.Write([]byte("Starting wifi network as access point '")) - console.Write([]byte(ssid)) - console.Write([]byte("'...\r\n")) + println("Starting wifi network as access point:") + println(ssid) 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")) + println("Ready.") + println(adaptor.GetAPIP()) } diff --git a/examples/espat/espstation/main.go b/examples/espat/espstation/main.go index 1597756..b7674cf 100644 --- a/examples/espat/espstation/main.go +++ b/examples/espat/espstation/main.go @@ -11,6 +11,7 @@ import ( "time" "tinygo.org/x/drivers/espat" + "tinygo.org/x/drivers/espat/net" ) // access point info @@ -26,8 +27,6 @@ var ( tx = machine.D10 rx = machine.D11 - console = machine.UART0 - adaptor *espat.Device ) @@ -40,44 +39,42 @@ func main() { // first check if connected if adaptor.Connected() { - console.Write([]byte("Connected to wifi adaptor.\r\n")) + println("Connected to wifi adaptor.") adaptor.Echo(false) connectToAP() } else { - console.Write([]byte("\r\n")) - console.Write([]byte("Unable to connect to wifi adaptor.\r\n")) + println("Unable to connect to wifi adaptor.") return } // now make UDP connection - ip := espat.ParseIP(hubIP) - raddr := &espat.UDPAddr{IP: ip, Port: 2222} - laddr := &espat.UDPAddr{Port: 2222} + ip := net.ParseIP(hubIP) + raddr := &net.UDPAddr{IP: ip, Port: 2222} + laddr := &net.UDPAddr{Port: 2222} - console.Write([]byte("Dialing UDP connection...\r\n")) - conn, _ := adaptor.DialUDP("udp", laddr, raddr) + println("Dialing UDP connection...") + conn, _ := net.DialUDP("udp", laddr, raddr) for { // send data - console.Write([]byte("Sending data...\r\n")) + println("Sending data...") 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")) + println("Disconnecting UDP...") conn.Close() - console.Write([]byte("Done.\r\n")) + println("Done.") } // connect to access point func connectToAP() { - console.Write([]byte("Connecting to wifi network...\r\n")) + println("Connecting to wifi network...") 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")) + println("Connected.") + println(adaptor.GetClientIP()) } diff --git a/examples/espat/tcpclient/main.go b/examples/espat/tcpclient/main.go index 0724b7f..3332ff6 100644 --- a/examples/espat/tcpclient/main.go +++ b/examples/espat/tcpclient/main.go @@ -11,6 +11,7 @@ import ( "time" "tinygo.org/x/drivers/espat" + "tinygo.org/x/drivers/espat/net" ) // access point info @@ -26,8 +27,6 @@ var ( tx = machine.PA22 rx = machine.PA23 - console = machine.UART0 - adaptor *espat.Device ) @@ -40,44 +39,42 @@ func main() { // first check if connected if adaptor.Connected() { - console.Write([]byte("Connected to wifi adaptor.\r\n")) + println("Connected to wifi adaptor.") adaptor.Echo(false) connectToAP() } else { - console.Write([]byte("\r\n")) - console.Write([]byte("Unable to connect to wifi adaptor.\r\n")) + println("Unable to connect to wifi adaptor.") return } // now make TCP connection - ip := espat.ParseIP(serverIP) - raddr := &espat.TCPAddr{IP: ip, Port: 8080} - laddr := &espat.TCPAddr{Port: 8080} + ip := net.ParseIP(serverIP) + raddr := &net.TCPAddr{IP: ip, Port: 8080} + laddr := &net.TCPAddr{Port: 8080} - console.Write([]byte("Dialing TCP connection...\r\n")) - conn, _ := adaptor.DialTCP("tcp", laddr, raddr) + println("Dialing TCP connection...") + conn, _ := net.DialTCP("tcp", laddr, raddr) for { // send data - console.Write([]byte("Sending data...\r\n")) + println("Sending data...") 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 TCP...\r\n")) + println("Disconnecting TCP...") conn.Close() - console.Write([]byte("Done.\r\n")) + println("Done.") } // connect to access point func connectToAP() { - console.Write([]byte("Connecting to wifi network...\r\n")) + println("Connecting to wifi network...") 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")) + println("Connected.") + println(adaptor.GetClientIP()) }