>11.1.0

INTRODUCTION

This week marks the changing point between my two homes for the Fab Academy program. After lecture this week, I packed up my apartment in Lima and came back to Toronto to spend the Easter weekend with my wife and family before continuing onward to Yokohama for the rest of the program. This is an incredibly exciting week for me, but also an exhausting one. Lima only has a 1 hour time zone difference to Toronto (it was the same time zone before Toronto switched to DST) but I feel the most jet lagged in my life. Between my two trans-continental flights and visits to see my in-laws in Niagara, I've net almost 17,000km of distance this week.

Being welcomed in Toronto by rain, snow and still freezing temperatures. I already miss Peru...
IMAGE CAPTION
Being welcomed in Toronto by rain, snow and still freezing temperatures. I already miss Peru...
IMG_FILE: warmwelcomecanada.png
TYPE: PNG
XYZ: 123

And yet, I still found time to try to learn how to make (almost) anything.

This week's goalsWEEK 11
[x] Send a message between two projects
[x] Design, build and connect wired or wireless node(s) with network or bus addresses and a local input and/or output device(s) 

>11.2.0

RECAP

These past three weeks we've been studying a lot about how computers (and microcontrollers) speak to each other. We learned about different ways that machines detect physical changes in our environment, by converting different physical quanta into electrical signals. We also learned about ways to drive those quanta back out into the world using the exact same electrical signals. This week, we bridge the two concepts and review the different ways that our machines (and connected devices, smart or not) speak to each other, and the many ways that humans have come up with clever ways to turn simple binary signals into comprehensible and actionable commands in our incredibly busy technical world.

In learning about these processes over the last 3 weeks, I've taken some time to produce some interactive demonstrations of how these protocols and systems function (with a layer of abstraction/simplification) to aid my own learning (and anyone else reading this!). If you'd like to take a look at some of the other input/output specific demonstrations, head on over to those pages, but I'll be copying a few of them here to this page once again to give context to my assignment this week. I made these demonstrations using a mix of vector graphics (creating SVG paths in Illustrator), implementing some of the existing HTML/CSS of my site, and prompt engineering Gemini to make the skeleton animate with Javascript. Here's the pre-prompt I used to generate each demonstration. You'll also find the individual prompt for each below each demo.

Icon
GEMINI PROMPTLLM

You are an expert front-end developer and web animation specialist. Help me write vanilla JavaSript (not React) to animate several core concepts in electrical engineering for a widget on my website. I will provide you with a prompt that describes the concept as I want to convey it, how the user interacts with it, state machines to consider, the SVG objects and their names, the HTML layout of the site that I've already configured, and some CSS properties of the interactive components. [Insert individual prompt here]

>11.3.0

Wired Communication

➔ UART

I have technically already deployed a use of UART in my week 03 assignment, when I programmed and listened to the ATtiny45 and ATtiny412 boards, though with less of an appreciate for how it was working. UART, in a sense, is the most primitive and brute-force method of communication between devices, at least compared to I2C and SPI. Instead of having a clock line that synchronizes the flow of data between hosts, UART requires both hosts to use an agreed upon "baud rate", essentially a frequency to decode the flow of 0s and 1s that usually needs to be set by a human in code or directly with the hardware, a dedicated transmission and receiving line (much like an ethernet cable), and a "start" bit that one device will send to let the other device know they intend to begin sending data. Once a link has been established, the communication between devices is essentially as raw as it gets, sending individual bits down the TX/RX, with little regard for acknowledgement or packet structure, all that's needed is an agreed parity (number of bits sent at a time). It's messy, it's asynchronous, it's legacy, but it's raw and it works for low level debugging and data transfer.

INTERACTIVE DEMO
UART
CLIENT 1
CLIENT 2
TX ➔ RX
RX ⬅ TX
Client 1 Terminal (RX)
Client 2 Terminal (RX)
AI Prompt Attribution
Prompt: You are an expert front-end developer and web animation specialist. Help me write vanilla JavaSript (not React) to animate several core concepts in electrical engineering for a widget on my website. I will provide you with a prompt that describes the concept as I want to convey it, how the user interacts with it, state machines to consider, the HTML layout of the site that I've already configured, and some CSS properties of the interactive components. Two #box-black hosts ('Client 1' and 'Client 2') connected by cross-over TX/RX lines. Each host has a dropdown to set its baud rate (9600, 115200). When 'Send' is clicked, animate a red electron stream. If baud rates match, "translate" the signal into a scrolling binary string and ASCII text "Hello"; if they mismatch, display a non-ASCII character and a 'Framing Error' warning. Use vanilla JS and unique IDs."

➔ I2C

Inter-Integrated Circuit. As we discussed last week, I2C uses two wires to packages every command with a specific hardware address, and it works in both directions, which means it is half-duplex. Output devices can (and should) talk back to the microcontroller to confirm receipt of any instructions.

I2C also solves the problem of running out of pins on your microcontroller by allowing you to run devices on the same bus, because each device will only listen for it's address before reading what's inside the package. Because these devices communicate by pulling the line to ground (0V) rather than pushing voltage out, the bus requires pull-up resistors to keep the default state HIGH.

INTERACTIVE DEMO
I2C
2 WIRE BUS
3.3V SDA (DATA) SCL (CLOCK) DEVICE A ADDR: 0x0A RECEIVED DEVICE B ADDR: 0x0B RECEIVED
AI Prompt Attribution
Prompt: You are an expert front-end developer and web animation specialist. Help me write vanilla JavaSript (not React) to animate several core concepts in electrical engineering for a widget on my website. I will provide you with a prompt that describes the concept as I want to convey it, how the user interacts with it, state machines to consider, the HTML layout of the site that I've already configured, and some CSS properties of the interactive components. For this animation: When either 'Ping' button is clicked, animate a white data pulse traveling from the Master to BOTH devices simultaneously along a red SDA line. Only the device with the matching button-name/address should turn red and display 'RECEIVED' while the other remains greyed out.

➔ SPI

Serial Peripheral Interface. Again, this works in a similar way to I2C, but instead of addressing individual components in binary, we directly wire them up with something called a "Chip Select" pin for each device. However, instead of using the same SDA line, we have a dedicated talk/receive line, making it "full-duplex".

INTERACTIVE DEMO
SPI
Master Control
BUS IDLE
Dedicated CS Lines + Shared Data Bus
CS-A (HIGH) CS-B (HIGH) SCK (CLOCK) MOSI (MASTER OUT) MISO (MASTER IN) MASTER DEVICE A ASLEEP DEVICE B ASLEEP
AI Prompt Attribution
Prompt: You are an expert front-end developer and web animation specialist. Help me write vanilla JavaSript (not React) to animate several core concepts in electrical engineering for a widget on my website. I will provide you with a prompt that describes the concept as I want to convey it, how the user interacts with it, state machines to consider, the HTML layout of the site that I've already configured, and some CSS properties of the interactive components. For this animation: On the 'CS-A' and 'CS-B' lines, when a button pulls a CS line LOW, the corresponding device must wake up and turn red. Animate a 'Full Duplex' transaction with red data flowing out (MOSI) and blue data flowing in (MISO) simultaneously while a red dashed SCK line pulses.

➔ USB

Universal Serial Bus. Certainly the most complicated when it comes to communication but for good reason. USB has a whole consortium surrounding it to dictate the rules and methods that devices are allowed to talk to each other. This allows (for better or for worse) a lot of amazing things to happen over USB, such as transferring huge amounts of data to supply video monitors, high-speed solid state drives, and our handy microcontroller development boards!

I'm going to try to keep things simple in my explanation for brevity (and the fact that I need to go spend time with my in-laws shortly). Before data can even flow via USB, the devices involved need to determine that a device is actually physically connected, and it's more involved than just pulling a pin low. For USB-A, this mostly takes place in software, with a back-and-forth handshake over the D+ and D- lines, but for more complicated specifications like USB-C that have many more data pins, it's possible to tie some of these pins down with resistors to communicate quickly what kind of protocol/data transfer is supported. Once the handshake is formalized, the device is able to identify itself usually with an onboard chip that stores basic information like it's name, the vendor who made it, what kind of interface it is and how much power (if any) it will ask to draw. The host device then assigns a unique identifier or address to that connection and is able to speak to it much in the same way your router will address messages for specific ports over WiFi or Ethernet. The demonstration below goes over the handshake protocol step-by-step in the moments before you hear that new hardware connect sound in Windows after you've plugged something in via USB.

INTERACTIVE DEMO
USB
USB HOST (e.g. PC) USB DEVICE (e.g. Keyboard) 1.5kΩ VBUS (Power) D- (Data) D+ (Data) GND
Device Identity:
Unassigned
> Waiting for physical connection...
AI Prompt Attribution
Prompt: You are an expert front-end developer and web animation specialist. Help me write vanilla JavaSript (not React) to animate several core concepts in electrical engineering for a widget on my website. I will provide you with a prompt that describes the concept as I want to convey it, how the user interacts with it, state machines to consider, the HTML layout of the site that I've already configured, and some CSS properties of the interactive components. Create a step-by-step interactive simulator of the USB Enumeration process. Connect the host and USB Device by 4 lines, VBUS, D+, D-, and GND. Include a 'Device Status' panel and an 'Event Log' terminal. Step 1: VBUS powers on, device applies a 1.5kΩ pull-up to D+ for hardware speed detection. Step 2: Software Handshake where the Host requests the Device Descriptor, shown via animated data flowing across the D+ line. Step 3: Addressing, where the Host assigns a unique Address, and the device acknowledges in the terminal
using simple monospace text."
>11.4.0

Wireless Communication

➔ WiFi

I find myself breaking down the magic that is WiFi (Wireless Fidelity) to my high school students whenever I get the chance. I'll be honest, on paper - it's quite boring to read about, but it really is a modern marvel among other communication protocols that power our world and keep us (for better or for worse) constantly entertained and connected. WiFi itself though has no actual direct link to the "internet" despite what a teenager might be demanding from you upon arrival to a new place. WiFi really is just a smart way to get what the Ethernet protocol achieves sans wires. It communicates over radio waves (specifically 2.4Ghz and 5Ghz) to blast out each 1 and 0 to all of the devices within range. WiFi enabled devices will listen for their MAC address (a unique code assigned to every wireless device, like an IP address or a home address), essentially waiting for their name to be called, before they start listening to this stream of 1s and 0s. If your name isn't called, the packet of 1s and 0s isn't for you.

The big idea here is that a router isn't shooting an invisible, private laser beam to your device. WiFi is screaming across the entire room, and other devices are just "politely ignoring" anything that doesn't have its name (MAC address) on it.

INTERACTIVE DEMO
WIFI
ROUTER TX: 2.4 GHz LAPTOP A1:B2:C3:D4:E5 LISTENING... SMARTPHONE F6:G7:H8:I9:J0 LISTENING... MICROCONTROLLER K1:L2:M3:N4:O5 LISTENING...
> Airwaves idle. Ready to broadcast.
AI Prompt Attribution
Prompt: You are an expert front-end developer and web animation specialist. Help me write vanilla JavaSript (not React) to animate several core concepts in electrical engineering for a widget on my website. I will provide you with a prompt that describes the concept as I want to convey it, how the user interacts with it, state machines to consider, the HTML layout of the site that I've already configured, and some CSS properties of the interactive components. Use the router
to broadcast to three distinct devices (Laptop, Smartphone, Microcontroller) spaced around it, each displaying a unique MAC address. Include a dropdown to select a 'Target MAC' and a 'Broadcast' button. When clicked, reset the visual state and animate expanding radial radio waves from the router outward. As the waves hit the devices, all devices temporarily change state to 'Checking MAC Header'. After a brief delay, the two mismatched devices reject the packet (turn red, 'DROPPED'), and the single matching device accepts the data (turns green, 'ACCEPTED'). Use a technical, high-contrast aesthetic and include a terminal log translating the events."

On my flight back from Lima, I thought "Hey, why don't I just pack my router in my carry on bag and use it to try out some of the CircutPython libraries on the plane?". And so I did just that...

I wonder my seatmates were thinking when I pulled out this router halfway through the flight and plugged it in under the seat
IMAGE CAPTION
I wonder my seatmates were thinking when I pulled out this router halfway through the flight and plugged it in under the seat
IMG_FILE: wifiontheplane.png
TYPE: PNG
XYZ: 123

We're not quite at the part of the course where we're making API calls, but I wanted to give it a try with a simple Flask server deployed on my laptop so my XIAO ESP32-S3 had something to talk to. The flask server on my computer really only served a single purpose: provide the XIAO with a mock payload of data to work with, everything else would be running entirely on the XIAO.

flask_server.pyCODE
from flask import Flask, jsonify

app = Flask(__name__)

@app.route('/api/data', methods=['GET'])
def get_data():
    mock_payload = {
        "status": "CONNECTED",
        "flight": "YYZ to HND",
        "message": "Hello from my laptop! This message is sent over WiFi to the XIAO ESP32 from a Flask sever, and then being sent back to my computer through the serial port and into this terminal window!",
        "author": "2026",
        "test": "\u001B[1;5;7m\u001B[48;5;2m#############################\u001B[0m",
        "green-bar": "Kieran Mills (ESAN/Kannai)",
        "sensor_target": 24.5
    }
    return jsonify(mock_payload)

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000, debug=True)

This "server" is run on a local IP address that I can point the XIAO to once I've got everyone on the same network. Once connected, the XIAO will essentially ask for the "mock_payload" object, which is just a JSON formatted chunk of data. I've just got some placeholder text in the script above, and I was trying out some ANSI escape codes to get the output to look fun and colourful in the serial terminal. For the XIAO to be able to connect to my router, I created a file called settings.toml to the root directory of the cricutpython drive with the SSID and password so I could easily change it without editing the python script.

settingstoml.png

Then we get a script together on the XIAO that tries to make this API request. This utilizes the socketpool and adafruit_requests libraries that utilize the onboard 802.11 antenna the ESP32 has to communicate over WiFi. All of the addressing and package information is handled by these packages. This script makes that API call and then prints what it receives in the serial terminal.

xiao_wifi.pyCODE
import os
import time
import wifi
import socketpool
import adafruit_requests

print("\n Connecting...")
ssid = os.getenv("CIRCUITPY_WIFI_SSID")
password = os.getenv("CIRCUITPY_WIFI_PASSWORD")
wifi.radio.connect(ssid, password)
print(f"Connected! ESP32 IP: {wifi.radio.ipv4_address}")

LAPTOP_IP = "192.168.0.100" #this changes to whatever your actual IP address is 
API_URL = f"http://{LAPTOP_IP}:5000/api/data"

pool = socketpool.SocketPool(wifi.radio)
requests = adafruit_requests.Session(pool)

print(f"\nFetching...")

response = requests.get(API_URL)
json_data = response.json()

print(json_data['test'])
print(json_data['test'])
print(json_data['test'])
print(json_data['test'])

print(f"FAB ACADEMY {json_data['author']}")

print(json_data['message'])
print(f"Flight: {json_data['flight']}")
print(json_data['green-bar'])

print(json_data['test'])
print(json_data['test'])
print(json_data['test'])
print(json_data['test'])

response.close()

I know that it's kind of a round about way to get these two devices to talk to each other, when they're already attached via USB for the serial monitor read out, but bare with me here, I pulled this off 30,000 feet in the air!

Had some issues getting it working at first and then I realized the XIAO needed the antenna attachment. Sure enough, when I plugged it in, I got the payload in my terminal.

runningcodeat30000feet.png

➔ Bluetooth

This works, in a sense, much similar to WiFi (Bluetooth even communicates over the same 2.4Ghz ISM band), but instead of having one central router for devices to be sending/receiving from, each device acts as it's own router, broadcasting messages for intended recipients who have been safely/securely paired. Yes, technically we can do this too with WiFi over adhoc, but Bluetooth uses a different kind of timing structure to make this work, very complex - and like I said - I need to go spend time with my in-laws this weekend.

Data sent over the air is usually encrypted using something called Elliptic Curve Cryptography (ECC) and uses public-private encoding to prevent "man-in-the-middle attacks", where someone can impersonate an access point and get the data you think you're sending to the intended recipient.

INTERACTIVE DEMO
BLUETOOTH
HOST (e.g. Smartphone) PERIPHERAL 1 (e.g. Earbuds) PERIPHERAL 2 (e.g. Smartwatch) 2.4 GHz SPECTRUM (CHANNELS 0-15) WIFI INTERFERENCE
> Piconet idle. Devices unpaired.
AI Prompt Attribution
Prompt: You are an expert front-end developer and web animation specialist. Help me write vanilla JavaSript (not React) to animate several core concepts in electrical engineering for a widget on my website. I will provide you with a prompt that describes the concept as I want to convey it, how the user interacts with it, state machines to consider, the HTML layout of the site that I've already configured, and some CSS properties of the interactive components. Create an interactive Bluetooth simulator demonstrating Adaptive Frequency Hopping (AFH) and Time Division Multiple Access scheduling using HTML/JS/SVG. Render a Central device connected to two Peripheral devices, sitting above a simplified 2.4GHz frequency spectrum bar (Channels 0-15). Include a button to 'Start AFH', which animates an active channel indicator jumping across the spectrum randomly, while alternating dashed connection lines to show the Central communicating with Peripheral 1, then Peripheral 2 on different channels. Include a toggle to 'Introduce WiFi Interference', which highlights a block of channels in red, actively removing them from the hopping algorithm's sequence to demonstrate noise avoidance. Output terminal logs to explain the schedule and channel blacklisting."

I read online that there's a great app called nRF Connect that does a great job of polling BLE devices that are self-advertising on Android, so I figured I'd give it a go, rather than using my laptop. I put together a script that uses the adafruit_ble libraries to make use of the very same antenna as our last example.

ble_test.pyCODE
import time
from adafruit_ble import BLERadio
from adafruit_ble.advertising.standard import ProvideServicesAdvertisement
from adafruit_ble.services.nordic import UARTService

print("\n Starting...")

ble = BLERadio()
uart = UARTService()
advertisement = ProvideServicesAdvertisement(uart)

while True:
    print("Advertising BLE...")
    ble.start_advertising(advertisement)

    while not ble.connected:
        time.sleep(0.1)

    print("Phone Connected...")
    ble.stop_advertising()

    while ble.connected:
        #write
        uart.write("HELLO FROM THE XIAO!")

        #read
        if uart.in_waiting:
            incoming = uart.readline().decode("utf-8").strip()
            print(f"BLE Received: {incoming}")

        time.sleep(2)

    print("Disconnected...")

This script broadcasts it's bluetooth name over the air where we're able to find it in the nRF Connect app through a quick scan. Once it makes a successful pair, it sends a string over UART and then continues to listen for any strings that are sent in return, outputting what it gets into the serial terminal.

>11.5.0

Putting it All Together

I thought this week would be a good opportunity to continue exploring I2C and SPI communication, as my final project relies heavily on SPI for reading the flash memory off the cartridges and communicating with the display, and I2C to speak to all of the external components that could be added to a cartridge. In my limited capacity aboard a plane (I didn't exactly have a the space to test out every little component), I decided to use another XIAO RP2040 to act as a "dummy" cartridge that could pretend to communicate back information to the main board (ESP32). I used my last day at ESAN to design and mill out a custom board that could accommodate the two boards simultaneously and even one of my W25Q128JVSIQ flash memory chips so I could do some more testing. I decided last minute to switch to UART for simplicity as I would only need to get the RX/TX traces milled.

Unfortunately, I couldn't have one final victory over the carvera before leaving ESAN. I was unfortunately humbled in my ambitions to get this all done on my last day. The resulting board has some kind of awful short on the ESP32 side that completely fries whatever board gets soldered down to it. I sadly lost two XIAOs to this little detour, and with an apartment to pack up and a flight to catch in the morning, I decided to cut my losses and concede defeat to the process. They let you take breadboards in your carry on, right?

uartintheair.png

While waiting for my flight to start boarding, I chipped away at the scripts that both microcontrollers would run. The ESP32 would host a simple HTML webpage that I could pick up by browsing the ESP's IP address on my laptop. This would give me an interface to send and recieve the pings from, seeing as I'm not wiring up much more to these two microcontrollers.

esp_side.pyCODE
import os
import time
import wifi
import socketpool
import busio
import board
from adafruit_httpserver import Server, Request, Response

print("\n Starting...")

uart = busio.UART(tx=board.D6, rx=board.D7, baudrate=115200, timeout=0.1)

ssid = os.getenv("CIRCUITPY_WIFI_SSID")
password = os.getenv("CIRCUITPY_WIFI_PASSWORD")
print(f"Connecting to {ssid}...")
wifi.radio.connect(ssid, password)
print(f"Connected! http://{wifi.radio.ipv4_address}")

pool = socketpool.SocketPool(wifi.radio)
server = Server(pool, "/static", debug=True)

#html
HTML_PAGE = """
<!DOCTYPE html>
<html>
<head>
    <title>PAK Base Station</title>
    <style>
        body { font-family: sans-serif; text-align: center; margin-top: 50px; background-color: #f4f4f9; }
        button { font-size: 20px; padding: 15px 30px; margin: 10px; border-radius: 8px; border: none; background-color: #007bff; color: white; cursor: pointer; }
        button:hover { background-color: #0056b3; }
    </style>
</head>
<body>
    <h1>PAK Wireless Command Center</h1>
    <p>Click a button to send a UART command to the RP2040.</p>

    <a href="/command/ping"><button>Send "PING"</button></a>
    <a href="/command/status"><button>Request "STATUS"</button></a>
</body>
</html>
"""
@server.route("/")
def base(request: Request):
    return Response(request, body=HTML_PAGE, content_type="text/html")

@server.route("/command/<cmd>")
def send_command(request: Request, cmd: str):
    print(f"Browser clicked: {cmd}")
    uart.write(bytes(f"{cmd.upper()}\n", "utf-8"))
    success_html = f"<h2>Sent '{cmd.upper()}' over UART!</h2><a href='/'>Go Back</a>"
    return Response(request, body=success_html, content_type="text/html")

server.start(str(wifi.radio.ipv4_address))

while True:
    server.poll()
    if uart.in_waiting:
        reply = uart.readline()
        if reply:
            print(f"RP2040 Replied: {reply.decode('utf-8').strip()}")

            time.sleep(0.01)

The ESP and RP2040 will communicate at 115200 baud. Reminder that you have to switch the RX and TX (the ESP's TX becomes the RP2040's RX and vice-versa). The ESP waits for a command from the web server, which is browsed on my laptop over WiFi, before sending a command to the RP2040. We can also ask it to poll the RP2040 from the web server using a simple python script loaded up on the RP2040 itself.

rp2040_side.pyCODE
import board
import busio
import time

uart = busio.UART(tx=board.D6, rx=board.D7, baudrate=115200, timeout=0.1)

print("Listening...")

while True:
    if uart.in_waiting:
        data = uart.readline()
        if data:
            message = data.decode('utf-8').strip()
            print(f"Received Command: {message}")

            if message == "PING":
                response = b"PONG\n"

            uart.write(response)
            print("Response sent.")

    time.sleep(0.1)

I connected the ESP to the USB power coming from the seat (so I could fully test out the fact that it was running without a tether) and hooked up the RP2040 via USB and started listening over serial. And the result is something that probably looked a little sketchy to my fellow seatmates aboard the plane. It didn't help that I had a middle seat, sandwiched between two strangers, but it made for some great conversation about my journey and what I've been learning at Fab Academy!

uartontheplane.png

My home soldering iron in Toronto died on me (go figure) and I wasn't able to get pin headers installed on the XIAOs before takeoff, so I bodged them with some dupont connectors which made for a pretty shoddy connection. It took a couple tries to get this right, so as soon as I saw "PING" show up in the terminal of the RP2040, coming from the ESP, I grabbed my picture and called it a day (or whatever time my brain thought it was on my 13 hour flight). I learned a lot this week, even with all the jetlag.

>11.6.0

Conclusion

This week was a literal trek, (17,000 kilometers to be precise) and for whatever reason, I spent my last day at ESAN thinking I could go into this assignment expecting to rely entirely on a custom milled carrier board, and I was quickly humbled when a short on that board ended up frying two of my microcontrollers right before I had to pack up my apartment. Despite the setback, a simple bodge of jumper wires allowed me to actually pull off the assignment requirements in the air. I learned a ton about the low-level nuances of the different protocols we're studying and added more interactive demonstrations to my site! A successful transition week, for sure.

See you soon, Yokohama!