Skip to content

Commit c7d9f94

Browse files
authored
Add go-idiomatic ESP-NOW wrappers (#44)
* Add ESP-NOW wrappers * address compile error * add go-idiomatic io.PacketConn interface for ESPNow + a streaming adapter * bound error range * add examples/espnow/main.go
1 parent 6a1cb8c commit c7d9f94

6 files changed

Lines changed: 867 additions & 12 deletions

File tree

doc.go

Lines changed: 67 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,68 @@
1-
// Package espradio provides support for the ESP32-S2 and ESP32-S3 microcontrollers.
2-
// It is based on the ESP-IDF framework and provides access to Wi-Fi and Bluetooth.
3-
// The package is designed to be used with the TinyGo compiler.
1+
// Package espradio provides wireless support for Espressif microcontrollers
2+
// when used with the TinyGo compiler.
3+
//
4+
// The package wraps the Espressif Wi-Fi blobs and exposes both lower-level
5+
// 1:1 bindings and higher-level Go APIs for common use cases such as:
6+
//
7+
// - station and soft-AP Wi-Fi
8+
// - scanning
9+
// - raw Ethernet/netdev integration
10+
// - ESP-NOW datagram communication
11+
//
12+
// ESP-NOW can be used directly through the thin wrapper functions, or through
13+
// the higher-level managed API returned by NewESPNow. The managed API maps each
14+
// remote peer to a Peer that implements net.PacketConn.
15+
//
16+
// For applications that prefer a stream-like API over packet-oriented reads and
17+
// writes, Peer can also be wrapped with PeerStream via peer.Stream().
18+
//
19+
// Example usage:
20+
//
21+
// func main() {
22+
// if err := espradio.Enable(espradio.Config{}); err != nil {
23+
// panic(err)
24+
// }
25+
// if err := espradio.Start(); err != nil {
26+
// panic(err)
27+
// }
28+
//
29+
// now, err := espradio.NewESPNow(espradio.ESPNowConfig{})
30+
// if err != nil {
31+
// panic(err)
32+
// }
33+
// defer now.Close()
34+
//
35+
// peer, err := now.AddPeer(espradio.PeerConfig{
36+
// Address: espradio.ESPNowAddr{0x24, 0x6f, 0x28, 0xaa, 0xbb, 0xcc},
37+
// If: espradio.WiFiInterfaceSTA,
38+
// })
39+
// if err != nil {
40+
// panic(err)
41+
// }
42+
// defer peer.Close()
43+
//
44+
// if _, err := peer.WriteTo([]byte("hello"), nil); err != nil {
45+
// panic(err)
46+
// }
47+
//
48+
// buf := make([]byte, espradio.ESPNowMaxDataLength)
49+
// n, addr, err := peer.ReadFrom(buf)
50+
// if err != nil {
51+
// panic(err)
52+
// }
53+
// println("received", n, "bytes from", addr.String())
54+
//
55+
// stream := peer.Stream()
56+
// if _, err := stream.Write([]byte("streamed payload")); err != nil {
57+
// panic(err)
58+
// }
59+
//
60+
// broadcast, err := now.Broadcast()
61+
// if err != nil {
62+
// panic(err)
63+
// }
64+
// if _, err := broadcast.WriteTo([]byte("announcement"), nil); err != nil {
65+
// panic(err)
66+
// }
67+
// }
468
package espradio // import "tinygo.org/x/espradio"

error.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ func (e Error) Error() string {
2121
return "espradio: unknown flash error"
2222
case e >= C.ESP_ERR_MESH_BASE:
2323
return "espradio: unknown mesh error"
24-
case e >= C.ESP_ERR_ESPNOW_BASE:
24+
case e >= C.ESP_ERR_ESPNOW_BASE && e <= C.ESP_ERR_ESPNOW_CHAN:
2525
switch e {
2626
case C.ESP_ERR_ESPNOW_NOT_INIT:
2727
return "espradio: esp-now not initialized"

espnow.go

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,10 @@ type ESPNowSendReport struct {
7373
}
7474

7575
var (
76-
espNowMu sync.RWMutex
77-
espNowRecvHandler func(ESPNowReceive)
78-
espNowSendHandler func(ESPNowSendReport)
76+
espNowMu sync.RWMutex
77+
espNowRecvHandler func(ESPNowReceive)
78+
espNowSendHandler func(ESPNowSendReport)
79+
activeManagedESPNow *ESPNow
7980
)
8081

8182
// ESPNowInit initializes the ESP-NOW subsystem and registers callback trampolines.
@@ -239,8 +240,9 @@ func copyMAC(ptr *C.uint8_t) [ESPNowAddressLength]byte {
239240
func espradio_on_esp_now_recv(srcAddr, destAddr *C.uint8_t, rssi C.int, channel, secondaryChannel C.uint8_t, noiseFloor C.int, timestamp C.uint32_t, data *C.uint8_t, dataLen C.int) {
240241
espNowMu.RLock()
241242
handler := espNowRecvHandler
243+
manager := activeManagedESPNow
242244
espNowMu.RUnlock()
243-
if handler == nil {
245+
if handler == nil && manager == nil {
244246
return
245247
}
246248

@@ -256,24 +258,36 @@ func espradio_on_esp_now_recv(srcAddr, destAddr *C.uint8_t, rssi C.int, channel,
256258
if data != nil && dataLen > 0 {
257259
event.Data = C.GoBytes(unsafe.Pointer(data), dataLen)
258260
}
259-
handler(event)
261+
if handler != nil {
262+
handler(event)
263+
}
264+
if manager != nil {
265+
manager.handleReceive(event)
266+
}
260267
}
261268

262269
//export espradio_on_esp_now_send
263270
func espradio_on_esp_now_send(destAddr, srcAddr *C.uint8_t, ifidx C.wifi_interface_t, rate C.wifi_phy_rate_t, txStatus C.wifi_tx_status_t, status C.esp_now_send_status_t) {
264271
espNowMu.RLock()
265272
handler := espNowSendHandler
273+
manager := activeManagedESPNow
266274
espNowMu.RUnlock()
267-
if handler == nil {
275+
if handler == nil && manager == nil {
268276
return
269277
}
270278

271-
handler(ESPNowSendReport{
279+
report := ESPNowSendReport{
272280
DestinationAddress: copyMAC(destAddr),
273281
SourceAddress: copyMAC(srcAddr),
274282
If: WiFiInterface(ifidx),
275283
Rate: uint32(rate),
276284
TxStatus: ESPNowSendStatus(txStatus),
277285
Status: ESPNowSendStatus(status),
278-
})
286+
}
287+
if handler != nil {
288+
handler(report)
289+
}
290+
if manager != nil {
291+
manager.handleSend(report)
292+
}
279293
}

0 commit comments

Comments
 (0)