Arduino Firmware Breakdown
This documentation explains the complete firmware architecture used in the Digital Contour Gauge communication prototype. The RP2040 firmware simultaneously acts as a sensing node, display renderer, embedded UI controller, memory manager, and UART communication bridge.
Embedded System Responsibilities
| Subsystem | Responsibility |
|---|---|
| Analog Acquisition Node | Reads potentiometer positions |
| Display Rendering Engine | Draws contour graphics onto the OLED |
| Embedded UI Controller | Handles menus and button interactions |
| Memory Buffer | Stores contour scans in SRAM |
| UART Communication Node | Streams coordinate packets to the laptop |
Imported Libraries & Hardware Drivers
The firmware begins by importing multiple communication and graphics libraries. These libraries provide hardware abstraction layers between the RP2040 and its peripherals.
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
Wire.h — I2C Communication Bus
The Wire library initializes and manages the synchronous I2C protocol.
This protocol uses:
- SDA → Serial Data Line
- SCL → Serial Clock Line
The OLED exists as a separate hardware node connected through this shared communication bus.
Adafruit_GFX.h — Graphics Engine
The graphics library provides primitive rendering instructions such as:
- Lines
- Circles
- Pixels
- Text
- Rectangles
Adafruit_SSD1306.h — OLED Driver
This driver translates graphics instructions into SSD1306-compatible display commands. Without this driver the RP2040 cannot understand the OLED memory architecture.
OLED Display Configuration
The firmware next defines the physical geometry of the display.
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define VIS_ZONE 44
| Constant | Purpose |
|---|---|
| SCREEN_WIDTH | OLED horizontal resolution |
| SCREEN_HEIGHT | OLED vertical resolution |
| VIS_ZONE | Reserved contour rendering zone |
The visualization area is intentionally restricted so the lower region of the display remains available for menus, labels, and instructions.
Adafruit_SSD1306 display(
SCREEN_WIDTH,
SCREEN_HEIGHT,
&Wire,
-1
);
This line constructs the OLED display object. All future graphics commands are routed through this object.
Global Memory Architecture
const int btnPin = 26;
struct Scan {
int y1;
int y2;
};
Scan library[5];
int savedTotal = 0;
int currentIdx = 0;
Push Button Pin
The push button is connected to GPIO pin 26. The firmware later configures this pin as an INPUT_PULLUP configuration.
Struct-Based Scan Storage
The firmware introduces a custom structure called Scan.
This behaves as a lightweight embedded database record.
Each stored scan contains:
- First contour coordinate
- Second contour coordinate
The array:
Scan library[5];
allocates memory for five contour profiles directly inside SRAM.
Structs allow multiple related variables to remain grouped together as a single logical object, making memory handling safer and easier to scale.
Finite State Machine Architecture
int currentState = 0;
int menuOption = 0;
| State | Meaning |
|---|---|
| 0 | Live View |
| 1 | Library Browser |
| 2 | Action Menu |
This design prevents conflicting interface behaviors. Only the logic relevant to the active state becomes executable.
Hardware Initialization — setup()
The setup function executes once during system startup. Its responsibility is to initialize every hardware subsystem.
void setup() {
Serial.begin(115200);
Wire.setSDA(6);
Wire.setSCL(7);
Wire.begin();
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
pinMode(btnPin, INPUT_PULLUP);
display.setTextColor(WHITE);
}
Serial.begin(115200)
Initializes UART communication between the RP2040 and the laptop. The baud rate must exactly match the Python listener configuration.
Wire.begin()
Activates the RP2040 internal I2C controller hardware. Without this call the OLED receives no communication frames.
display.begin(..., 0x3C)
Initializes the OLED using I2C address 0x3C. This behaves like the display's network address on the local bus.
Main Runtime Loop
The loop function behaves as the embedded runtime engine. It continuously:
- Samples analog inputs
- Processes button events
- Updates the display
- Handles menu states
int v1 = map(analogRead(A2), 0, 1023, VIS_ZONE, 8);
int v2 = map(analogRead(A1), 0, 1023, VIS_ZONE, 8);
The potentiometers generate raw ADC values from 0–1023. The map function rescales those values into OLED pixel coordinates.
The firmware continuously translates physical mechanical movement into graphical screen coordinates in real time.
Multi-Function Button System
A single push button controls:
- Saving scans
- Opening the library
- Entering menus
- Scrolling menu options
- Confirming actions
if (digitalRead(btnPin) == LOW)
The firmware constantly checks whether the button transitions LOW. If pressed, the system begins timing the interaction duration.
if (duration > 800)
Any press longer than 800 milliseconds becomes a long press event.
if (millis() - lastClick < 300)
The firmware compares timestamps to determine whether the interaction was:
- Single click
- Double click
UART Communication & Coordinate Streaming
This section performs the primary networking operation of the firmware. The RP2040 converts graphical contour data into structured coordinate packets.
Serial.println("START_DATA");
This line acts as a transmission handshake key. The Python application listens for this exact phrase before beginning extraction.
float mmY1 = map(library[i].y1, VIS_ZONE, 8, 0, MAX_TRAVEL);
float mmY2 = map(library[i].y2, VIS_ZONE, 8, 0, MAX_TRAVEL);
The firmware converts OLED pixel coordinates back into real mechanical distances measured in millimeters.
Serial.print("SCAN:");
Serial.print(i);
Serial.print(",");
Serial.print(x1);
Serial.print(",");
Serial.print(mmY1);
Serial.print(",");
Serial.print(x2);
Serial.print(",");
Serial.println(mmY2);
Each scan becomes a structured UART packet formatted as:
SCAN:0,0.0,12.5,3.0,15.2
| Packet Field | Meaning |
|---|---|
| SCAN:0 | Scan Identifier |
| 0.0 | First X Coordinate |
| 12.5 | First Y Coordinate |
| 3.0 | Second X Coordinate |
| 15.2 | Second Y Coordinate |
Embedded Graphics Rendering
display.drawLine(15, y1, 113, y2, WHITE);
display.fillCircle(15, y1, 2, WHITE);
display.fillCircle(113, y2, 2, WHITE);
| Function | Purpose |
|---|---|
| drawLine() | Creates contour segment |
| fillCircle() | Draws contour endpoints |
| setCursor() | Positions text cursor |
| print() | Renders text labels |
The OLED therefore behaves as a live vector visualization interface.
Complete Firmware Architecture Summary
| Subsystem | Main Functions | Purpose |
|---|---|---|
| Analog Input | analogRead() | Reads potentiometer positions |
| Scaling Engine | map() | Converts voltages into screen coordinates |
| I2C Network | Wire.begin() | Activates OLED communication |
| Graphics Engine | drawLine() | Renders contour geometry |
| UI State Machine | currentState | Controls embedded interface flow |
| Memory Buffer | Scan library[5] | Stores contour snapshots |
| UART Network | Serial.print() | Streams coordinate packets |
| Data Packaging | CSV Formatting | Structures geometry frames |
This firmware demonstrates how a small RP2040 microcontroller can simultaneously behave as a sensing node, graphics processor, memory manager, embedded UI controller, and distributed communication endpoint.