tinygo.org/x/drivers@v0.27.1-0.20240509133757-7dbca2a54349/espat/espat.go (about) 1 // Package espat implements TCP/UDP wireless communication over serial 2 // with a separate ESP8266 or ESP32 board using the Espressif AT command set 3 // across a UART interface. 4 // 5 // In order to use this driver, the ESP8266/ESP32 must be flashed with firmware 6 // supporting the AT command set. Many ESP8266/ESP32 chips already have this firmware 7 // installed by default. You will need to install this firmware if you have an 8 // ESP8266 that has been flashed with NodeMCU (Lua) or Arduino firmware. 9 // 10 // AT Command Core repository: 11 // https://github.com/espressif/esp32-at 12 // 13 // Datasheet: 14 // https://www.espressif.com/sites/default/files/documentation/0a-esp8266ex_datasheet_en.pdf 15 // 16 // AT command set: 17 // https://www.espressif.com/sites/default/files/documentation/4a-esp8266_at_instruction_set_en.pdf 18 // 19 // 02/2023 sfeldma@gmail.com Heavily modified to use netdev interface 20 21 package espat // import "tinygo.org/x/drivers/espat" 22 23 import ( 24 "errors" 25 "fmt" 26 "machine" 27 "net" 28 "net/netip" 29 "strconv" 30 "strings" 31 "sync" 32 "time" 33 34 "tinygo.org/x/drivers/netdev" 35 "tinygo.org/x/drivers/netlink" 36 ) 37 38 type Config struct { 39 // UART config 40 Uart *machine.UART 41 Tx machine.Pin 42 Rx machine.Pin 43 } 44 45 type socket struct { 46 inUse bool 47 protocol int 48 laddr netip.AddrPort 49 } 50 51 type Device struct { 52 cfg *Config 53 uart *machine.UART 54 // command responses that come back from the ESP8266/ESP32 55 response []byte 56 // data received from a TCP/UDP connection forwarded by the ESP8266/ESP32 57 data []byte 58 socket socket 59 mu sync.Mutex 60 } 61 62 func NewDevice(cfg *Config) *Device { 63 return &Device{ 64 cfg: cfg, 65 response: make([]byte, 1500), 66 data: make([]byte, 0, 1500), 67 } 68 } 69 70 func (d *Device) NetConnect(params *netlink.ConnectParams) error { 71 72 if len(params.Ssid) == 0 { 73 return netlink.ErrMissingSSID 74 } 75 76 d.uart = d.cfg.Uart 77 d.uart.Configure(machine.UARTConfig{TX: d.cfg.Tx, RX: d.cfg.Rx}) 78 79 // Connect to ESP8266/ESP32 80 fmt.Printf("Connecting to device...") 81 82 for i := 0; i < 5; i++ { 83 if d.Connected() { 84 break 85 } 86 time.Sleep(1 * time.Second) 87 } 88 89 if !d.Connected() { 90 fmt.Printf("FAILED\r\n") 91 return netlink.ErrConnectFailed 92 } 93 94 fmt.Printf("CONNECTED\r\n") 95 96 // Connect to Wifi AP 97 fmt.Printf("Connecting to Wifi SSID '%s'...", params.Ssid) 98 99 d.SetWifiMode(WifiModeClient) 100 101 err := d.ConnectToAP(params.Ssid, params.Passphrase, 10 /* secs */) 102 if err != nil { 103 fmt.Printf("FAILED\r\n") 104 return err 105 } 106 107 fmt.Printf("CONNECTED\r\n") 108 109 ip, err := d.Addr() 110 if err != nil { 111 return err 112 } 113 fmt.Printf("DHCP-assigned IP: %s\r\n", ip) 114 fmt.Printf("\r\n") 115 116 return nil 117 } 118 119 func (d *Device) NetDisconnect() { 120 d.DisconnectFromAP() 121 fmt.Printf("\r\nDisconnected from Wifi\r\n\r\n") 122 } 123 124 func (d *Device) NetNotify(cb func(netlink.Event)) { 125 // Not supported 126 } 127 128 func (d *Device) GetHostByName(name string) (netip.Addr, error) { 129 ip, err := d.GetDNS(name) 130 if err != nil { 131 return netip.Addr{}, err 132 } 133 return netip.ParseAddr(ip) 134 } 135 136 func (d *Device) GetHardwareAddr() (net.HardwareAddr, error) { 137 return net.HardwareAddr{}, netlink.ErrNotSupported 138 } 139 140 func (d *Device) Addr() (netip.Addr, error) { 141 resp, err := d.GetClientIP() 142 if err != nil { 143 return netip.Addr{}, err 144 } 145 prefix := "+CIPSTA:ip:" 146 for _, line := range strings.Split(resp, "\n") { 147 if ok := strings.HasPrefix(line, prefix); ok { 148 ip := line[len(prefix)+1 : len(line)-2] 149 return netip.ParseAddr(ip) 150 } 151 } 152 return netip.Addr{}, fmt.Errorf("Error getting IP address") 153 } 154 155 func (d *Device) Socket(domain int, stype int, protocol int) (int, error) { 156 157 switch domain { 158 case netdev.AF_INET: 159 default: 160 return -1, netdev.ErrFamilyNotSupported 161 } 162 163 switch { 164 case protocol == netdev.IPPROTO_TCP && stype == netdev.SOCK_STREAM: 165 case protocol == netdev.IPPROTO_TLS && stype == netdev.SOCK_STREAM: 166 case protocol == netdev.IPPROTO_UDP && stype == netdev.SOCK_DGRAM: 167 default: 168 return -1, netdev.ErrProtocolNotSupported 169 } 170 171 // Only supporting single connection mode, so only one socket at a time 172 if d.socket.inUse { 173 return -1, netdev.ErrNoMoreSockets 174 } 175 d.socket.inUse = true 176 d.socket.protocol = protocol 177 178 return 0, nil 179 } 180 181 func (d *Device) Bind(sockfd int, ip netip.AddrPort) error { 182 d.socket.laddr = ip 183 return nil 184 } 185 186 func (d *Device) Connect(sockfd int, host string, ip netip.AddrPort) error { 187 var err error 188 var addr = ip.Addr().String() 189 var rport = strconv.Itoa(int(ip.Port())) 190 var lport = strconv.Itoa(int(d.socket.laddr.Port())) 191 192 switch d.socket.protocol { 193 case netdev.IPPROTO_TCP: 194 err = d.ConnectTCPSocket(addr, rport) 195 case netdev.IPPROTO_UDP: 196 err = d.ConnectUDPSocket(addr, rport, lport) 197 case netdev.IPPROTO_TLS: 198 err = d.ConnectSSLSocket(host, rport) 199 } 200 201 if err != nil { 202 if host == "" { 203 return fmt.Errorf("Connect to %s timed out", ip) 204 } else { 205 return fmt.Errorf("Connect to %s:%d timed out", host, ip.Port()) 206 } 207 } 208 209 return nil 210 } 211 212 func (d *Device) Listen(sockfd int, backlog int) error { 213 switch d.socket.protocol { 214 case netdev.IPPROTO_UDP: 215 default: 216 return netdev.ErrProtocolNotSupported 217 } 218 return nil 219 } 220 221 func (d *Device) Accept(sockfd int) (int, netip.AddrPort, error) { 222 return -1, netip.AddrPort{}, netdev.ErrNotSupported 223 } 224 225 func (d *Device) sendChunk(sockfd int, buf []byte, deadline time.Time) (int, error) { 226 // Check if we've timed out 227 if !deadline.IsZero() { 228 if time.Now().After(deadline) { 229 return -1, netdev.ErrTimeout 230 } 231 } 232 err := d.StartSocketSend(len(buf)) 233 if err != nil { 234 return -1, err 235 } 236 n, err := d.Write(buf) 237 if err != nil { 238 return -1, err 239 } 240 _, err = d.Response(1000) 241 if err != nil { 242 return -1, err 243 } 244 return n, err 245 } 246 247 func (d *Device) Send(sockfd int, buf []byte, flags int, deadline time.Time) (int, error) { 248 249 d.mu.Lock() 250 defer d.mu.Unlock() 251 252 // Break large bufs into chunks so we don't overrun the hw queue 253 254 chunkSize := 1436 255 for i := 0; i < len(buf); i += chunkSize { 256 end := i + chunkSize 257 if end > len(buf) { 258 end = len(buf) 259 } 260 _, err := d.sendChunk(sockfd, buf[i:end], deadline) 261 if err != nil { 262 return -1, err 263 } 264 } 265 266 return len(buf), nil 267 } 268 269 func (d *Device) Recv(sockfd int, buf []byte, flags int, deadline time.Time) (int, error) { 270 271 d.mu.Lock() 272 defer d.mu.Unlock() 273 274 var length = len(buf) 275 276 // Limit length read size to chunk large read requests 277 if length > 1436 { 278 length = 1436 279 } 280 281 for { 282 // Check if we've timed out 283 if !deadline.IsZero() { 284 if time.Now().After(deadline) { 285 return -1, netdev.ErrTimeout 286 } 287 } 288 289 n, err := d.ReadSocket(buf[:length]) 290 if err != nil { 291 return -1, err 292 } 293 if n == 0 { 294 d.mu.Unlock() 295 time.Sleep(100 * time.Millisecond) 296 d.mu.Lock() 297 continue 298 } 299 300 return n, nil 301 } 302 } 303 304 func (d *Device) Close(sockfd int) error { 305 d.mu.Lock() 306 defer d.mu.Unlock() 307 308 d.socket.inUse = false 309 return d.DisconnectSocket() 310 } 311 312 func (d *Device) SetSockOpt(sockfd int, level int, opt int, value interface{}) error { 313 return netdev.ErrNotSupported 314 } 315 316 // Connected checks if there is communication with the ESP8266/ESP32. 317 func (d *Device) Connected() bool { 318 d.Execute(Test) 319 320 // handle response here, should include "OK" 321 _, err := d.Response(1000) 322 if err != nil { 323 return false 324 } 325 return true 326 } 327 328 // Write raw bytes to the UART. 329 func (d *Device) Write(b []byte) (n int, err error) { 330 return d.uart.Write(b) 331 } 332 333 // Read raw bytes from the UART. 334 func (d *Device) Read(b []byte) (n int, err error) { 335 return d.uart.Read(b) 336 } 337 338 // how long in milliseconds to pause after sending AT commands 339 const pause = 300 340 341 // Execute sends an AT command to the ESP8266/ESP32. 342 func (d Device) Execute(cmd string) error { 343 _, err := d.Write([]byte("AT" + cmd + "\r\n")) 344 return err 345 } 346 347 // Query sends an AT command to the ESP8266/ESP32 that returns the 348 // current value for some configuration parameter. 349 func (d Device) Query(cmd string) (string, error) { 350 _, err := d.Write([]byte("AT" + cmd + "?\r\n")) 351 return "", err 352 } 353 354 // Set sends an AT command with params to the ESP8266/ESP32 for a 355 // configuration value to be set. 356 func (d Device) Set(cmd, params string) error { 357 _, err := d.Write([]byte("AT" + cmd + "=" + params + "\r\n")) 358 return err 359 } 360 361 // Version returns the ESP8266/ESP32 firmware version info. 362 func (d Device) Version() []byte { 363 d.Execute(Version) 364 r, err := d.Response(2000) 365 if err != nil { 366 //return []byte("unknown") 367 return []byte(err.Error()) 368 } 369 return r 370 } 371 372 // Echo sets the ESP8266/ESP32 echo setting. 373 func (d Device) Echo(set bool) { 374 if set { 375 d.Execute(EchoConfigOn) 376 } else { 377 d.Execute(EchoConfigOff) 378 } 379 // TODO: check for success 380 d.Response(100) 381 } 382 383 // Reset restarts the ESP8266/ESP32 firmware. Due to how the baud rate changes, 384 // this messes up communication with the ESP8266/ESP32 module. So make sure you know 385 // what you are doing when you call this. 386 func (d Device) Reset() { 387 d.Execute(Restart) 388 d.Response(100) 389 } 390 391 // ReadSocket returns the data that has already been read in from the responses. 392 func (d *Device) ReadSocket(b []byte) (n int, err error) { 393 // make sure no data in buffer 394 d.Response(300) 395 396 count := len(b) 397 if len(b) >= len(d.data) { 398 // copy it all, then clear socket data 399 count = len(d.data) 400 copy(b, d.data[:count]) 401 d.data = d.data[:0] 402 } else { 403 // copy all we can, then keep the remaining socket data around 404 copy(b, d.data[:count]) 405 copy(d.data, d.data[count:]) 406 d.data = d.data[:len(d.data)-count] 407 } 408 409 return count, nil 410 } 411 412 // Response gets the next response bytes from the ESP8266/ESP32. 413 // The call will retry for up to timeout milliseconds before returning nothing. 414 func (d *Device) Response(timeout int) ([]byte, error) { 415 // read data 416 var size int 417 var start, end int 418 pause := 100 // pause to wait for 100 ms 419 retries := timeout / pause 420 421 for { 422 size = d.uart.Buffered() 423 424 if size > 0 { 425 end += size 426 d.uart.Read(d.response[start:end]) 427 428 // if "+IPD" then read socket data 429 if strings.Contains(string(d.response[:end]), "+IPD") { 430 // handle socket data 431 return nil, d.parseIPD(end) 432 } 433 434 // if "OK" then the command worked 435 if strings.Contains(string(d.response[:end]), "OK") { 436 return d.response[start:end], nil 437 } 438 439 // if "Error" then the command failed 440 if strings.Contains(string(d.response[:end]), "ERROR") { 441 return d.response[start:end], errors.New("response error:" + string(d.response[start:end])) 442 } 443 444 // if anything else, then keep reading data in? 445 start = end 446 } 447 448 // wait longer? 449 retries-- 450 if retries == 0 { 451 return nil, errors.New("response timeout error:" + string(d.response[start:end])) 452 } 453 454 time.Sleep(time.Duration(pause) * time.Millisecond) 455 } 456 } 457 458 func (d *Device) parseIPD(end int) error { 459 // find the "+IPD," to get length 460 s := strings.Index(string(d.response[:end]), "+IPD,") 461 462 // find the ":" 463 e := strings.Index(string(d.response[:end]), ":") 464 465 // find the data length 466 val := string(d.response[s+5 : e]) 467 468 // TODO: verify count 469 v, err := strconv.Atoi(val) 470 if err != nil { 471 // not expected data here. what to do? 472 return err 473 } 474 475 // load up the socket data 476 //d.data = append(d.data, d.response[e+1:end]...) 477 d.data = append(d.data, d.response[e+1:e+1+v]...) 478 return nil 479 } 480 481 // IsSocketDataAvailable returns of there is socket data available 482 func (d *Device) IsSocketDataAvailable() bool { 483 return len(d.data) > 0 || d.uart.Buffered() > 0 484 }