1537  Software Prototyping: Programming Languages

1537.1 Learning Objectives

By the end of this chapter, you will be able to:

  • Compare IoT Programming Languages: Evaluate C/C++, Python, JavaScript, and Rust for embedded development
  • Select Languages by Constraint: Choose appropriate languages based on memory, real-time, and team factors
  • Write Cross-Platform Code: Understand language portability considerations
  • Evaluate Trade-offs: Balance development speed against runtime performance

1537.2 Prerequisites

Before diving into this chapter, you should be familiar with:


1537.3 C/C++ for Microcontrollers

Why C/C++: - Direct hardware control - Minimal overhead and high efficiency - Real-time determinism - Industry standard for embedded systems

Characteristics: - Manual memory management - Low-level hardware access - Compiled to machine code - Small binary sizes

Arduino C++ Example:

#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>

Adafruit_BME280 bme;

void setup() {
  Serial.begin(115200);
  if (!bme.begin(0x76)) {
    Serial.println("BME280 not found!");
    while (1);
  }
}

void loop() {
  float temp = bme.readTemperature();
  float pressure = bme.readPressure() / 100.0F;
  float humidity = bme.readHumidity();

  Serial.print("Temp: ");
  Serial.print(temp);
  Serial.print(" C, Pressure: ");
  Serial.print(pressure);
  Serial.print("hPa, Humidity: ");
  Serial.print(humidity);
  Serial.println("%");

  delay(2000);
}

Best For: - Resource-constrained microcontrollers - Real-time applications - Power-critical battery devices - Performance-sensitive applications

1537.4 Python for IoT

Why Python: - Rapid development - Extensive libraries - Readable, maintainable code - Strong ecosystem for data analysis and ML

MicroPython: Subset of Python 3 running on microcontrollers (ESP32, ESP8266, Raspberry Pi Pico).

Example:

from machine import Pin, I2C
import bme280
import time

i2c = I2C(scl=Pin(22), sda=Pin(21))
bme = bme280.BME280(i2c=i2c)

while True:
    temp, pressure, humidity = bme.read_compensated_data()
    print(f"Temp: {temp/100:.2f} C, Pressure: {pressure/25600:.2f}hPa, Humidity: {humidity/1024:.2f}%")
    time.sleep(2)

CPython (Raspberry Pi): Full Python implementation on Linux-based systems.

Example:

import board
import adafruit_bme280

i2c = board.I2C()
bme280 = adafruit_bme280.Adafruit_BME280_I2C(i2c)

while True:
    print(f"Temp: {bme280.temperature:.2f} C")
    print(f"Humidity: {bme280.humidity:.2f}%")
    print(f"Pressure: {bme280.pressure:.2f}hPa")
    time.sleep(2)

Best For: - Raspberry Pi and capable microprocessors - Rapid prototyping and experimentation - Data processing and analysis - Integration with ML frameworks

1537.5 JavaScript/Node.js

Why JavaScript: - Familiar to web developers - Asynchronous I/O model - Rich ecosystem (npm packages) - Full-stack IoT (device to cloud to web app)

Johnny-Five Framework: JavaScript robotics and IoT platform.

Example:

const five = require("johnny-five");
const board = new five.Board();

board.on("ready", function() {
  const temperature = new five.Thermometer({
    controller: "BME280"
  });

  temperature.on("change", function() {
    console.log(`${this.celsius} C`);
  });
});

Best For: - Web developer-friendly IoT - Real-time applications (WebSockets) - Prototyping with rapid iteration - Projects integrating with web services

1537.6 Rust for Embedded

Why Rust: - Memory safety without garbage collection - Zero-cost abstractions - Fearless concurrency - Growing embedded ecosystem

Example:

#![no_std]
#![no_main]

use cortex_m_rt::entry;
use panic_halt as _;
use stm32f4xx_hal::{prelude::*, pac};

#[entry]
fn main() -> ! {
    let dp = pac::Peripherals::take().unwrap();
    let gpioa = dp.GPIOA.split();
    let mut led = gpioa.pa5.into_push_pull_output();

    loop {
        led.toggle();
        cortex_m::asm::delay(8_000_000);
    }
}

Best For: - Safety-critical applications - Long-lived deployed systems - Projects requiring both performance and safety - Advanced embedded developers

1537.7 Language Selection Criteria

1537.7.1 By Resource Constraints

RAM Available Recommended Language
Severe (<16KB) C only
Limited (16-64KB) C/C++
Moderate (64KB-1MB) C/C++, MicroPython
Abundant (>1MB) Python, JavaScript, any

1537.7.2 By Development Speed

Priority Best Choice
Rapid prototyping Python, JavaScript
Production optimization C/C++, Rust
Team familiarity Match to existing skills

1537.7.3 By Team Skills

Team Background Recommended Path
Embedded engineers C/C++
Web developers JavaScript, then C
Data scientists Python
Systems programmers Rust

1537.7.4 By Real-Time Requirements

Requirement Language Choice
Hard real-time (<1ms) C/C++, Rust
Soft real-time (1-100ms) Any with proper design
No real-time Python, JavaScript

1537.8 Language Comparison Table

Factor C/C++ Python JavaScript Rust
Memory Footprint Tiny (2-20KB) Large (100-500KB) Medium (50-200KB) Small (10-50KB)
Development Speed Slow Fast Fast Medium
Safety Manual Automatic GC Automatic GC Compile-time
Learning Curve Medium Easy Easy Hard
Library Ecosystem Excellent Excellent Good Growing
Real-time Support Excellent Poor Poor Excellent
WarningTradeoff: Python vs C for Microcontrollers

Python (MicroPython): - 10x faster development - 10x larger memory footprint - 10x slower execution - Interactive REPL for debugging

C/C++: - Slower development - Minimal memory overhead - Maximum performance - Compile-test-debug cycle

Recommendation: Start with MicroPython for proof-of-concept, migrate to C when performance or memory becomes critical.

1537.9 Knowledge Check

Question: You’re using the Arduino framework to prototype a battery-powered environmental sensor. After deployment, the device drains batteries 10x faster than expected. What is the MOST likely cause related to the Arduino framework?

Explanation: Arduino framework prioritizes ease of use over power efficiency. By default, the MCU runs continuously at full speed with all peripherals enabled. Deep sleep modes require explicit configuration using platform-specific libraries (ESP.deepSleep() for ESP32, LowPower library for AVR). The framework doesn’t automatically disable peripherals or enter low-power states. For production battery-powered devices, you must: disable unused peripherals, use sleep modes between measurements, and consider switching to vendor SDKs (ESP-IDF, STM32Cube) for fine-grained power control.

1537.10 What’s Next

The next section covers Software Architecture Patterns, where you’ll learn about bare-metal, state machine, event-driven, and RTOS-based architectures for organizing your IoT firmware effectively.