Skip to main content

Embedded Systems and IoT - Arduino, Microcontrollers, Real-Time Systems Complete Guide

By · · 8 min read

Embedded Systems and IoT - Complete Mastery

Introduction: Computing Beyond Computers

Embedded systems are specialized computing devices dedicated to performing specific tasks. Unlike general-purpose computers, they are purpose-built with constraints:

  • Limited Resources: Low power, small memory, restricted processing
  • Real-Time Requirements: Must respond within time constraints
  • Reliability: Often work unsupervised for years
  • Cost-Sensitive: Mass production demands efficiency

Common Applications:

  • Smart home devices (Alexa, Google Home)
  • Automotive systems (engine control, infotainment)
  • Medical devices (pacemakers, monitors)
  • Industrial IoT (sensors, controllers)
  • Consumer electronics (smartwatches, fitness trackers)
  • Mobile devices (phones, tablets)

This guide covers microcontroller fundamentals and IoT development.


1. Microcontroller Basics

Arduino Platform

Arduino: Beginner-friendly microcontroller platform
- Open-source hardware and software
- Easy programming (simplified C/C++)
- Large community and libraries
- Affordable boards ($20-50)

Popular Boards:
Arduino Uno: Most common, 16KB RAM, 32KB Flash
Arduino MKR WiFi 1010: WiFi connectivity, 32KB RAM
Arduino Due: High performance, 96KB RAM
Arduino Nano: Compact, embedded use

Hardware Components

Essential Parts:
1. Microcontroller (MCU): Brain of the system
   - Processor: CPU core
   - Memory: RAM, Flash, EEPROM
   - I/O Pins: Digital, Analog

2. Power Supply: Battery or external

3. Crystal Oscillator: Clock signal for timing

4. Reset Circuit: Boot microcontroller

5. I/O Interfaces:
   - Digital pins (HIGH/LOW)
   - Analog pins (0-1023 values)
   - Serial pins (communication)

6. Sensors: Collect data
   - Light (LDR)
   - Temperature (DHT22)
   - Motion (PIR)
   - Distance (HC-SR04 ultrasonic)
   - Accelerometer (MPU6050)

7. Actuators: Perform actions
   - LED: Visual output
   - Motor: Movement
   - Relay: Switch high voltage
   - Buzzer: Sound output

Arduino Development Environment

// Arduino sketch structure
// setup(): Runs once at startup
void setup() {
  Serial.begin(9600);  // Serial communication
  pinMode(LED_PIN, OUTPUT);  // Set pin mode
  pinMode(BUTTON_PIN, INPUT);
}

// loop(): Runs repeatedly
void loop() {
  int buttonState = digitalRead(BUTTON_PIN);
  
  if (buttonState == HIGH) {
    digitalWrite(LED_PIN, HIGH);  // Turn LED on
    Serial.println("LED ON");
  } else {
    digitalWrite(LED_PIN, LOW);   // Turn LED off
    Serial.println("LED OFF");
  }
  
  delay(100);  // Wait 100ms
}

// Pin modes:
// INPUT: Read from pin
// OUTPUT: Write to pin
// INPUT_PULLUP: Read with internal pullup resistor

// Digital I/O:
// digitalWrite(pin, HIGH/LOW)  - Set digital output
// digitalRead(pin)              - Read digital input

// Analog I/O (0-1023):
// analogWrite(pin, value)       - PWM output (0-255)
// analogRead(pin)               - Read analog input

2. Sensors and Sensor Integration

Analog Sensors

// Temperature Sensor (LM35)
// 10mV per degree Celsius

const int TEMP_PIN = A0;

void setup() {
  Serial.begin(9600);
}

void loop() {
  int rawValue = analogRead(TEMP_PIN);
  
  // Convert to voltage (0-5V)
  float voltage = (rawValue / 1023.0) * 5.0;
  
  // Convert to temperature
  // LM35: 10mV/°C, so 0.01V = 1°C
  float temperature = voltage * 100.0;
  
  Serial.print("Temperature: ");
  Serial.print(temperature);
  Serial.println("°C");
  
  delay(1000);  // Read every second
}

Digital Sensors

// Ultrasonic Sensor (HC-SR04)
// Measure distance by measuring echo time

const int TRIG_PIN = 9;
const int ECHO_PIN = 10;

void setup() {
  Serial.begin(9600);
  pinMode(TRIG_PIN, OUTPUT);
  pinMode(ECHO_PIN, INPUT);
}

void loop() {
  // Send ultrasonic pulse
  digitalWrite(TRIG_PIN, LOW);
  delayMicroseconds(2);
  digitalWrite(TRIG_PIN, HIGH);
  delayMicroseconds(10);
  digitalWrite(TRIG_PIN, LOW);
  
  // Measure echo time
  long duration = pulseIn(ECHO_PIN, HIGH);
  
  // Calculate distance
  // Speed of sound = 343 m/s = 0.0343 cm/µs
  // Distance = speed × time / 2 (round trip)
  float distance = (duration * 0.0343) / 2;
  
  Serial.print("Distance: ");
  Serial.print(distance);
  Serial.println("cm");
  
  delay(100);
}

I2C Communication

// I2C: Two-wire protocol (SDA, SCL)
// Multiple devices on same bus
// Addresses identify devices

#include <Wire.h>

const int MPU6050_ADDR = 0x68;  // I2C address

void setup() {
  Serial.begin(9600);
  Wire.begin();  // Join I2C bus
}

void loop() {
  // Read from accelerometer (axes: X, Y, Z)
  Wire.beginTransmission(MPU6050_ADDR);
  Wire.write(0x3B);  // Accelerometer data register
  Wire.endTransmission(false);
  
  // Request 6 bytes (2 per axis)
  Wire.requestFrom(MPU6050_ADDR, 6, true);
  
  if (Wire.available() >= 6) {
    int16_t accelX = Wire.read() << 8 | Wire.read();
    int16_t accelY = Wire.read() << 8 | Wire.read();
    int16_t accelZ = Wire.read() << 8 | Wire.read();
    
    Serial.print("X: "); Serial.print(accelX);
    Serial.print(" Y: "); Serial.print(accelY);
    Serial.print(" Z: "); Serial.println(accelZ);
  }
  
  delay(500);
}

SPI Communication

// SPI: Serial Peripheral Interface
// Faster than I2C, separate chip selects
// Master-slave architecture

#include <SPI.h>

const int CS_PIN = 10;
const int MOSI = 11;  // Master Out, Slave In
const int MISO = 12;  // Master In, Slave Out
const int CLK = 13;   // Clock

void setup() {
  Serial.begin(9600);
  pinMode(CS_PIN, OUTPUT);
  digitalWrite(CS_PIN, HIGH);  // Chip select inactive
  
  SPI.begin();
  SPI.setClockDivider(SPI_CLOCK_DIV16);
}

void loop() {
  // Communicate with SPI slave
  digitalWrite(CS_PIN, LOW);  // Select slave
  
  byte response = SPI.transfer(0xA5);  // Send byte, receive byte
  
  digitalWrite(CS_PIN, HIGH);  // Deselect slave
  
  Serial.println(response, HEX);
  
  delay(1000);
}

3. Communication Protocols

UART (Serial Communication)

// Send/receive text over serial port
// Baud rate: 9600, 115200, etc.

void setup() {
  Serial.begin(9600);  // Initialize serial
}

void loop() {
  // Send data
  Serial.print("Hello ");
  Serial.println("World");  // println adds newline
  
  // Receive data
  if (Serial.available() > 0) {
    char received = Serial.read();
    Serial.print("Received: ");
    Serial.println(received);
  }
  
  delay(1000);
}

// Serial Monitor: View output in Arduino IDE
// Tools → Serial Monitor → 9600 baud

WiFi Connectivity

// Arduino MKR WiFi 1010 with WiFi
#include <WiFi.h>

char ssid[] = "NetworkName";
char pass[] = "Password";

void setup() {
  Serial.begin(9600);
  
  // Connect to WiFi
  Serial.println("Connecting to WiFi...");
  
  while (WiFi.begin(ssid, pass) != WL_CONNECTED) {
    Serial.print(".");
    delay(1000);
  }
  
  Serial.println("\nWiFi connected!");
  Serial.print("IP: ");
  Serial.println(WiFi.localIP());
}

void loop() {
  // Use WiFi for IoT
}

MQTT (IoT Communication)

// Lightweight publish-subscribe protocol
// Perfect for IoT devices

#include <WiFi.h>
#include <ArduinoMqttClient.h>

WiFiClient wifiClient;
MqttClient mqttClient(wifiClient);

const char broker[] = "mqtt.example.com";
int port = 1883;

const char topic[] = "home/temperature";

void setup() {
  Serial.begin(9600);
  WiFi.begin(ssid, pass);  // Connect as before
  
  // Connect to MQTT broker
  while (!mqttClient.connect(broker, port)) {
    Serial.print("MQTT connection failed: ");
    Serial.println(mqttClient.connectError());
    delay(1000);
  }
  
  Serial.println("MQTT connected!");
}

void loop() {
  // Read temperature sensor
  int temperature = readTemperature();
  
  // Publish to MQTT topic
  String payload = String(temperature);
  mqttClient.publish(topic, payload);
  
  Serial.print("Published: ");
  Serial.println(payload);
  
  delay(5000);  // Publish every 5 seconds
}

4. Real-Time Operating Systems (RTOS)

FreeRTOS Basics

// Real-time OS for embedded systems
// Enables multitasking on single-core MCU

#include <Arduino.h>
#include <FreeRTOS.h>
#include <task.h>

// Task 1: Read sensor
void sensorTask(void *pvParameters) {
  while (1) {
    int reading = analogRead(A0);
    Serial.print("Sensor: ");
    Serial.println(reading);
    vTaskDelay(pdMS_TO_TICKS(1000));  // Delay 1 second
  }
}

// Task 2: Control output
void controlTask(void *pvParameters) {
  while (1) {
    digitalWrite(LED_PIN, HIGH);
    vTaskDelay(pdMS_TO_TICKS(500));
    digitalWrite(LED_PIN, LOW);
    vTaskDelay(pdMS_TO_TICKS(500));
  }
}

void setup() {
  Serial.begin(9600);
  pinMode(LED_PIN, OUTPUT);
  
  // Create tasks
  xTaskCreate(
    sensorTask,      // Function to execute
    "SensorTask",    // Task name
    128,             // Stack size (bytes)
    NULL,            // Parameters
    1,               // Priority (0=low, 4=high)
    NULL             // Task handle
  );
  
  xTaskCreate(
    controlTask,
    "ControlTask",
    128,
    NULL,
    1,
    NULL
  );
}

void loop() {
  // FreeRTOS scheduler runs tasks
  // loop() not needed with FreeRTOS
}

// Benefits:
// - Tasks run "concurrently"
// - Scheduler switches between tasks
// - No need for complex state machines
// - Responsive real-time behavior

Task Synchronization

#include <FreeRTOS.h>
#include <task.h>
#include <queue.h>

// Queue for communication between tasks
QueueHandle_t dataQueue;

void producerTask(void *pvParameters) {
  while (1) {
    int sensorValue = analogRead(A0);
    
    // Send to queue
    xQueueSend(dataQueue, &sensorValue, portMAX_DELAY);
    
    vTaskDelay(pdMS_TO_TICKS(500));
  }
}

void consumerTask(void *pvParameters) {
  int receivedValue;
  
  while (1) {
    // Receive from queue
    if (xQueueReceive(dataQueue, &receivedValue, portMAX_DELAY)) {
      Serial.print("Received: ");
      Serial.println(receivedValue);
    }
  }
}

void setup() {
  Serial.begin(9600);
  
  // Create queue (10 items, size of int)
  dataQueue = xQueueCreate(10, sizeof(int));
  
  xTaskCreate(producerTask, "Producer", 128, NULL, 1, NULL);
  xTaskCreate(consumerTask, "Consumer", 128, NULL, 1, NULL);
}

void loop() {}

5. IoT Architecture

Edge Device (Arduino)

┌─ Sensors ─┐
│ • Temperature
│ • Humidity
│ • Motion
└───────────┘
      ↓
┌─ Edge Device ─────┐
│ Arduino/ESP32      │
│ • Process data     │
│ • Local decision   │
│ • Filter noise     │
└──────┬─────────────┘
       ↓
   WiFi/4G/LoRaWAN
       ↓
┌─ Cloud Server ────┐
│ • Store history    │
│ • Analytics        │
│ • Machine Learning │
└────────────────────┘

IoT Communication Protocols Comparison

Protocol    Range      Power     Bandwidth   Latency   Use Case
Bluetooth   10m        Medium    1Mbps       Low       Wearables
WiFi        100m       High      54Mbps      Low       Home automation
LoRaWAN     10km       Very Low  50kbps      Medium    Long-range sensors
NB-IoT      10km       Very Low  250kbps     Medium    Cellular IoT
Zigbee      100m       Very Low  250kbps     Medium    Smart home
6LoWPAN     100m       Very Low  250kbps     Medium    Smart grid

Cloud Integration Example

// Arduino sending data to cloud
// Using HTTPClient library

#include <WiFi.h>
#include <HTTPClient.h>

void setup() {
  Serial.begin(9600);
  WiFi.begin(ssid, pass);
  
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
  }
  
  Serial.println("Connected to WiFi");
}

void loop() {
  // Read sensor
  float temperature = readTemperature();
  float humidity = readHumidity();
  
  // Create JSON payload
  String jsonData = "{";
  jsonData += "\"temperature\":" + String(temperature) + ",";
  jsonData += "\"humidity\":" + String(humidity);
  jsonData += "}";
  
  // Send to cloud
  if (WiFi.status() == WL_CONNECTED) {
    HTTPClient http;
    
    http.begin("http://api.example.com/data");
    http.addHeader("Content-Type", "application/json");
    
    int httpCode = http.POST(jsonData);
    
    if (httpCode == 200) {
      Serial.println("Data sent successfully");
    } else {
      Serial.print("Error: ");
      Serial.println(httpCode);
    }
    
    http.end();
  }
  
  delay(10000);  // Send every 10 seconds
}

6. Power Management

Battery Life Optimization

// Sleep modes save power when device inactive

void setup() {
  Serial.begin(9600);
  pinMode(BUTTON_PIN, INPUT);
  attachInterrupt(digitalPinToInterrupt(BUTTON_PIN), wakeUp, RISING);
}

volatile boolean woken = false;

void wakeUp() {
  woken = true;
}

void loop() {
  // Do work
  int reading = analogRead(A0);
  Serial.println(reading);
  
  // Enter sleep mode (power down, timer off)
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);
  sleep_enable();
  sleep_mode();
  sleep_disable();
  
  // Woke up from interrupt or timer
  if (woken) {
    Serial.println("Button pressed!");
    woken = false;
  }
}

// Power consumption:
// Wake: 50mA
// Sleep: 0.1mA (500x reduction)
// Battery life: 500x longer!

Power Budget Calculation

Battery capacity: 2000 mAh
Device current (awake): 50 mA
Device current (sleep): 0.1 mA

Scenario 1: Always awake
Battery life = 2000 mAh / 50 mA = 40 hours

Scenario 2: 90% sleep, 10% awake
Average current = 0.9 × 0.1 + 0.1 × 50 = 5.09 mA
Battery life = 2000 / 5.09 = 393 hours (16 days!)

7. Real-World IoT Project: Smart Temperature Monitor

// Complete project: Temperature monitoring with cloud storage

#include <WiFi.h>
#include <HTTPClient.h>
#include <DHT.h>

#define DHTTYPE DHT22
#define DHTPIN 4

DHT dht(DHTPIN, DHTTYPE);

void setup() {
  Serial.begin(9600);
  
  // WiFi
  WiFi.begin(ssid, pass);
  while (WiFi.status() != WL_CONNECTED) delay(500);
  
  // Sensor
  dht.begin();
  
  // Pin modes
  pinMode(LED_PIN, OUTPUT);
  pinMode(BUZZER_PIN, OUTPUT);
}

void loop() {
  // Read temperature and humidity
  float temperature = dht.readTemperature();
  float humidity = dht.readHumidity();
  
  if (isnan(temperature) || isnan(humidity)) {
    Serial.println("Sensor error");
    return;
  }
  
  // Check thresholds
  if (temperature > 30) {
    digitalWrite(LED_PIN, HIGH);
    digitalWrite(BUZZER_PIN, HIGH);
    delay(100);
    digitalWrite(BUZZER_PIN, LOW);
  } else {
    digitalWrite(LED_PIN, LOW);
  }
  
  // Send to cloud
  String payload = "{\"temp\":" + String(temperature) + 
                   ",\"humid\":" + String(humidity) + "}";
  
  HTTPClient http;
  http.begin("http://api.example.com/readings");
  http.addHeader("Content-Type", "application/json");
  http.POST(payload);
  http.end();
  
  // Display on serial
  Serial.print("Temp: ");
  Serial.print(temperature);
  Serial.print("°C, Humidity: ");
  Serial.print(humidity);
  Serial.println("%");
  
  delay(5000);  // Read every 5 seconds
}

Key Takeaways

  1. Arduino = Entry Point - Best for learning embedded systems
  2. Sensors first - Understand analog/digital I/O thoroughly
  3. Communication critical - I2C, SPI, UART, WiFi, MQTT
  4. Power matters - Sleep modes crucial for battery life
  5. Real-time = RTOS - Multitasking essential for complex systems
  6. Cloud integration - IoT = device + cloud
  7. Interrupts powerful - Handle time-critical events
  8. Reliability essential - Embedded systems must run unattended
  9. Test extensively - Hardware bugs are expensive
  10. Community resources - Libraries available, use them!