Week 4 — Embedded programming
This week was about embedded programming (
Fab Academy — Embedded Programming). For my individual assignment, I built a Wokwi simulation in
Cursor Desktop: an Arduino Uno reads a
DHT22 temperature and humidity sensor, shows the values on a
1602 LCD with an I2C backpack at address 0x27, and
prints the same readings to the serial monitor. My final project will use
temperature and humidity sensing to monitor a plant’s growing environment; this
simulation is an early way to test the sense → display path before building the
full physical circuit.
Individual assignment — Wokwi simulation: Uno + DHT22 + LCD1602 (I2C)
Assignment checklist
The Fab Academy individual assignment asks us to browse a microcontroller datasheet, write and test a program for an embedded system, interact with local input/output devices, communicate with a wired or wireless connection, include source code, and document the programming process. My work covers those points as follows:
- Microcontroller documentation: I used an Arduino Uno R3 based on the ATmega328P. I checked the Uno pinout and ATmega328P documentation to confirm that D2 can be used as a digital data pin, and that Uno hardware I2C uses SDA=A4 and SCL=A5.
- Input and output: the DHT22 is the input device; the LCD1602 and serial monitor are the output devices.
- Communication: the LCD communicates with the Uno through I2C, and the Uno reports readings to the computer through Serial.
- Programming process: I edited the sketch in Cursor Desktop, used the Wokwi extension to run the circuit, and checked both the LCD and the serial monitor while debugging.
-
Source and hero shots: the full sketch is included below and
stored at
code/week04/wokwi/sketch.ino. The screenshots show the circuit, simulator window, and serial output. - Group work: see the group assignment section on this page.
1. Task And Motivation
Because my final project will rely on temperature and humidity sensors to watch the plant’s growing environment, I wanted a low‑risk way to prove the circuit and code first. I chose to do that in simulation (Wokwi) so I could iterate on wiring and software before committing to a breadboard or a specific lab session.
Within that goal, I wanted to test the basic sensing path before working with physical wiring: environment → sensor → microcontroller → readable output. The DHT22 gives temperature and humidity readings, and the LCD makes the result visible without depending only on the serial monitor.
I chose a DHT22 because it is a common classroom sensor, has better resolution than a DHT11, and is suitable for indoor temperature and humidity experiments. I used a 1602 LCD with an I2C backpack because it needs only two signal wires instead of many parallel LCD pins.
My workflow was: install the Wokwi extension in Cursor Desktop, create the
circuit in diagram.json, write the Arduino sketch, start the
simulator, check the LCD and serial monitor, then document the process with
screenshots and source code.
2. Learning
I pushed myself to learn beyond the simulator: I wanted to see what a real
character LCD actually looks like on the bench, so I hunted around the lab and
found an LCD2004 with an I2C backpack (my Wokwi build still
uses a 1602 because that part is easy to fit in the diagram, but
the hardware idea is the same). On the back I noticed
three small address pads and no obvious label telling me the I2C
address out of the box. Rather than guess, I followed that curiosity into
DFRobot’s Chinese wiki for the classic backpack style (
A0/A1/A2 as the low address
bits). There I learned how jumper soldering maps to the
0x20–0x27 range, and how to match a real module to the right
LiquidCrystal_I2C address—then I confirmed that against the pads on
the board I had in hand.
The DHT22 uses a single-wire digital protocol. In the Arduino environment I used the DHT library, which also recommends waiting about 2 s between readings. The LCD backpack uses I2C. Many boards in the lab use a backpack based on an I/O expander such as the PCF8574, where those three address lines pick one of eight addresses in the 0x20–0x27 range (other chip families sometimes default to 0x3F instead). In my Wokwi project and in code I used address 0x27, consistent with what I inferred from the wiki and from inspecting the solder bridges on the backpack.
How the I2C address is set on the backpack
On the DFRobot DFR0063-style I2C LCD module, the wiki documents that the last
three address bits are controlled by small solder jumpers (or bridges) on the
back of the backpack: each of A0, A1, and
A2 can be read as 0 or 1
depending on how that pad is connected, and the combination picks the
7‑bit I2C address. Below is the lab module (LCD2004) from the back—the three pads
that set the address are circled; then the manufacturer table I pulled from the
wiki to interpret them and lock in 0x27 for
LiquidCrystal_I2C.
0x27 versus another
value in code. (Simulation still uses a 1602 for convenience.)
Reference: DFRobot — I2C LCD1602 module DFR0063 (documentation and address setup).
One important detail is that the Wokwi DHT22 data pin is labelled SDA, but it is not the same SDA line as I2C. In this circuit, the DHT22 data pin goes to D2. The LCD is the part that uses real I2C, with SDA → A4 and SCL → A5 on the Arduino Uno.
For the microcontroller reference, I checked the ATmega328P documentation and the Arduino Uno pinout. This confirmed the digital pin and I2C pin choices before I finished the Wokwi wiring.
I also kept one earlier ESP32-S3 draft in the repository as a reference for a
possible future board change:
code/week04/esp32-s3-dht-lcd-main.cpp.txt. This week’s documented circuit and screenshots use the Arduino Uno project
in diagram.json.
3. Plan
- Install and open the Wokwi simulator from Cursor Desktop.
- Add an Arduino Uno, DHT22, and LCD1602 with I2C pins to the Wokwi diagram.
- Connect power, ground, DHT data, and LCD I2C lines.
- Add the required Arduino libraries for DHT and LCD control.
- Write a sketch that reads every 2 s, updates the LCD, and prints to Serial.
- Run the simulation, take screenshots, and save the source files in the repository.
4. Process
Step A — Installing Wokwi In Cursor Desktop
- I opened Cursor Desktop and used ⌘+Shift+X to open the Extensions panel.
- I searched for Wokwi, selected Wokwi Simulator, and clicked Install.
- I followed the Wokwi extension setup guide to enable the simulator in the editor.
-
I opened the project folder
code/week04/wokwi/in Cursor, because that folder containsdiagram.json,sketch.ino, and the project settings. - From the Command Palette I ran Wokwi: Start Simulator. When the simulator opened, I used the serial monitor to check the printed sensor values.
If the simulator does not start, the first things I check are whether the opened
folder contains diagram.json, whether sketch.ino is in
the same project folder, and whether the libraries listed in
libraries.txt
are available.
Step B — Building The Circuit
The wiring in my diagram.json is:
| Signal | Component pin | Arduino Uno pin |
|---|---|---|
| 5 V | DHT22 VCC, LCD VCC |
5V |
| GND | DHT22 GND, LCD GND |
GND |
| DHT22 data | DHT22 pin labelled SDA |
D2 |
| I2C | LCD SDA / SCL |
A4 / A5 |
I set the LCD to I2C mode and used address 0x27. On real hardware, if the LCD stays blank, I would first check the I2C address and the contrast setting on the backpack.
Step C — Code And Libraries
The project uses the Arduino framework. The dependency list is saved as
libraries.txt.
The sketch initializes the LCD, reads the DHT22 every two seconds, calculates
heat index in Celsius, updates both LCD rows, and prints the same values to the
serial monitor. If the DHT read fails, the LCD shows a short error message.
Downloadable source: code/week04/wokwi/sketch.ino.
/**
* Fab Week 4 — Wokwi: Arduino Uno + DHT22 + LCD1602 (I2C)
* See diagram.json in this folder for wiring.
*/
#include <Arduino.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <DHT.h>
#define DHT_PIN 2
#define DHTTYPE DHT22
#define LCD_I2C_ADDR 0x27
#define LCD_COLS 16
#define LCD_ROWS 2
DHT dht(DHT_PIN, DHTTYPE);
LiquidCrystal_I2C lcd(LCD_I2C_ADDR, LCD_COLS, LCD_ROWS);
static void showSensorError(void) {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("DHT read error");
lcd.setCursor(0, 1);
lcd.print("chk D2 / DHT22");
}
void setup() {
Serial.begin(9600);
lcd.init();
lcd.backlight();
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Uno + DHT22");
lcd.setCursor(0, 1);
lcd.print("LCD I2C 0x27");
delay(800);
dht.begin();
Serial.println(F("Arduino Uno — DHT22 @ D2, LCD1602 I2C SDA=A4 SCL=A5 addr=0x27"));
}
void loop() {
delay(2000);
float rh = dht.readHumidity();
float tC = dht.readTemperature();
if (isnan(rh) || isnan(tC)) {
Serial.println(F("[err] DHT read failed — wiring / type / 2s sample"));
showSensorError();
return;
}
float hiC = dht.computeHeatIndex(tC, rh, false);
char line0[17];
char line1[17];
(void)snprintf(line0, sizeof(line0), "T:%4.1fC H:%4.0f%%", tC, rh);
(void)snprintf(line1, sizeof(line1), "HeatIdx:%5.1fC", hiC);
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(line0);
lcd.setCursor(0, 1);
lcd.print(line1);
Serial.print(F("RH: "));
Serial.print(rh, 1);
Serial.print(F("% T: "));
Serial.print(tC, 1);
Serial.print(F(" C HI: "));
Serial.print(hiC, 1);
Serial.println(F(" C"));
}
Step D — Running The Simulator And Serial Monitor
Step E — Reproducing The Simulation
- Open
code/week04/wokwi/as the project folder in Cursor Desktop. - Install the Wokwi Simulator extension and run Wokwi: Start Simulator.
- Check that the libraries in
libraries.txtare available. - Watch the LCD and serial monitor while the simulated DHT22 values update.
- Click the DHT22 part in Wokwi and change the simulated temperature or humidity to test the program.
- For real hardware, check the DHT module pull-up requirement, LCD I2C address, and 5 V/GND wiring before powering the circuit.
5. Conclusion
This exercise gave me a working chain from a physical quantity to a readable output: the DHT22 turns temperature and humidity into values in the sketch, the LCD shows them locally, and Serial sends them back to the computer. The main wiring detail I learned is that the DHT22 data pin label can be confusing in Wokwi: it says SDA, but in this circuit it is a single data line connected to D2, not the LCD's I2C bus.
Files: the Wokwi project is stored in
code/week04/wokwi/ (sketch.ino,
diagram.json, and libraries.txt). Screenshots and photos
are in images/week04/, including the Wokwi captures, the combined
result frame, and the two
LCD backpack address figures (wiki table and hardware photo).
My next step is to connect the same type of sensor to a real board, compare the simulated values with physical readings, and decide what temperature thresholds the final project character should respond to.
Group assignment
Guangzhou (Chaihuo) — group documentation: comparing toolchains and development workflows across embedded architectures.
Abstract
The group demonstrates and contrasts what is available on different embedded architectures in the lab: not only compilers and link scripts, but the full toolchain (build system, programmer/debug probe, flashing utilities, debug servers, and board-support packages) and the day-to-day workflow from “edit code” to “running firmware on hardware.” The write-up compares development software (IDEs, extensions, vendor tools), programming languages in use (C, C++, Rust, MicroPython, etc.), and pre-deployment utilities (linters, simulators, packaging, CI) as part of one coherent story. It should make it obvious why a student would pick one stack versus another for a given board or assignment.
Case study: RP2040 vs ESP32-C3 (Seeed XIAO)
The group uses Raspberry Pi RP2040 and Espressif ESP32-C3 as two common Fab-lab paths: RP2040 highlights PIO, dual-core MCU, USB device capability, and an “MCU + external flash” model without on-chip radio; ESP32-C3 adds 2.4 GHz Wi‑Fi and Bluetooth 5 LE on a single-core RISC-V for connected / low-power IoT. The thumb-sized Seeed XIAO RP2040 and XIAO ESP32C3 share a similar footprint, so they are a fair pair for a toolchain comparison.
References: Seeed Studio — XIAO series introduction (online) · offline copy in this repo: xiao-series-introduction-seeed-wiki.html.
Chip capabilities (check datasheets for your revision)
| Topic | RP2040 | ESP32-C3 |
|---|---|---|
| CPU | Dual ARM Cortex-M0+, typically 133 MHz (overclocking depends on board and firmware) | Single-core 32-bit RISC-V, up to about 160 MHz |
| On-chip SRAM | 264 KB (multi-bank) | About 400 KB (partitioning per Espressif datasheet) |
| Program storage | No in-package flash; external QSPI serial flash (often 2–16 MB) | In-package or on-board flash common (~4 MB class for many modules) |
| Wireless | No built-in Wi‑Fi / Bluetooth | 2.4 GHz Wi‑Fi (802.11 b/g/n) + Bluetooth 5 LE |
| USB | USB 1.1 controller + PHY (device or host roles depend on SDK and hardware) | USB serial / JTAG paths for flash and debug (exact options depend on chip revision and board) |
| Notable peripherals | PIO (eight state machines) for custom fast I/O protocols | Rich connectivity: wireless stacks, GDMA, multiple SPI / I2C / UART, PWM, ADC, etc. |
| ADC | On-chip ADC (channels and resolution per RP2040 datasheet) | 12-bit SAR ADC (available GPIO count depends on package / module) |
| GPIO (chip) | Up to about 30 multifunction GPIO | About 22 programmable GPIO (XIAO exposes a subset) |
| Security | Typical MCU features (no integrated wireless security stack) | Optional secure boot, flash encryption, hardware crypto accelerators (series / config dependent) |
| Typical supply | Often about 1.8–5.5 V (confirm on Pico / XIAO schematic) | Often 3.0–3.6 V at the module (USB 5 V via LDO) |
XIAO boards break out far fewer pins than the chip maximum; routing, antenna, and USB front-end affect which GPIO and RF performance you actually get.
Toolchains and workflows (typical class paths)
| Stage | RP2040 (e.g. XIAO RP2040) | ESP32-C3 (e.g. XIAO ESP32C3) |
|---|---|---|
| Official SDK | Pico SDK (CMake + GCC); verify pin map and clocks for XIAO |
ESP-IDF (FreeRTOS components);
idf.py build flash monitor
|
| Arduino | Arduino-Pico core | arduino-esp32 — select ESP32-C3 board target |
| Scripting | MicroPython / CircuitPython (mature on RP2040) | MicroPython and other scripting options; many Wi‑Fi examples |
| Debug | SWD (Pico Probe, DAPLink, etc.); printf-style trace in some setups | OpenOCD + GDB, USB-JTAG paths depending on silicon and board |
| Flash | UF2 drag-and-drop (BOOTSEL) or SWD to external flash | UART download, USB flash; ESP-IDF integration |
| Pre-flash tooling |
clangd, PIO tooling ideas from Raspberry Pi docs, optional static analysis
|
idf.py menuconfig, partition table, Wi‑Fi
provisioning, log levels
|
| Ecosystem strengths | USB gadgets, LED / display drivers, PIO for odd protocols | MQTT / HTTP, BLE, provisioning, OTA (with security care) |
How to choose (for coursework)
- Sensor-heavy, real-time GPIO, custom protocols, no network: RP2040 is often simpler; PIO can implement timing without saturating the CPU.
- Cloud / phone BLE / always-on connectivity: ESP32-C3 ships wireless stacks and examples; Arduino and ESP-IDF communities are large.
- Documentation tip: for each board, write one command sequence from new project → build → flash → serial monitor → (optional) step debug, and log one real failure (wrong COM port, boot mode, CMake toolchain version, etc.).
1. Architectures and boards
Name at least two embedded families or boards the lab actually uses (e.g. AVR, SAMD, STM32, ESP32, RP2040, RISC-V) and what each is good for in coursework.
2. Toolchain inventory per target
For each architecture, list the compiler/toolchain, flash method, debugger (if any), and default BSP or core packages.
3. Workflow comparison
Compare side by side: project creation, build, flash, serial monitor, breakpoint debugging (if supported), and common failure modes.
4. Languages and pre-deployment tools
Summarize language choices and tools used before flashing (formatting, static checks, emulation where relevant).