To connect a microcontroller to a Discord server, you need the Raspberry Pi Pico W (not the standard Pico) running MicroPython, utilizing the urequests library to hit Discord Webhooks. Because the RP2040’s 264KB SRAM cannot handle Discord’s persistent WebSocket gateway without crashing, the industry-standard workaround is using stateless HTTPS Webhooks. Furthermore, because the Pico W’s WiFi stack (CYW43439) is notorious for silent drops, integrating a dedicated hardware serial (ser) debug line via UART is mandatory for reliable bench testing.
Time to Build: 45 minutes
Target Board Variant: Raspberry Pi Pico W (RP2040 with Infineon CYW43439 WiFi/BLE)
Project Overview & Hardware Spec Sheet
Before writing code, we need to establish the physical layer. The standard Pico W onboard USB handles the MicroPython REPL, but when the WiFi driver crashes, the USB serial port often locks up, leaving you blind. Adding a secondary UART serial adapter gives you an unkillable debug stream.
| Component | Exact Variant / Model | Purpose & Notes |
|---|---|---|
| Microcontroller | Raspberry Pi Pico W (with pre-soldered headers) | Main logic and WiFi. Do not use the standard Pico or Pico H. |
| Serial Adapter | CP2102 or FT232RL USB-to-TTL 3.3V | Provides hardware UART serial debug. Must be 3.3V logic, NOT 5V. |
| Wiring | 22 AWG solid core jumper wires | For breadboard connections. |
| Logic Shifter (Optional) | BSS138 Bidirectional Level Shifter | Only required if your serial adapter is strictly 5V tolerant. |
Pin Mapping & UART Serial Wiring
We will use UART0 on the Pico W for our dedicated serial debug output. This keeps the primary USB port free for flashing firmware and standard REPL interaction.
| Pico W Pin | Function | Connects to CP2102/FT232RL Pin |
|---|---|---|
| Pin 1 (GP0) | UART0 TX | RX (Receive) |
| Pin 2 (GP1) | UART0 RX | TX (Transmit) |
| Pin 38 (GND) | Ground | GND |
MicroPython Code: Pico Discord Server Integration
The following code targets the Pico W. It connects to your local WiFi, formats a JSON payload, and sends it to a Discord Server via a Webhook URL. Notice the explicit gc.collect() calls; the Pico W’s TLS handshake for HTTPS consumes roughly 40KB of RAM, and failing to clear garbage memory will result in immediate allocation crashes.
import network
import urequests
import ujson
import machine
import gc
import time
# --- PIN & UART DEFINITIONS ---
# Using UART0 on GP0 (TX) and GP1 (RX) for secondary serial debug
uart_debug = machine.UART(0, baudrate=115200, tx=machine.Pin(0), rx=machine.Pin(1))
def debug_print(msg):
"""Prints to both standard USB REPL and hardware UART serial."""
print(msg)
uart_debug.write((str(msg) + '\n').encode('utf-8'))
# --- NETWORK CREDENTIALS ---
WIFI_SSID = "YourNetworkName"
WIFI_PASSWORD = "YourNetworkPassword"
# Generate this via Discord Server Settings -> Integrations -> Webhooks
DISCORD_WEBHOOK_URL = "https://discord.com/api/webhooks/123456789/ABCDEFGHIJKLMNOPQRSTUVWXYZ"
def connect_wifi():
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
# Prevents the WiFi radio from drawing too much current during peak TX
wlan.config(pm = 0xa11140)
if not wlan.isconnected():
debug_print(f"Connecting to {WIFI_SSID}...")
wlan.connect(WIFI_SSID, WIFI_PASSWORD)
timeout = 20
while not wlan.isconnected() and timeout > 0:
time.sleep(1)
timeout -= 1
if wlan.isconnected():
debug_print(f"WiFi Connected! IP: {wlan.ifconfig()[0]}")
return True
else:
debug_print("WiFi Connection Failed.")
return False
def send_discord_message(content):
# Force garbage collection before TLS handshake to prevent MemoryError
gc.collect()
headers = {'Content-Type': 'application/json'}
payload = ujson.dumps({
"username": "Pico W Bot",
"content": content
})
try:
debug_print("Sending HTTPS request to Discord...")
response = urequests.post(DISCORD_WEBHOOK_URL, data=payload, headers=headers)
debug_print(f"Response Status: {response.status_code}")
response.close()
return True
except Exception as e:
debug_print(f"Request Failed: {e}")
return False
# --- MAIN EXECUTION LOOP ---
if __name__ == "__main__":
debug_print("System Booting...")
if connect_wifi():
# Send a test message to the Discord server
send_discord_message("🟢 Pico W is online and reporting via serial debug!")
else:
debug_print("Halting execution due to network failure.")
Debugging: Exact Error Strings & The First 3 Checks
When building a Pico Discord serial integration, the WiFi stack will inevitably fail. Here are the exact error strings you will see in your serial monitor, ranked by frequency, and how to fix them.
1. OSError: [Errno 113] EHOSTUNREACH or [Errno 118]
Cause: The Pico W connected to the router, but the DNS resolution for discord.com failed, or the router is blocking the IoT device from accessing the WAN. MicroPython’s DNS resolver is notoriously fragile on the RP2040.
Fix: Hardcode the DNS server in your WiFi config before connecting: wlan.config(dns='8.8.8.8').
2. RuntimeError: no available WiFi
Cause: The CYW43439 WiFi driver crashed. This usually happens if you attempt to initialize the network interface while the 3.3V rail is sagging under the load of the initial radio calibration spike (which can pull up to 130mA momentarily).
Fix: Ensure your USB cable is high-quality (20 AWG power wires minimum) and plugged directly into a PC or a 2A wall brick, not an unpowered USB hub.
3. MemoryError: memory allocation failed, allocating X bytes
Cause: The TLS handshake for Discord’s HTTPS webhook requires a large contiguous block of RAM. If memory is fragmented, the allocation fails even if total free RAM seems sufficient.
Fix: Always call gc.collect() immediately before calling urequests.post(), as shown in the code block above.
- Verify the Webhook URL: Open the URL in a desktop browser. If it returns
{"message": "Unknown Webhook", "code": 10015}, the URL is valid but the payload format is wrong. If it times out, your network is blocking it. - Check the Baud Rate: Ensure your serial monitor (PuTTY, TeraTerm, or Thonny) is set to exactly
115200baud to match the UART0 initialization. - Ping Test: Modify the code to ping
8.8.8.8before attempting the HTTPS request to isolate DNS issues from routing issues.
Extending and Simplifying the Build
Depending on your deployment environment, you may need to alter the hardware footprint.
To Simplify (Headless Deployment): Once the code is stable, remove the CP2102 serial adapter entirely. Delete the uart_debug initialization and route all print statements to standard print(). This frees up GP0 and GP1 for I2C sensors and reduces the physical footprint to just the Pico W.
To Extend (Sensor Integration): Add a DHT22 temperature sensor to GP16. Read the sensor value every 60 seconds, and format the Discord payload to include the temperature. Because Discord enforces rate limits on webhooks (typically 5 requests per 2 seconds per channel, but practically you should limit automated posts to 1 per minute to avoid IP bans), use time.sleep(60) in your main loop. For the official rate limit specifications, refer to the Discord Developer Portal documentation on rate limits.
Frequently Asked Questions
Can I use a standard Pico instead of a Pico W for a Discord server bot via serial?
No. The standard Raspberry Pi Pico lacks the CYW43439 WiFi/BLE chip. While you could connect the standard Pico to a PC via USB serial and have the PC run a Python script to forward the data to Discord, the Pico itself cannot natively reach the Discord server. If you want standalone network capabilities without WiFi, you would need to wire an Ethernet module (like the W5500) to the standard Pico’s SPI pins, though MicroPython’s Ethernet TLS support is currently much less stable than the Pico W’s WiFi stack.
Why does my Pico W Discord serial monitor freeze when sending webhook requests?
This freeze is almost always caused by the watchdog timer or a blocking network call that starves the USB CDC (serial) task. When urequests.post() executes, it blocks the main thread while waiting for the TLS handshake and TCP ACKs. If the router takes too long to respond, the USB serial buffer overflows or the host PC drops the COM port connection. Using the secondary hardware UART (GP0/GP1) bypasses the USB stack entirely, ensuring your debug logs survive network hangs. For deeper networking architecture details, consult the official Raspberry Pi MicroPython networking guide.
How do I generate a Discord webhook URL for the Pico W?
Open your Discord Server, right-click the specific text channel where you want the Pico to post, and select Edit Channel. Navigate to Integrations on the left sidebar, then click Webhooks. Click New Webhook, name it (e.g., "Pico Telemetry"), and click Copy Webhook URL. Paste this exact string into the DISCORD_WEBHOOK_URL variable in your MicroPython code. Note that anyone with this URL can post to that channel, so do not commit it to public GitHub repositories.






