Skip to content

Commit 4a80a4a

Browse files
authored
Merge pull request #3089 from jedgarpark/espnow-basics
first commit esp-now basics
2 parents e44ae35 + 43d9f48 commit 4a80a4a

File tree

1 file changed

+157
-0
lines changed

1 file changed

+157
-0
lines changed

ESP-NOW_basics/espnow_transceiver.py

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
# SPDX-FileCopyrightText: John Park for Adafruit 2025
2+
# SPDX-License-Identifier: MIT
3+
"""
4+
ESP-NOW transciever demo for ESP32-S2/S3 TFT Feather boards with display
5+
"""
6+
import time
7+
import wifi
8+
import espnow
9+
import board
10+
import digitalio
11+
import displayio
12+
import terminalio
13+
from adafruit_display_text import label
14+
from adafruit_display_shapes.rect import Rect
15+
16+
17+
# Setup the display (using built-in display like your example)
18+
display = board.DISPLAY
19+
group = displayio.Group()
20+
21+
# Create background rectangles like your example
22+
background_rect = Rect(0, 0, display.width, display.height, fill=000000)
23+
group.append(background_rect)
24+
25+
# Set up a button on pin D0/BOOT
26+
button = digitalio.DigitalInOut(board.BUTTON)
27+
button.direction = digitalio.Direction.INPUT
28+
button.pull = digitalio.Pull.UP
29+
30+
# CONFIGURATION: Set this for each device
31+
DEVICE_ID = "board_A" # Options: "board_A", "board_B", "board_C", "board_D"
32+
33+
# Channel switching hack
34+
wifi.radio.start_ap(" ", "", channel=6, max_connections=0)
35+
wifi.radio.stop_ap()
36+
37+
def format_mac(mac_bytes):
38+
return ':'.join(f'{b:02x}' for b in mac_bytes)
39+
40+
def get_my_mac():
41+
return format_mac(wifi.radio.mac_address)
42+
43+
# Initialize ESP-NOW
44+
e = espnow.ESPNow()
45+
peer = espnow.Peer(mac=b'\xff\xff\xff\xff\xff\xff', channel=6)
46+
e.peers.append(peer)
47+
48+
my_mac = get_my_mac()
49+
print(f"{DEVICE_ID} board starting - MAC: {my_mac}")
50+
51+
# Colors
52+
TOMATO = 0xFF6347
53+
WHITE = 0xFFFFFF
54+
GREEN = 0x00FF00
55+
BLUE = 0x0080FF
56+
RED = 0xFF0000
57+
58+
# Sender colors mapping
59+
SENDER_COLORS = {
60+
"board_A": 0x32FF32, # Lime green
61+
"board_B": 0x00FFFF, # Cyan
62+
"board_C": 0xC8A2C8, # Lilac
63+
"board_D": 0xFFFFFF # White
64+
}
65+
66+
def get_sender_color(rx_message):
67+
"""Extract sender ID from message and return corresponding color"""
68+
for board_id in SENDER_COLORS:
69+
if rx_message.startswith(board_id):
70+
return SENDER_COLORS[board_id]
71+
return WHITE # Default to white if sender not recognized
72+
73+
# Create text labels using anchor positioning like your example
74+
title_label = label.Label(terminalio.FONT, text=f"ESP-NOW {DEVICE_ID}", color=TOMATO,
75+
scale=2, anchor_point=(0, 0), anchored_position=(5, 5))
76+
group.append(title_label)
77+
78+
status_label = label.Label(terminalio.FONT, text="press D0 to send", color=TOMATO,
79+
scale=2, anchor_point=(0, 0), anchored_position=(5, 35))
80+
group.append(status_label)
81+
82+
sent_label = label.Label(terminalio.FONT, text="TX'd: --", color=TOMATO,
83+
scale=2, anchor_point=(0, 0), anchored_position=(5, 65))
84+
group.append(sent_label)
85+
86+
# Create a second label for the colored counter part
87+
sent_counter_label = label.Label(terminalio.FONT, text="",color=SENDER_COLORS.get(DEVICE_ID, WHITE),
88+
scale=2, anchor_point=(0, 0), anchored_position=(80, 65))
89+
group.append(sent_counter_label)
90+
91+
received_label = label.Label(terminalio.FONT, text="RX'd: --", color=TOMATO,
92+
scale=2, anchor_point=(0, 0), anchored_position=(5, 95))
93+
group.append(received_label)
94+
95+
# Create a second label for the colored message part
96+
received_message_label = label.Label(terminalio.FONT, text="", color=WHITE,
97+
scale=2, anchor_point=(0, 0), anchored_position=(80, 95))
98+
group.append(received_message_label)
99+
100+
# Show the display
101+
display.root_group = group
102+
103+
# Button debouncing
104+
last_button_time = 0
105+
button_debounce = 0.2 # 200ms debounce
106+
message_count = 0
107+
108+
while True:
109+
current_time = time.monotonic()
110+
111+
# Check for button press (button is active low due to pull-up)
112+
if not button.value and (current_time - last_button_time > button_debounce):
113+
message_count += 1
114+
message = f"{DEVICE_ID} {message_count}"
115+
116+
try:
117+
e.send(message, peer)
118+
print(f"Sent: {message}")
119+
120+
# Update display
121+
status_label.text = "...>"
122+
status_label.color = TOMATO
123+
sent_label.text = "TX'd: " # Keep this tomato colored
124+
sent_counter_label.text = str(message_count) # Color with sender color
125+
sent_counter_label.color = SENDER_COLORS.get(DEVICE_ID, WHITE)
126+
127+
except Exception as ex: # pylint: disable=broad-except
128+
print(f"Send failed: {ex}")
129+
status_label.text = "xxx"
130+
status_label.color = RED
131+
132+
last_button_time = current_time
133+
134+
# Reset status after a moment
135+
if current_time - last_button_time > 0.2:
136+
status_label.text = "press D0 to send"
137+
status_label.color = TOMATO
138+
139+
# Check for incoming packets
140+
if e:
141+
packet = e.read()
142+
if packet:
143+
sender_mac = format_mac(packet.mac)
144+
145+
if sender_mac != my_mac:
146+
message = packet.msg.decode('utf-8')
147+
print(f"received: {message}")
148+
149+
# Update display with sender color
150+
status_label.text = "<..."
151+
status_label.color = TOMATO
152+
sender_color = get_sender_color(message)
153+
received_label.text = "RX'd: " # Keep this tomato colored
154+
received_message_label.text = message[-12:] # Color just the message
155+
received_message_label.color = sender_color
156+
157+
time.sleep(0.05) # Light polling

0 commit comments

Comments
 (0)