- Published on
組込みシステム(くみこみしすてむ)&IoTプログラミング完全ガイド2025:ファームウェア、RTOS、通信プロトコル
- Authors

- Name
- Youngju Kim
- @fjvbn20031
はじめに:なぜ組込み/IoTか?
2025年現在(げんざい)、世界のIoTデバイス数(すう)は約(やく)300億(おく)を超(こ)えました。スマートホーム、産業(さんぎょう)センサー、ウェアラブル、自動運転車(じどううんてんしゃ)、スマートファクトリーに至(いた)るまで、組込みシステムは現代(げんだい)インフラの中核(ちゅうかく)です。一般的(いっぱんてき)なアプリ開発(かいはつ)と組込み開発は根本的(こんぽんてき)に異(こと)なる考(かんが)え方(かた)を必要(ひつよう)とします。メモリはKB単位(たんい)に限(かぎ)られ、CPUはMHz級(きゅう)、数年(すうねん)バッテリー動作(どうさ)、そしてリアルタイム制約(せいやく)を満(み)たさなければなりません。
本(ほん)ガイドでは組込みシステムの基礎(きそ)からIoT本番(ほんばん)システムまでフルスタックで扱(あつか)います。マイコン選定(せんてい)、開発環境(かんきょう)、RTOSベースのマルチタスク、ハードウェアインターフェース、センサー統合(とうごう)、IoTプロトコル、OTA、電力(でんりょく)管理(かんり)、Edge AI、セキュリティまで。
1. 組込みシステム vs 一般(いっぱん)コンピューティング
1.1 核心(かくしん)の違(ちが)い
| 項目(こうもく) | 一般 | 組込み |
|---|---|---|
| CPU速度(そくど) | GHz | MHz |
| RAM | GB | KB〜MB |
| 記憶(きおく) | SSD/HDD TB | Flash KB〜MB |
| OS | Linux/Windows/macOS | ベアメタル/RTOS/組込みLinux |
| 電力 | 数十〜数百W | μA〜mW |
| リアルタイム | なし/弱(よわ)い | 強(つよ)い |
| 寿命(じゅみょう) | 数年 | 10-20年 |
1.2 資源(しげん)制約(せいやく)の思考(しこう)
組込み開発者(かいはつしゃ)は常(つね)に資源制約を意識(いしき)します。
- メモリ割(わ)り当(あ)て:malloc/freeは最小限(さいしょうげん)、静的(せいてき)割り当てを優先(ゆうせん)
- スタック:再帰(さいき)制限、ローカル配列(はいれつ)サイズ
- CPU:浮動小数点(ふどうしょうすうてん)より整数(せいすう)
- 電力:ほとんどの時間(じかん)スリープ、割り込みで起床(きしょう)
1.3 リアルタイム要件(ようけん)
- Hard:締切(しめきり)遅延(ちえん)=システム失敗(しっぱい)(航空(こうくう)、医療(いりょう))
- Firm:遅延=結果(けっか)無効(むこう)(ライブ動画(どうが))
- Soft:遅延=品質(ひんしつ)低下(ていか)(マルチメディア)
2. マイコン(MCU)のラインナップ
2.1 主要(しゅよう)MCUプラットフォーム
| MCU | CPU | RAM | Flash | 価格(かかく) | 特徴(とくちょう) |
|---|---|---|---|---|---|
| Arduino Uno | AVR 16MHz | 2KB | 32KB | $5-25 | 学習(がくしゅう) |
| ESP32 | Xtensa 240MHz | 520KB | 4MB+ | $3-10 | Wi-Fi+BLE内蔵(ないぞう) |
| STM32F4 | Cortex-M4 168MHz | 192KB | 1MB | $5-15 | 産業用(さんぎょうよう) |
| STM32L0 | Cortex-M0+ 32MHz | 8KB | 64KB | $1-5 | 低電力 |
| RP2040 | Dual M0+ 133MHz | 264KB | 2MB | $4 | 低価格 |
| nRF52840 | M4F 64MHz | 256KB | 1MB | $5-15 | BLE/Thread/Zigbee |
| ESP32-S3 | Xtensa 240MHz | 512KB | 8MB+ | $5-12 | Wi-Fi 6 |
2.2 MCU選定(せんてい)ガイド
- IoT+Wi-Fi:ESP32
- 低電力バッテリー:nRF52840、STM32L
- 高性能(こうせいのう)制御(せいぎょ):STM32F4/F7
- 学習/試作(しさく):Arduino Uno、Pico
3. 開発環境構築(こうちく)
3.1 主要(しゅよう)IDE/ツールチェーン
Arduino IDE (初心者(しょしんしゃ)向(む)け)
const int LED_PIN = 13;
void setup() {
pinMode(LED_PIN, OUTPUT);
Serial.begin(115200);
}
void loop() {
digitalWrite(LED_PIN, HIGH);
delay(500);
digitalWrite(LED_PIN, LOW);
delay(500);
}
PlatformIO
[env:esp32dev]
platform = espressif32
board = esp32dev
framework = arduino
monitor_speed = 115200
ESP-IDF
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
static const char *TAG = "main";
void app_main(void) {
ESP_LOGI(TAG, "Hello ESP32!");
while (1) {
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
3.2 デバッグツール
- JTAG/SWD:ST-Link、J-Link
- GDB+OpenOCD
- シリアルモニター
- ロジックアナライザー
- オシロスコープ
4. RTOS(りあるたいむおーえす)の基礎
4.1 RTOS vs ベアメタル vs 組込みLinux
| 項目 | ベアメタル | RTOS | 組込みLinux |
|---|---|---|---|
| 複雑度(ふくざつど) | 低 | 中 | 高 |
| メモリ | 最小(さいしょう) | KB〜数百KB | MB+ |
| マルチタスク | スーパーループ | タスク | プロセス |
| 決定論(けっていろん) | 高 | 高 | 中 |
4.2 RTOS核心概念(かくしんがいねん)
- タスク:独立(どくりつ)実行(じっこう)単位、優先度(ゆうせんど)あり
- スケジューラ:プリエンプティブ優先度ベース
- コンテキストスイッチング
- セマフォ、ミューテックス、キュー、イベントフラグ、ソフトウェアタイマー
4.3 優先度反転(はんてん)
低優先度タスクが高優先度タスクをブロックする現象(げんしょう)。
- Priority Inheritance(優先度継承(けいしょう))
- Priority Ceiling(優先度上限(じょうげん))
1997年 Mars Pathfinderで発生(はっせい)した有名(ゆうめい)なバグ。
5. FreeRTOS 深(ふか)掘(ぼ)り
5.1 タスク生成(せいせい)
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
void sensor_task(void *pv) {
while (1) {
float temp = read_temperature();
printf("T: %.2f\n", temp);
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
void app_main(void) {
xTaskCreate(sensor_task, "sensor", 4096, NULL, 5, NULL);
}
5.2 キュー
QueueHandle_t xQ;
typedef struct {
float temperature;
float humidity;
} sensor_data_t;
void producer(void *pv) {
sensor_data_t d;
while (1) {
d.temperature = read_temp();
d.humidity = read_humid();
xQueueSend(xQ, &d, pdMS_TO_TICKS(100));
vTaskDelay(pdMS_TO_TICKS(500));
}
}
void consumer(void *pv) {
sensor_data_t d;
while (1) {
if (xQueueReceive(xQ, &d, portMAX_DELAY)) {
printf("T=%.1f H=%.1f\n", d.temperature, d.humidity);
}
}
}
5.3 セマフォとミューテックス
SemaphoreHandle_t xMutex;
int counter = 0;
void task(void *pv) {
while (1) {
if (xSemaphoreTake(xMutex, portMAX_DELAY)) {
counter++;
xSemaphoreGive(xMutex);
}
vTaskDelay(pdMS_TO_TICKS(100));
}
}
5.4 メモリ管理(かんり)
heap_1/2/3/4/5が提供(ていきょう)されます。heap_4が推奨(すいしょう)。
void *p = pvPortMalloc(256);
if (p) vPortFree(p);
printf("Free: %u\n", xPortGetFreeHeapSize());
6. Zephyr RTOS
Linux Foundationが管理(かんり)するオープンソースRTOS。450以上(いじょう)のボードサポート、Device Tree、Kconfig。
#include <zephyr/kernel.h>
#include <zephyr/drivers/gpio.h>
#define LED0_NODE DT_ALIAS(led0)
static const struct gpio_dt_spec led = GPIO_DT_SPEC_GET(LED0_NODE, gpios);
int main(void) {
gpio_pin_configure_dt(&led, GPIO_OUTPUT_ACTIVE);
while (1) {
gpio_pin_toggle_dt(&led);
k_msleep(1000);
}
return 0;
}
7. ハードウェアインターフェース
7.1 GPIO
#include "driver/gpio.h"
void cfg_gpio(void) {
gpio_config_t c = {
.intr_type = GPIO_INTR_POSEDGE,
.mode = GPIO_MODE_INPUT,
.pin_bit_mask = (1ULL << 4),
.pull_up_en = 1,
};
gpio_config(&c);
}
7.2 I2C
2線(SDA/SCL)、100kHz-3.4MHz。
#include "driver/i2c.h"
#define SENSOR_ADDR 0x76
esp_err_t i2c_init(void) {
i2c_config_t conf = {
.mode = I2C_MODE_MASTER,
.sda_io_num = 21,
.scl_io_num = 22,
.sda_pullup_en = GPIO_PULLUP_ENABLE,
.scl_pullup_en = GPIO_PULLUP_ENABLE,
.master.clk_speed = 400000,
};
i2c_param_config(0, &conf);
return i2c_driver_install(0, conf.mode, 0, 0, 0);
}
7.3 SPI
4線、全二重(ぜんにじゅう)、高速(こうそく)。
#include "driver/spi_master.h"
void spi_init(void) {
spi_bus_config_t cfg = {
.miso_io_num = 19,
.mosi_io_num = 23,
.sclk_io_num = 18,
.quadwp_io_num = -1,
.quadhd_io_num = -1,
.max_transfer_sz = 4096,
};
spi_bus_initialize(HSPI_HOST, &cfg, SPI_DMA_CH_AUTO);
}
7.4 UART
#include "driver/uart.h"
void uart_init(void) {
uart_config_t c = {
.baud_rate = 115200,
.data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_DISABLE,
.stop_bits = UART_STOP_BITS_1,
};
uart_param_config(UART_NUM_1, &c);
uart_driver_install(UART_NUM_1, 1024, 0, 0, NULL, 0);
}
7.5 ADC
#include "esp_adc/adc_oneshot.h"
adc_oneshot_unit_handle_t h;
void adc_init(void) {
adc_oneshot_unit_init_cfg_t init = { .unit_id = ADC_UNIT_1 };
adc_oneshot_new_unit(&init, &h);
adc_oneshot_chan_cfg_t cfg = {
.bitwidth = ADC_BITWIDTH_12,
.atten = ADC_ATTEN_DB_12,
};
adc_oneshot_config_channel(h, ADC_CHANNEL_0, &cfg);
}
7.6 PWM
#include "driver/ledc.h"
void pwm_init(void) {
ledc_timer_config_t t = {
.duty_resolution = LEDC_TIMER_13_BIT,
.freq_hz = 5000,
};
ledc_timer_config(&t);
}
8. センサー統合(とうごう)
8.1 温湿度(おんしつど)(DHT22、BME280)
#include <DHT.h>
#define DHTPIN 4
DHT dht(DHTPIN, DHT22);
void setup() {
Serial.begin(115200);
dht.begin();
}
void loop() {
float h = dht.readHumidity();
float t = dht.readTemperature();
Serial.printf("T:%.1f H:%.1f\n", t, h);
delay(2000);
}
8.2 加速度(かそくど)(MPU6050)
#include <MPU6050.h>
MPU6050 mpu;
void setup() {
Wire.begin();
mpu.initialize();
}
void loop() {
int16_t ax, ay, az, gx, gy, gz;
mpu.getMotion6(&ax, &ay, &az, &gx, &gy, &gz);
delay(100);
}
8.3 GPS(NEO-6M)
#include <TinyGPS++.h>
TinyGPSPlus gps;
void loop() {
while (Serial2.available()) {
if (gps.encode(Serial2.read()) && gps.location.isValid()) {
Serial.printf("%.6f %.6f\n", gps.location.lat(), gps.location.lng());
}
}
}
9. IoT通信(つうしん)プロトコル比較(ひかく)
| プロトコル | 範囲(はんい) | 帯域 | 電力 | 用途(ようと) |
|---|---|---|---|---|
| Wi-Fi | 50-100m | 高 | 高 | ホーム |
| BLE | 10-50m | 中 | 低 | ウェアラブル |
| Zigbee | 10-100m | 低 | 低 | スマートホーム |
| Thread | 10-100m | 低 | 低 | Matter |
| LoRaWAN | 2-15km | 極低 | 極低 | スマートシティ |
| NB-IoT | セル | 低 | 低 | 遠隔資産(えんかくしさん) |
9.1 Wi-Fi接続(せつぞく)
#include "esp_wifi.h"
void wifi_init_sta(void) {
esp_netif_init();
esp_event_loop_create_default();
esp_netif_create_default_wifi_sta();
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
esp_wifi_init(&cfg);
wifi_config_t wc = {
.sta = { .ssid = "MyWiFi", .password = "MyPass" },
};
esp_wifi_set_mode(WIFI_MODE_STA);
esp_wifi_set_config(WIFI_IF_STA, &wc);
esp_wifi_start();
}
10. MQTT 深(ふか)掘(ぼ)り
10.1 核心概念(かくしんがいねん)
- ブローカー:Mosquitto、EMQX、HiveMQ
- クライアント:Publisher/Subscriber
- トピック:階層的(かいそうてき)文字列(もじれつ)
- QoS:0/1/2
- Retained Message
- LWT(遺言(ゆいごん)メッセージ)
10.2 ESP32 MQTTクライアント
#include "mqtt_client.h"
esp_mqtt_client_handle_t client;
static void mqtt_event_handler(void *args, esp_event_base_t base,
int32_t id, void *data) {
esp_mqtt_event_handle_t e = data;
switch ((esp_mqtt_event_id_t)id) {
case MQTT_EVENT_CONNECTED:
esp_mqtt_client_subscribe(client, "device/cmd", 1);
break;
case MQTT_EVENT_DATA:
printf("%.*s\n", e->data_len, e->data);
break;
default: break;
}
}
void mqtt_start(void) {
esp_mqtt_client_config_t cfg = {
.broker.address.uri = "mqtts://mqtt.example.com:8883",
};
client = esp_mqtt_client_init(&cfg);
esp_mqtt_client_register_event(client, ESP_EVENT_ANY_ID, mqtt_event_handler, NULL);
esp_mqtt_client_start(client);
}
10.3 トピック設計(せっけい)
- 階層的に直感的(ちょっかんてき):tenant/site/device/metric
- ワイルドカード:
+単一(たんいつ)、#複数(ふくすう) - 読(よ)み書(か)きパス分離(ぶんり)
11. CoAP、LoRaWAN、BLE
11.1 CoAP
RFC 7252、REST、UDP、軽量(けいりょう)。
11.2 LoRaWAN
#include "LoRaWan_APP.h"
uint8_t devEui[] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77 };
void setup() {
Mcu.begin();
LoRaWAN.init(loraWanClass, loraWanRegion);
}
void loop() {
if (deviceState == DEVICE_STATE_SEND) {
prepareTxFrame(appPort);
LoRaWAN.send();
}
LoRaWAN.cycle(txDutyCycleTime);
}
11.3 BLE
#include <BLEDevice.h>
#include <BLEServer.h>
BLECharacteristic *pChar;
void setup() {
BLEDevice::init("ESP32_Sensor");
BLEServer *s = BLEDevice::createServer();
BLEService *svc = s->createService("4fafc201-1fb5-459e-8fcc-c5c9c331914b");
pChar = svc->createCharacteristic(
"beb5483e-36e1-4688-b7f5-ea07361b26a8",
BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY
);
svc->start();
s->getAdvertising()->start();
}
12. OTA ファームウェア更新(こうしん)
12.1 基本(きほん)
- A/Bパーティション
- ロールバック
- HTTPS+署名(しょめい)検証(けんしょう)
- 電源(でんげん)喪失(そうしつ)耐性(たいせい)
12.2 ESP-IDF OTA
#include "esp_https_ota.h"
void ota_task(void *pv) {
esp_http_client_config_t cfg = {
.url = "https://ota.example.com/firmware.bin",
.cert_pem = (char *)server_cert_pem_start,
};
esp_https_ota_config_t ota = { .http_config = &cfg };
if (esp_https_ota(&ota) == ESP_OK) {
esp_restart();
}
vTaskDelete(NULL);
}
12.3 Secure Boot + Flash暗号化(あんごうか)
- Secure Boot:RSA/ECDSA署名
- Flash Encryption:AES-256
- eFuse:ワンタイムプログラマブル
13. 電力(でんりょく)管理(かんり)
13.1 ESP32スリープモード
| モード | 電流(でんりゅう) | RAM | 復帰(ふっき) |
|---|---|---|---|
| Active | 100-240mA | - | - |
| Modem-Sleep | 3-20mA | 保持(ほじ) | μs |
| Light-Sleep | 0.8mA | 保持 | ms |
| Deep-Sleep | 10μA | RTCのみ | 数百ms |
| Hibernation | 5μA | なし | 起動(きどう) |
13.2 Deep Sleep
#include "esp_sleep.h"
RTC_DATA_ATTR int boot_count = 0;
void app_main(void) {
boot_count++;
esp_sleep_enable_timer_wakeup(30 * 60 * 1000000ULL);
esp_deep_sleep_start();
}
13.3 バッテリー寿命(じゅみょう)計算(けいさん)
平均電流(へいきんでんりゅう)=(アクティブ電流×アクティブ時間 + スリープ電流×スリープ時間)/全時間(ぜんじかん)
14. Edge AI on MCU
14.1 TensorFlow Lite Micro
#include "tensorflow/lite/micro/micro_interpreter.h"
constexpr int kTensorArenaSize = 100 * 1024;
uint8_t tensor_arena[kTensorArenaSize];
void setup() {
const tflite::Model* model = tflite::GetModel(g_model);
static tflite::MicroMutableOpResolver<5> resolver;
static tflite::MicroInterpreter interpreter(
model, resolver, tensor_arena, kTensorArenaSize);
interpreter.AllocateTensors();
interpreter.Invoke();
}
14.2 Edge Impulse
ノーコード機械学習(きかいがくしゅう)パイプライン。
15. セキュリティ
15.1 チェックリスト
- Secure Boot有効化(ゆうこうか)
- Flash Encryption
- TLS 1.2+
- 証明書(しょうめいしょ)検証
- ハードウェア暗号(あんごう)加速(かそく)
- デバッグポート無効化(むこうか)
- ファームウェア署名
- TRNG
- 鍵(かぎ)ローテーション
- 最小権限(さいしょうけんげん)
15.2 Mutual TLS
esp_mqtt_client_config_t cfg = {
.broker.address.uri = "mqtts://broker.example.com:8883",
.broker.verification.certificate = (const char*)server_ca_pem,
.credentials.authentication.certificate = (const char*)client_cert_pem,
.credentials.authentication.key = (const char*)client_key_pem,
};
16. 本番(ほんばん)考慮(こうりょ)事項(じこう)
16.1 ログと監視(かんし)
- ローカルログレベル
- 異常(いじょう)時(じ)のみ遠隔(えんかく)送信(そうしん)
- テレメトリ:CPU/メモリ/電池(でんち)
- クラッシュダンプ
16.2 デバイスライフサイクル
- プロビジョニング
- オンボーディング
- 運用(うんよう)
- 廃棄(はいき)
16.3 製造(せいぞう)
- パーティションテーブル
- MACベース固有(こゆう)ID
- 自動(じどう)テスト治具(じぐ)
- 認証(にんしょう):FCC、CE、IC、KC
17. 実習(じっしゅう)プロジェクト案
- 環境(かんきょう)モニター:ESP32+BME280+MQTT
- スマートドアロック:ESP32+RFID+BLE
- 農場(のうじょう)センサーネットワーク:LoRaWAN
- ウェアラブル心拍計(しんぱくけい):nRF52
- 音声(おんせい)制御(せいぎょ)ハブ:ESP32-S3+TFLite Micro
- 工場(こうじょう)資産(しさん)追跡(ついせき):LoRaWAN+GPS
18. クイズ
Q1. FreeRTOSにおける優先度反転(ゆうせんどはんてん)問題(もんだい)の解決策(かいけつさく)二(ふた)つは?
A:Priority Inheritance(優先度継承(けいしょう))とPriority Ceiling(優先度上限(じょうげん))です。前者(ぜんしゃ)はミューテックスを保持(ほじ)している低優先度タスクの優先度を一時的(いちじてき)に上(あ)げ、後者(こうしゃ)は最大(さいだい)優先度を事前(じぜん)に指定(してい)します。
Q2. MQTT QoSレベル0/1/2の違い?
A:QoS 0は「最大(さいだい)1回(かい)」(損失(そんしつ)可能(かのう))、QoS 1は「最低(さいてい)1回」(重複(じゅうふく)可能)、QoS 2は「正確(せいかく)に1回」。センサーテレメトリは通常(つうじょう)0か1、重要(じゅうよう)なコマンドは2を使(つか)います。
Q3. OTA更新でA/Bパーティションが必要(ひつよう)な理由(りゆう)?
A:新(あたら)しいファームウェアを別(べつ)のパーティションに書(か)き込(こ)みながら現在(げんざい)のファームウェアが動作を維持(いじ)し、失敗(しっぱい)時(じ)に自動(じどう)ロールバックが可能です。ダウンロード中(ちゅう)の電源(でんげん)喪失(そうしつ)でもブリックしません。
Q4. I2CとSPIの主(おも)な違い?
A:I2Cは2線(SDA/SCL)でアドレスで機器(きき)を識別(しきべつ)、100kHz-3.4MHz。SPIは4線(MOSI/MISO/SCK/CS)でCS線で機器選択、全二重、数十MHz。単純(たんじゅん)な複数センサーはI2C、高速ディスプレイ/FlashはSPIが適切(てきせつ)です。
Q5. ESP32 Deep Sleepでデータを保持(ほじ)する方法(ほうほう)?
A:RTC_DATA_ATTRマクロで変数(へんすう)をRTCメモリに配置(はいち)するとDeep Sleep中(ちゅう)も保持されます(8KB制限(せいげん))。例:RTC_DATA_ATTR int boot_count = 0;。より大(おお)きなデータはNVSまたはフラッシュに保存(ほぞん)します。
19. 参考(さんこう)資料(しりょう)
- FreeRTOS公式(こうしき)ドキュメント
- Zephyr Project Documentation
- ESP-IDF Programming Guide
- STM32 HAL Driver
- MQTT 5.0 Specification
- CoAP RFC 7252
- LoRa Alliance Technical Docs
- TensorFlow Lite Micro
- Edge Impulse Docs
- Arm Mbed OS Documentation
- Making Embedded Systems - Elecia White
- Mastering STM32 - Carmine Noviello
- The Hardware Hacker - Andrew Huang
結論(けつろん)
組込み/IoT開発はハードウェア、リアルタイム、制約環境、ネットワーキング、セキュリティが交差(こうさ)する魅力的(みりょくてき)な分野(ぶんや)です。ArduinoからESP32/STM32/Zephyrへ進化(しんか)し、本番(ほんばん)水準(すいじゅん)のOTA、セキュリティ、電力管理を備(そな)えたシステムを作(つく)れます。2025年、Matter、Thread、Edge AI標準(ひょうじゅん)が成熟(せいじゅく)し、組込み開発者(かいはつしゃ)の機会(きかい)はさらに拡大(かくだい)しています。