開発環境の準備
ESP8266による開発を進めるため、以下のツール群を用意する。
- 統合開発環境:AiThinkerIDE
- ファームウェア書き込みツール:flash_download_tools
- シリアル通信ツール:AiThinker Serial Tool
- USBドライバ:CH341SER
プロジェクトテンプレートの作成
Espressifが提供するESP8266 NonOS SDKをベースに、不要なファイルを削除し自分専用のテンプレートを作成する。SDK(Software Development Kit)はソフトウェア開発用のパッケージであり、API(Application Programming Interface)はSDKが提供する機能を呼び出すためのインターフェースである。
SDKディレクトリの整理
SDKの圧縮ファイルを展開後、以下の手順でファイルを裁査する。
driver_libフォルダ内のmakefile(拡張子なし)を削除する。third_partyフォルダを丸ごと削除する。examplesフォルダ内にあるwpsプロジェクトを一階層上に移動し、フォルダ名をappに変更する。その後、空になったexamplesフォルダを削除する。
IDEへのインポートとビルド
AiThinkerIDEを起動し、プロジェクトエクスプローラーから「Import」を選択。既存プロジェクトとして先ほど整理したフォルダを選択し、ツールチェーンに「Cygwin GCC」を指定してインポートを完了する。
インポート後、app/user/user_main.cを開き、不要なサンプルコード(特定の行範囲)を削除する。ファイルを保存したら、プロジェクトを右クリックして「Clean Project」を実行し、続けて「Build Project」を行う。コンパイルが成功すると、コンソールに以下の2つのバイナリファイルと書き込みアドレスが表示される。
eagle.flash.bin→0x00000eagle.irom0text.bin→0x10000
ファームウェアの書き込みと動作確認
フラッシュツールの設定
ESP8266は外部フラッシュメモリ(W25Qシリーズ等)を使用するため、メモリ容量に応じた設定が必要である。一般的な開発ボード(32Mbit / 4MByte)の場合、以下の4つのバイナリファイルを対応するアドレスに書き込む。
eagle.flash.bin:必須ファームウェアeagle.irom0text.bin:ユーザープログラムesp_init_data_default.bin:デフォルトRFパラメータblank.bin:システムパラメータ(2箇所に書き込み)
シリアル通信による確認
シリアルモニタを開き、ポートとボーレートを設定して開発ボードのリセットボタンを押下すると、プログラム内の出力が確認できる。
ESP8266の機能概要
ESP8266はWi-Fi機能を内蔵したマイクロコントローラ(MCU)である。従来のマイコンがレジスタを直接操作するのに対し、ESP8266はSDKが提供するAPI関数を呼び出すことで周辺機能(IO、タイマー、PWM、UART、I2C等)を簡便に利用できる。
GPIOの制御とピンマルチプレクス
GPIO(General Purpose Input/Output)は汎用的な入出力インターフェースである。ESP8266には17のピンが存在するが、SDIO通信に6本(IO6〜11)、UART0通信に2本(IO1, IO3)が固定で使用されるため、ユーザーが自由に利用できるピンは8本に限定される。また、GPIO16はディープスリープからの復帰に使われる特殊なピンである。
LEDを点灯させるサンプルコード:
void user_init(void) {
// GPIOピンを输出機能に設定
PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO5_U, FUNC_GPIO5);
// LEDを点灯(Highレベルを出力)
GPIO_OUTPUT_SET(GPIO_ID_PIN(5), 1);
}
コールバック関数
イベント駆動型プログラミングにおいて、特定のイベント発生時に呼び出される関数をコールバック関数と呼ぶ。割り込みハンドラもコールバック関数の一種である。例えば、システム初期化完了時に処理を実行したい場合、system_init_done_cb()にコールバック関数を登録する。
タイマーの利用
ソフトウェアタイマー
ソフトウェアタイマーは、一定周期で反復処理を行う際に使用する。ポーリングのためのwhileループはCPUを占有するため避けるべきである。主なAPIはos_timer_disarm(無効化)、os_timer_setfn(コールバック登録)、os_timer_arm(開始)である。
#include "osapi.h"
#include "user_interface.h"
os_timer_t interval_timer;
void timer_callback(void) {
os_printf("Timer expired!\n");
}
void ICACHE_FLASH_ATTR user_init(void) {
os_timer_disarm(&interval_timer);
os_timer_setfn(&interval_timer, (os_timer_func_t *)timer_callback, NULL);
os_timer_arm(&interval_timer, 3000, 1); // 3秒ごとに繰り返し
}
ハードウェアタイマー
ハードウェアタイマーは物理的なタイマーモジュールを使用し、マイクロ秒単位の高精度な処理に向いている。NMI(ノンマスカブル割り込み)またはFRC1を選択できる。基本的な手順は、hw_timer_initで初期化し、hw_timer_set_funcでコールバックを設定し、hw_timer_armで起動する。精度を要さない限り、ソフトウェアタイマーの使用が推奨される。
void hw_timeout_cb(void) {
os_printf("HW Timer tick\n");
}
void ICACHE_FLASH_ATTR user_init(void) {
hw_timer_init(0, 1); // FRC1ソース、リピート有効
hw_timer_set_func(hw_timeout_cb);
hw_timer_arm(1000000); // 1秒(マイクロ秒単位)
}
PWMによる呼吸灯制御
PWM(Pulse Width Modulation)は周期(Period)とデューティ比(Duty)で制御される。LEDの明暗やモータの回転速度を調整するために使用する。SDKではpwm_init、pwm_set_duty、pwm_startが主要なAPIである。
#include "osapi.h"
#include "user_interface.h"
os_timer_t breath_timer;
uint32_t current_duty = 0;
int16_t duty_step = 400;
uint32_t pwm_pin_info[1][3] = {
{PERIPHS_IO_MUX_GPIO2_U, FUNC_GPIO2, 2}
};
void breath_callback(void) {
current_duty += duty_step;
if (current_duty >= 20000) {
duty_step = -400;
} else if (current_duty == 0) {
duty_step = 400;
}
pwm_set_duty(current_duty, 0);
pwm_start();
}
void ICACHE_FLASH_ATTR user_init(void) {
uint32_t init_duty[1] = {0};
pwm_init(1000, init_duty, 1, pwm_pin_info);
os_timer_disarm(&breath_timer);
os_timer_setfn(&breath_timer, (os_timer_func_t *)breath_callback, NULL);
os_timer_arm(&breath_timer, 20, 1);
}
UART通信
UART0はデフォルトで有効であり、40MHzクリスタル時のボーレートは115200、26MHz時は74880となる。uart_init関数を使用してUART0およびUART1のボーレートを個別に設定できる。通信を行うには、專用のドライバファイル(uart.c, uart.h, uart_register.h)をプロジェクトに追加する必要がある。
I2C通信と温湿度センサ(AHT10)
I2CはSDAとSCLの2本の線で構成される同期式シリアルバスである。スタートコンディション(SCLがHighの間にSDAがHigh→Low)、ストップコンディション(SCLがHighの間にSDAがLow→High)、ACK/NACK等のプロトコルによって通信が制御される。スレーブデバイスは7ビットのアドレスで識別される。
AHT10温湿度センサとの通信では、測定トリガ(0xAC)、ソフトリセット(0xBA)などのコマンドを使用する。データ取得は、測定トリガ発行→ステータス確認(測定完了待ち)→データ読み出し、という手順で行う。
#include "osapi.h"
#include "user_interface.h"
#include "driver/i2c_master.h"
#include "driver/AHT10.h"
os_timer_t sensor_timer;
void sensor_read_callback(void) {
AHT10_Read_Data();
}
void ICACHE_FLASH_ATTR user_init(void) {
i2c_master_gpio_init();
AHT10_Init();
os_timer_disarm(&sensor_timer);
os_timer_setfn(&sensor_timer, (os_timer_func_t *)sensor_read_callback, NULL);
os_timer_arm(&sensor_timer, 2000, 1);
}