%%{init: {'theme': 'base', 'themeVariables': { 'primaryColor': '#2C3E50', 'primaryTextColor': '#fff', 'primaryBorderColor': '#16A085', 'lineColor': '#16A085', 'secondaryColor': '#E67E22', 'tertiaryColor': '#ECF0F1'}}}%%
stateDiagram-v2
[*] --> IDLE
IDLE --> SENSING: Timer expires
SENSING --> TRANSMITTING: Data ready
TRANSMITTING --> SLEEPING: Send complete
SLEEPING --> IDLE: Wake up
TRANSMITTING --> IDLE: Send failed
1534 Software Prototyping: Architecture Patterns
1534.1 Learning Objectives
By the end of this chapter, you will be able to:
- Implement Bare-Metal Architecture: Write efficient single-loop firmware for simple applications
- Design State Machines: Organize firmware using states and transitions for predictable behavior
- Build Event-Driven Systems: Use interrupts and callbacks for responsive, power-efficient code
- Understand RTOS Concepts: Apply real-time operating systems for complex multi-tasking
1534.2 Prerequisites
Before diving into this chapter, you should be familiar with:
- Programming Languages for IoT: Understanding of C/C++ for embedded systems
- Development Environments: Ability to compile and upload firmware
1534.3 Bare-Metal Architecture
Description: Direct programming without operating system, executing in a single infinite loop.
Structure:
void setup() {
// Initialize hardware
initHardware();
initSensors();
initCommunication();
}
void loop() {
// Main program logic
readSensors();
processData();
transmitData();
checkCommands();
delay(1000);
}Advantages: - Simple and predictable - Minimal overhead - Full control over execution - Easy to understand
Disadvantages: - Blocking operations problematic - Difficult to manage complex timing - Poor scalability
Best For: - Simple applications - Learning and experimentation - Resource-constrained devices
1534.4 State Machine Architecture
Description: Organizing code around states and transitions, common in embedded systems.
State Diagram:
Implementation:
enum State {
IDLE,
SENSING,
TRANSMITTING,
SLEEPING
};
State currentState = IDLE;
void loop() {
switch(currentState) {
case IDLE:
if (shouldSense()) {
currentState = SENSING;
}
break;
case SENSING:
readSensors();
currentState = TRANSMITTING;
break;
case TRANSMITTING:
if (sendData()) {
currentState = SLEEPING;
}
break;
case SLEEPING:
enterSleep(SLEEP_TIME);
currentState = IDLE;
break;
}
}Advantages: - Clear logic flow - Easier to debug - Predictable behavior - Power management friendly
Disadvantages: - Can become complex with many states - Requires careful design
Best For: - Battery-powered devices - Applications with distinct modes - Safety-critical systems
1534.5 Event-Driven Architecture
Description: Responding to events (interrupts, messages, timers) rather than polling.
Implementation:
volatile bool dataReady = false;
void setup() {
attachInterrupt(digitalPinToInterrupt(SENSOR_PIN),
sensorISR, RISING);
}
void sensorISR() {
dataReady = true;
}
void loop() {
if (dataReady) {
processData();
dataReady = false;
}
// MCU can sleep when no events
lowPowerMode();
}Event Queue Pattern:
struct Event {
uint8_t type;
uint32_t data;
};
Queue<Event> eventQueue;
void buttonISR() {
Event e = {EVENT_BUTTON, millis()};
eventQueue.push(e);
}
void timerISR() {
Event e = {EVENT_TIMER, 0};
eventQueue.push(e);
}
void loop() {
if (!eventQueue.isEmpty()) {
Event e = eventQueue.pop();
handleEvent(e);
} else {
enterLowPowerMode();
}
}Advantages: - Responsive to external events - Power-efficient (sleep when idle) - Scales well with complexity
Disadvantages: - Interrupt handling complexity - Potential race conditions - Debugging can be challenging
Best For: - Interactive devices - Low-power applications - Complex systems with multiple inputs
1534.6 RTOS-Based Architecture
Description: Using Real-Time Operating System for task scheduling and resource management.
FreeRTOS Example:
#include <Arduino.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
void sensorTask(void *parameter) {
while(1) {
readSensors();
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
}
void communicationTask(void *parameter) {
while(1) {
sendData();
vTaskDelay(5000 / portTICK_PERIOD_MS);
}
}
void setup() {
xTaskCreate(sensorTask, "Sensor", 2048, NULL, 1, NULL);
xTaskCreate(communicationTask, "Comm", 2048, NULL, 1, NULL);
}
void loop() {
// Empty - tasks handle everything
}RTOS Primitives:
| Primitive | Purpose | Example Use |
|---|---|---|
| Task | Independent execution unit | Sensor reading, communication |
| Queue | Inter-task data passing | Sensor data to network task |
| Semaphore | Resource synchronization | SPI bus sharing |
| Mutex | Mutual exclusion | Shared data protection |
| Timer | Periodic callbacks | Heartbeat, watchdog |
Advantages: - Multiple concurrent tasks - Priority-based scheduling - Resource protection (mutexes, semaphores) - Professional development paradigm
Disadvantages: - Memory overhead - Complexity - Steeper learning curve
Best For: - Complex multi-function devices - Professional embedded development - Systems requiring strict timing
Decision context: When deciding whether to use a Real-Time Operating System (FreeRTOS, Zephyr) or write bare-metal firmware for your IoT device
| Factor | RTOS | Bare-Metal |
|---|---|---|
| Code Size Overhead | 10-50 KB | 0 KB |
| RAM Overhead | 1-4 KB per task | Minimal |
| Context Switch Time | 1-10 microseconds | N/A |
| Timing Predictability | Bounded (configurable priorities) | Deterministic |
| Multi-tasking | Native support | Manual implementation |
| Power Management | RTOS tickless idle modes | Full control |
| Learning Curve | Moderate | Low (scales poorly) |
| Debugging Complexity | Higher (race conditions) | Lower |
Choose RTOS when: - Device has 3+ concurrent activities - Timing requirements vary by priority - Team-maintained code (not solo developer) - Using ESP-IDF, Zephyr, or professional frameworks - Device has 64+ KB RAM available
Choose Bare-Metal when: - Extreme power constraints - Very limited memory (<32 KB RAM) - Single primary function - Hard real-time with sub-microsecond deadlines - Prototyping with Arduino for quick validation
Default recommendation: Start with bare-metal for prototyping, migrate to RTOS when adding concurrent features.
1534.7 Architecture Selection Flowchart
%%{init: {'theme': 'base', 'themeVariables': { 'primaryColor': '#2C3E50', 'primaryTextColor': '#fff', 'primaryBorderColor': '#16A085', 'lineColor': '#16A085', 'secondaryColor': '#E67E22', 'tertiaryColor': '#ECF0F1'}}}%%
flowchart TD
START["Select Architecture"] --> Q1{"How many<br/>concurrent tasks?"}
Q1 -->|"1-2"| Q2{"Power<br/>critical?"}
Q1 -->|"3+"| RTOS["RTOS Architecture"]
Q2 -->|"Yes"| Q3{"Need instant<br/>response?"}
Q2 -->|"No"| BARE["Bare-Metal"]
Q3 -->|"Yes"| EVENT["Event-Driven"]
Q3 -->|"No"| STATE["State Machine"]
style START fill:#E67E22,stroke:#2C3E50,color:#fff
style RTOS fill:#16A085,stroke:#2C3E50,color:#fff
style BARE fill:#16A085,stroke:#2C3E50,color:#fff
style EVENT fill:#16A085,stroke:#2C3E50,color:#fff
style STATE fill:#16A085,stroke:#2C3E50,color:#fff
1534.8 Architecture Comparison
| Factor | Bare-Metal | State Machine | Event-Driven | RTOS |
|---|---|---|---|---|
| Complexity | Low | Medium | Medium | High |
| Memory Overhead | None | Low | Low | High |
| Power Efficiency | Variable | Good | Excellent | Good |
| Responsiveness | Poor | Medium | Excellent | Good |
| Scalability | Poor | Medium | Good | Excellent |
| Debug Difficulty | Low | Low | Medium | High |
| Best RAM | <16KB | 16-64KB | 16-64KB | >64KB |
1534.9 Knowledge Check
1534.10 Whatโs Next
The next section covers Libraries and Frameworks, where youโll learn about sensor libraries, communication protocols, display libraries, and version control practices for IoT firmware development.