フライングタイガーで信号機を買った

概要

フライングタイガーで小さい信号機を買ったので、購入してみました。分解しようとしましたが断念、、、

商品

なんと400円もします!

セリアのと比べると高く感じてしまいます、、、

並べてみると結構大きさが違いますね。あとフライングタイガーのは上から赤黄緑の順ですが、セリアのは緑黄赤です。

AliExpressで確認したところ、セリアの信号機は赤からのと、緑からの2種類ありました。一般的には赤からなので、修正したのかな?

セリアのを自作で制御する場合には、中身分解して色のカバーを取り替えるだけで配列は変更できるはずです。

信号機制御

セリアは2ループすると自動的に電源OFFになりましたが、フライングタイガーのものは電源ONにすると、信号機として動き続ける仕様でした。

緑、緑点灯、黄色、黄色点灯、赤、黄色、緑の順だったかな?

構造

電池はLR41が2個で動いています。

裏側はこんな感じで、上に電源ボタンが1つだけあります。

そして、本体のはめ込みは爪で行っており、足の部分までがっちり爪で固定されています。分解すると爪が壊れるような気がしたので、これ以上分解していません。

オフィシャル情報

まとめ

自動電源OFFがないので、常に動かす用途には適しています。しかしながら改造する用途にはちょっと分解が大変そうなのと、高いので向いていなそうです。

AliExpressには同じ形の信号機はなかったので、オリジナル商品なのかな?

ESP32の高精度タイマー割り込みを調べる

概要

Arduino環境ではTickerクラスを利用したタイマーと、より高精度の割り込みを利用したタイマー処理があり、割り込みを利用したタイマー処理を調べてみました。

タイマーの概要

ESP32には4つのタイマーがあり、自由に利用することができます。タイマーの原理としては、指定したクロック数が経過したらカウントして、特定カウントになったらタイマー割り込みをかけて、割り込み関数を呼び出すような動きになっています。

そのため、クロック数とカウント数の掛け算でタイマーの間隔を制御しています。あとの例では80クロック×1000000カウント=80Mクロックでタイマー割り込みが発生しています。タイマーは通常80MHzで動いていますので、1秒間隔での実行になります。

タイマーの動作周波数はCPU周波数に応じて変化します。CPU周波数と同一ではなく、ペリフェラル周波数と呼ばれる周辺機器の周波数です。

CPU周波数ペリフェラル周波数
240MHz
120MHz
80MHz
80MHz
40MHz40MHz
20MHz20MHz
10MHz10MHz

上記の対応になっていて、ペリフェラル周波数の上限は80MHzで、それ以下の場合にはCPU周波数と同一です。

つまり、1カウントの時間をわかりやすい1マイクロ秒に設定したい場合には、ペリフェラル周波数に合わせて変える必要があります。変更していない場合、80Mクロックで設定していたタイマーを、ペリフェラル周波数40MHzで動かすと2秒になります。

最小構成スケッチ

#define LED_PIN    10

hw_timer_t * timer = NULL;

void IRAM_ATTR onTimer() {
  digitalWrite(LED_PIN, !digitalRead(LED_PIN));
}

void setup() {
  Serial.begin(115200);
  pinMode(LED_PIN, OUTPUT);

  timer = timerBegin(0, 80, true);
  timerAttachInterrupt(timer, &onTimer, true);
  timerAlarmWrite(timer, 1000000, true);
  timerAlarmEnable(timer);
}

void loop() {
}

最小構成のLチカです。このよう処理は実際にはTickerクラスを利用したほうが良いです。

タイマー作成

  timer = timerBegin(0, 80, true);

timerBegin()でタイマーを作成しています。一番最初の引数はタイマーのIDです。ESP32はタイマーが4つあり、0-3までのタイマーを利用できます。

2つ目が何クロックでカウントをするかの数値になります。80の場合、80クロックで1カウントします。getApbFrequency()/1000000で、どのCPU周波数でも1マイクロ秒に固定化することができます。

3つ目がカウンターをカウントアップする場合にはtrue。カウントダウンする場合にはfalseになります。通常はtrueのみしか使わないと思います。

タイマー割り込み関数登録

  timerAttachInterrupt(timer, &onTimer, true);

最初の引数は設定するタイマー。2つ目は割り込み時に呼ばれる割り込み関数。3つ目が割り込み検知方法です。

void IRAM_ATTR onTimer() {

割り込み関数は上記のようにIRAM_ATTRが追加されています。

このオプションは関数を高速なIRAM上に読み込みことを保証させるもののようです。おのオプションが無いと、フラッシュ上に配置される可能性があり、その場合には低速動作になり、結果的にクラッシュする可能性があるみたいです。

単純なプログラムの場合にはおそらく指定しなくても動きますが、基本的には割り込み関数は高速である必要があり、IRAM_ATTRをつけてください。

3つ目の割り込み検知方法は、trueの場合にはエッジトリガー、falseの場合にはレベルトリガーになります。タイマーの場合にはカウントアップしていって、指定カウントに変わったところを検知するので、エッジトリガーのtrueを指定します。

タイマー割り込み判定方法設定

  timerAlarmWrite(timer, 1000000, true);

割り込みが発生したときの、トリガー条件を設定します。最初の引数は設定するタイマー。2つ目はカウント数。3つ目がautoreloadで、trueの場合には定期実行、falseの場合には1ショットの実行になります。

タイマー有効化

  timerAlarmEnable(timer);

上記でタイマーを有効化しています。timerAlarmDisable()という関数で停止させることもできます。

公式スケッチ例

ESP32\Timer\RepeatTimer

/*
 Repeat timer example

 This example shows how to use hardware timer in ESP32. The timer calls onTimer
 function every second. The timer can be stopped with button attached to PIN 0
 (IO0).

 This example code is in the public domain.
 */

// Stop button is attached to PIN 0 (IO0)
#define BTN_STOP_ALARM    0

hw_timer_t * timer = NULL;
volatile SemaphoreHandle_t timerSemaphore;
portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED;

volatile uint32_t isrCounter = 0;
volatile uint32_t lastIsrAt = 0;

void IRAM_ATTR onTimer(){
  // Increment the counter and set the time of ISR
  portENTER_CRITICAL_ISR(&timerMux);
  isrCounter++;
  lastIsrAt = millis();
  portEXIT_CRITICAL_ISR(&timerMux);
  // Give a semaphore that we can check in the loop
  xSemaphoreGiveFromISR(timerSemaphore, NULL);
  // It is safe to use digitalRead/Write here if you want to toggle an output
}

void setup() {
  Serial.begin(115200);

  // Set BTN_STOP_ALARM to input mode
  pinMode(BTN_STOP_ALARM, INPUT);

  // Create semaphore to inform us when the timer has fired
  timerSemaphore = xSemaphoreCreateBinary();

  // Use 1st timer of 4 (counted from zero).
  // Set 80 divider for prescaler (see ESP32 Technical Reference Manual for more
  // info).
  timer = timerBegin(0, 80, true);

  // Attach onTimer function to our timer.
  timerAttachInterrupt(timer, &onTimer, true);

  // Set alarm to call onTimer function every second (value in microseconds).
  // Repeat the alarm (third parameter)
  timerAlarmWrite(timer, 1000000, true);

  // Start an alarm
  timerAlarmEnable(timer);
}

void loop() {
  // If Timer has fired
  if (xSemaphoreTake(timerSemaphore, 0) == pdTRUE){
    uint32_t isrCount = 0, isrTime = 0;
    // Read the interrupt count and time
    portENTER_CRITICAL(&timerMux);
    isrCount = isrCounter;
    isrTime = lastIsrAt;
    portEXIT_CRITICAL(&timerMux);
    // Print it
    Serial.print("onTimer no. ");
    Serial.print(isrCount);
    Serial.print(" at ");
    Serial.print(isrTime);
    Serial.println(" ms");
  }
  // If button is pressed
  if (digitalRead(BTN_STOP_ALARM) == LOW) {
    // If timer is still running
    if (timer) {
      // Stop and free timer
      timerEnd(timer);
      timer = NULL;
    }
  }
}

実際のタイマー利用例です。マルチタスクや割り込みを使う場合には、通常のコールバックと違い意図しないタイミングで変数を更新される可能性などがあり、排他処理を行う必要があります。

ざっくり説明すると、他の人が使っていないのを確認してから、利用を宣言して、使い終わっていたら、開放する処理です。

上記に詳しく書いてありますが、スケッチ例ではバイナリセマフォを利用しています。記事には書いていませんが、キューに似ているが、1つしか保存しない通知機能もあるのでどこかでマルチタスクでの注意点をまとめたいと思っています。

このスケッチでは、1秒間隔でシリアルに時間を出力し、GPIOがLOWになるとタイマーを停止する動きになります。M5StickCの場合にはGPIO37がHOMEボタンなので、書き換えてから実行してみてください。

この動き自体はGPIOにLOWになったら割り込み関数を実行したほうが適して、あまり良くないサンプルに見えます。

バイナリセマフォ作成

  timerSemaphore = xSemaphoreCreateBinary();

バイナリセマフォとは、一人だけセマフォを取得できる排他制御です。ただし、デフォルトは誰もセマフォを取得できず、誰かが許可した場合にだけ取得可能です。

  timer = timerBegin(0, 80, true);
  timerAttachInterrupt(timer, &onTimer, true);
  timerAlarmWrite(timer, 1000000, true);
  timerAlarmEnable(timer);

1マイクロ秒単位のタイマーを作成して、1000000カウント(1秒)したらonTimer()関数を割り込みで呼び出すタイマーを実行しています。

ここは最小構成スケッチと同じです。

セマフォ取得判定

  if (xSemaphoreTake(timerSemaphore, 0) == pdTRUE){

ここはセマフォの処理になります。0はタイムアウトの時間を表し、この場合にはセマフォを取得できなかった場合には即時終了します。portMAX_DELAYを指定すると、セマフォが取得できるまで、ブロッキングして待ちます。

タイマー値取得

    // Read the interrupt count and time
    portENTER_CRITICAL(&timerMux);
    isrCount = isrCounter;
    isrTime = lastIsrAt;
    portEXIT_CRITICAL(&timerMux);

セマフォが取得できた場合には、タイマー値を取得してからシリアルに出力しています。portENTER_CRITICAL()とportEXIT_CRITICAL()は割り込み禁止で、この関数に囲まれているクリティカルセクションと呼ばれる処理を実行中には、タイマー割り込みなどが発生しません。

取得しようとしている変数が、タイマー割り込みで変更される可能性があるものなので、取得途中で変更されて困るものは、割り込み禁止を設定します。

タイマー停止

  if (digitalRead(BTN_STOP_ALARM) == LOW) {
    // If timer is still running
    if (timer) {
      // Stop and free timer
      timerEnd(timer);
      timer = NULL;
    }
  }

ここは単純にGPIOがLOWの場合にはタイマーを終了しています。

セマフォ宣言

volatile SemaphoreHandle_t timerSemaphore;

volatile宣言がついています。割り込みで利用する変数にはすべてこの宣言を利用する必要があります。

この宣言があると、変数の置き場所がいろいろな場所から変更されても大丈夫な領域に確保されます。

割り込み禁止用Mux宣言

portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED;

割り込み禁止も、ミューテックスと呼ばれる排他制御です。初期値は常にportMUX_INITIALIZER_UNLOCKEDで排他制御されていない状態です。

タイマー用変数宣言

volatile uint32_t isrCounter = 0;
volatile uint32_t lastIsrAt = 0;

isrCounterが割り込み発生回数、lastIsrAtが最後に割り込みが発生したときの経過時間が入っている変数です。

両方とも割り込みから変更される可能性があるのでvolatile宣言が付いています。

割り込み関数

void IRAM_ATTR onTimer(){
  // Increment the counter and set the time of ISR
  portENTER_CRITICAL_ISR(&timerMux);
  isrCounter++;
  lastIsrAt = millis();
  portEXIT_CRITICAL_ISR(&timerMux);
  // Give a semaphore that we can check in the loop
  xSemaphoreGiveFromISR(timerSemaphore, NULL);
  // It is safe to use digitalRead/Write here if you want to toggle an output
}

割り込み関数では、通常と使える関数群が違いますので注意しましょう。_ISRが最後につく関数群がある場合には、そちらを利用しないと正しく動きません。ですが、単純な処理の場合には_ISRの割り込み中用の関数群ではなくても動いてしまうので、注意してください。複雑な割り込みが発生した場合など、意図しないタイミングでハングアップする可能性があります。

portENTER_CRITICAL_ISR()とportEXIT_CRITICAL_ISR()で囲んだ間で、タイマー変数の更新を行っています。この処理であれば、おそらく割り込み禁止をしなくても大丈夫そうですが、割込み禁止にしているようです。

xSemaphoreGiveFromISR(timerSemaphore, NULL)で、タイマー用のバイナリセマフォに許可を与えています。NULLに指定しているところには、本来以下のように値をいれた変数にする必要があります。

static signed portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;
xSemaphoreGiveFromISR( xSemaphore, &xHigherPriorityTaskWoken );

この関数を実行後にxHigherPriorityTaskWokenの中身を確認し、pdTRUEに変更された場合には、セマフォ取得待ちでブロックだった優先度の高いタスクが実行されたのがわかります。

ブロックしていると、かなりテクニカルな処理が必要になるので、なるべくノンブロックで動かすことをおすすめします。

まとめ

割り込みは非常に難しいです。今回の例ではありませんでしたが基本的にはマルチタスクを意識したコーディングが必要になります。

使う関数群なども違いますので注意が必要です。このへんの処理はArduinoやESP32ではなく、内部で利用しているFreeRTOSの知識が必要になります。ただし日本語の情報も少なく、体系的に勉強するのは難易度が高そうです。

ESP32のタイマークラス Tickerを調べる

概要

ESP32でタイマー処理を行うクラスのTickerを調べてみました。

タイマー追加の内部処理

Arduino15\packages\esp32\hardware\esp32\1.0.4\libraries\Ticker\src\Ticker.cpp

void Ticker::_attach_ms(uint32_t milliseconds, bool repeat, callback_with_arg_t callback, uint32_t arg) {
  esp_timer_create_args_t _timerConfig;
  _timerConfig.arg = reinterpret_cast<void*>(arg);
  _timerConfig.callback = callback;
  _timerConfig.dispatch_method = ESP_TIMER_TASK;
  _timerConfig.name = "Ticker";
  if (_timer) {
    esp_timer_stop(_timer);
    esp_timer_delete(_timer);
  }
  esp_timer_create(&_timerConfig, &_timer);
  if (repeat) {
    esp_timer_start_periodic(_timer, milliseconds * 1000ULL);
  } else {
    esp_timer_start_once(_timer, milliseconds * 1000ULL);
  }
}

タイマー追加はすべて、上記関数へのラッパーでした。

構造体に設定をつめて、esp_timer_create()関数を呼び出してから、単発だったらesp_timer_start_once()関数、リピートの場合にはesp_timer_start_periodic()関数を呼び出しています。

esp_timer_系関数はマイクロ秒単位で引数を取りますが、Tickerクラスはミリ秒単位で管理しているようです。

タイマー停止の内部処理

Arduino15\packages\esp32\hardware\esp32\1.0.4\libraries\Ticker\src\Ticker.cpp

void Ticker::detach() {
  if (_timer) {
    esp_timer_stop(_timer);
    esp_timer_delete(_timer);
    _timer = nullptr;
  }
}

停止時にはesp_timer_stop()してからesp_timer_delete()しているだけでした。

ラッパークラス 単発系(once)

単発実行は以下の4関数あります。

  • void once(float seconds, callback_t callback)
  • void once(float seconds, void (*callback)(TArg), TArg arg)
  • void once_ms(uint32_t milliseconds, callback_t callback)
  • void once_ms(uint32_t milliseconds, void (*callback)(TArg), TArg arg)

_msとついているのは受け取ったミリ秒をそのまま渡して、ついていないものは秒で受け取ったものを1000倍してミリ秒にしてから_attach_ms()を呼び出しています。

基本的には_ms系だけ使うほうがいい気がします。

あとはコールバック関数と、コールバック関数用の変数を渡しています。変数無しの関数は0を渡していました。

  template<typename TArg>
  void once(float seconds, void (*callback)(TArg), TArg arg)
  {
    static_assert(sizeof(TArg) <= sizeof(uint32_t), "attach() callback argument size must be <= 4 bytes");
    uint32_t arg32 = (uint32_t)(arg);
    _attach_ms(seconds * 1000, false, reinterpret_cast<callback_with_arg_t>(callback), arg32);
  }
  template<typename TArg>
  void once_ms(uint32_t milliseconds, void (*callback)(TArg), TArg arg)
  {
    static_assert(sizeof(TArg) <= sizeof(uint32_t), "attach_ms() callback argument size must be <= 4 bytes");
    uint32_t arg32 = (uint32_t)(arg);
    _attach_ms(milliseconds, false, reinterpret_cast<callback_with_arg_t>(callback), arg32);
  }

複数のタイマーを同じコールバック関数で動かしたい場合には、変数で区別するのが便利そうです。用途別に別のコールバック関数を準備するのであれば変数はいらないと思います。

スケッチ例を見てみる

Ticker/Blinker

#include <Arduino.h>
#include <Ticker.h>
// attach a LED to pPIO 21
#define LED_PIN 21
Ticker blinker;
Ticker toggler;
Ticker changer;
float blinkerPace = 0.1;  //seconds
const float togglePeriod = 5; //seconds
void change() {
  blinkerPace = 0.5;
}
void blink() {
  digitalWrite(LED_PIN, !digitalRead(LED_PIN));
}
void toggle() {
  static bool isBlinking = false;
  if (isBlinking) {
    blinker.detach();
    isBlinking = false;
  }
  else {
    blinker.attach(blinkerPace, blink);
    isBlinking = true;
  }
  digitalWrite(LED_PIN, LOW);  //make sure LED on on after toggling (pin LOW = led ON)
}
void setup() {
  pinMode(LED_PIN, OUTPUT);
  toggler.attach(togglePeriod, toggle);
  changer.once(30, change);
}
void loop() {
  
}

サンプルスケッチはLEDがGPIO21に接続されている想定ですので、各自環境に合わせて書き換えてください。私はM5StickCで動かしたので10に変更しました。(M5StickCはLOWにすると光るので点滅が逆転します)

このスケッチには3つのタイマークラスが登場しています。

Ticker toggler;

toggler.attach(togglePeriod, toggle);

上記で設定されています。togglePeriodは5秒なので、5秒間隔でtoggle()関数を呼び出しています。

Ticker changer;

  changer.once(30, change);

上記で設定されています。30秒後に一度だけchange()関数を呼び出しています。

Ticker blinker;

    blinker.attach(blinkerPace, blink);

上記で設定されています。blinkerPaceは当初0.1秒なので、0.1秒間隔でblink()関数を呼び出しています。

    blinker.detach();

また、5秒間隔で呼ばれるtoggle()関数の中でタイマーの無効化も呼ばれています。

スケッチの動き

5秒間隔で動くblinkerタイマーにより、Lチカするblinkerタイマーの起動と停止を交互に実行することで、5秒間隔で当初0.1秒間隔のLチカと、LOWのモードを切り替えています。

changerタイマーが30秒後に1度実行され、その中でLチカする間隔を制御するblinkerPace変数を0.1秒から0.5秒に書き換えています。

そのため30秒後からは0.5秒間隔でLチカするのと、LOWのモードを切り替える動きになります。

ちょっと詰め込みすぎなスケッチ例ですね。

Ticker/Arguments

#include <Arduino.h>
#include <Ticker.h>
// attach a LED to GPIO 21
#define LED_PIN 21
Ticker tickerSetHigh;
Ticker tickerSetLow;
void setPin(int state) {
  digitalWrite(LED_PIN, state);
}
void setup() {
  pinMode(LED_PIN, OUTPUT);
  digitalWrite(1, LOW);
  
  // every 25 ms, call setPin(0) 
  tickerSetLow.attach_ms(25, setPin, 0);
  
  // every 26 ms, call setPin(1)
  tickerSetHigh.attach_ms(26, setPin, 1);
}
void loop() {
}

引数ありバージョンのスケッチ例です。こちらの方がシンプルなスケッチですが、動かしてみないとよくわからないと思います。こちらもLEDの接続しているGPIOに書き換えてください。

digitalWrite(1, LOW);の行は1じゃなくてLED_PINな気もします。

タイマーは2つあり、25ミリ秒間隔でsetPin(0)を呼び出すタイマーと、26ミリ秒間隔でsetPin(1)を呼び出すタイマーです。

この2つの間隔が違うタイマーでLEDをON/OFFしているので、周期がずれた点滅を繰り返すと思います。

備考

このタイマーで利用されているesp_timer_系の関数は、精度が悪いようです。

上記スライドか、以下の書籍にて詳しく紹介されています。

ざっくりと説明すると、Wi-Fi接続などの優先度の高い処理が動くと、タイマー処理が遅延して実行されるとのことです。

ただし、esp_timer_系のタイマーは本来はマイクロ秒単位の精度ですが、Tickerクラスはミリ秒単位で動作しています。上記実験では最大12ミリ秒程度の遅延がありましたので、その程度の遅延が発生しても問題がない用途であれば、Tickerクラスを利用しても問題はないと思います。

まとめ

0.1秒以上のタイマーであれば、十分な精度があるように思います。ミリ秒単位での遅延が問題になる場合には、別のtimer系の関数がありますので後日調べてみたいと思います。

ESP32のWi-fi設定方法調査

概要

ESP32のWi-Fi設定をどのようにすればよいのかを調査しました。

当初はESP-TOUCH(SmartConfig)の調査でしたが、Wi-Fi設定の全般的な動きを調べました。

Wi-Fi接続先指定

[非推奨] スケッチに直書き

const char *wifi_ssid = "SSID";
const char *wifi_key = "Password";
void setup() {
  WiFi.begin(wifi_ssid, wifi_key);
}

SSIDとKEYをスケッチに直接埋め込みます。他の環境で接続する場合には書き換えて転送し直します。

上記みたいなやつです。ブログなどでこのように紹介してあることがありますが、非推奨です。

[非推奨] NVSの独自項目に事前設定

void setup() {
  preferences.begin("Wi-Fi", true);
  preferences.getString("ssid", wifi_ssid, sizeof(wifi_ssid));
  preferences.getString("key", wifi_key, sizeof(wifi_key));
  preferences.end();
  WiFi.begin(wifi_ssid, wifi_key);
}

過去このブログでの推奨方法でしたが、今回調査した結果を踏まえると、次の方法がよいのかなと思いました。

[推奨] 無指定で前回設定を利用する

void setup() {
    WiFi.begin();
}

無指定で接続すると、前回接続したときの設定で接続します。一度他のスケッチなどで接続しておけば、このスケッチでは一切設定はいりません。

Wi-Fi接続設定の保存方法

無指定で接続する場合には、接続先情報をあらかじめ保存しておく必要があります。代表的な方法を紹介します。

スケッチに直書きで設定する

#include "WiFi.h"

void setup() {
  WiFi.begin("SSID", "KEY");
}

void loop() {
}

接続に失敗しても内部に保存されます。一番お手軽です。

softAPモードで設定する

アクセスポイントモードでESP32を起動して、ブラウザ経由で設定します。ブラウザで設定できるので、環境依存が少ないです。

しかしながら、結構ブラウザ画面上から設定値を受け取るのは面倒なので、不特定多数に配布するようなデバイスは仕方ないですが、それ以外だと他の方法の方が実装は楽です。

上記はちょっと違いますが、ブラウザから設定した値を取得する例です。

ESP-TOUCH(SmartConfig)で設定する

ESPの標準的な機能で、スマホからWi-Fi設定をする機能が提供されています。

スケッチ例(GitHub)

#include "WiFi.h"

void setup() {
  // シリアル初期化
  Serial.begin(115200);

  // 前回接続時情報で接続する
  Serial.println("WiFi begin");
  WiFi.begin();
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print(".");
    delay(500);

    // 10秒以上接続できなかったら抜ける
    if ( 10000 < millis() ) {
      break;
    }
  }
  Serial.println("");

  // 未接続の場合にはSmartConfig待受
  if ( WiFi.status() != WL_CONNECTED ) {
    WiFi.mode(WIFI_STA);
    WiFi.beginSmartConfig();

    Serial.println("Waiting for SmartConfig");
    while (!WiFi.smartConfigDone()) {
      delay(500);
      Serial.print("#");
      // 30秒以上接続できなかったら抜ける
      if ( 30000 < millis() ) {
        Serial.println("");
        Serial.println("Reset");
        ESP.restart();
      }
    }

    // Wi-fi接続
    Serial.println("");
    Serial.println("Waiting for WiFi");
    while (WiFi.status() != WL_CONNECTED) {
      delay(500);
      Serial.print(".");
      // 60秒以上接続できなかったら抜ける
      if ( 60000 < millis() ) {
        Serial.println("");
        Serial.println("Reset");
        ESP.restart();
      }
    }
    Serial.println("");
    Serial.println("WiFi Connected.");
  }

  Serial.print("IP Address: ");
  Serial.println(WiFi.localIP());
}

void loop() {
}

最初は無指定で接続して、10秒間Wi-Fiに接続できなかった場合には、WiFi.beginSmartConfig()を呼び出して、ESP-TOUCH(SmartConfig)の設定待ちに入っています。

この状態のときにiOSかAndroidの「Espressif Esptouch」アプリから設定を送信します。このときに位置情報の取得を許可しないとWi-FiのSSIDが取得できません。このアプリ以外でもESP-TOUCHに対応しているアプリはありますので、そちらですとSSIDを手で入力したり、過去に送信した設定を保存しておく機能などがあります。

ただし、このWi-Fi接続先情報は平文で通信しているので、情報セキュリティー的には好ましくありませんので注意してください。

Wi-Fi接続先設定の読み出し方法

SSIDの情報をスケッチに埋め込む場合に、どう暗号化しようか悩むのですが、それ以前の問題として、最後に接続したWi-Fi接続先情報がNVSから読み出すことができます。。。

スケッチ例(GitHub)

#include <Preferences.h>

Preferences preferences;

void setup() {
  Serial.begin(115200);
  delay(2000);
  
  char wifi_ssid[37] = {};
  char wifi_key[66] = {};
  preferences.begin("nvs.net80211", true);
  preferences.getBytes("sta.ssid", wifi_ssid, sizeof(wifi_ssid));
  preferences.getBytes("sta.pswd", wifi_key, sizeof(wifi_key));

  Serial.printf("sta.ssid : %s\n", &wifi_ssid[4]);
  Serial.printf("sta.pswd : %s\n", wifi_key);
}

void loop() {
}

ESP32のフラッシュを抜き出して解析してみたのですが、上記のコードで読み出すことができました。

なので、スケッチにWi-Fiなどの情報を暗号化して保存しても、こちらの情報で中身がバレるので意味がありませんでした。

Wi-Fi接続先設定の削除方法

中古の端末を人に譲る場合には、念の為情報を削除してから譲ることをおすすめします。

esptool.py --port COM3 erase_flash

基本的には上記のようなコマンドで消せます。CLIがよくわからない人に関してはM5Burnerなどのツールを使って消すのがよいと思います。

あとは、作った本人も存在をほぼほぼ忘れていた上記ツールを使うと、Arduino IDEから消すこともできます。

まとめ

無指定最強でした。しかしながら、すぐに読み出せてしまう問題があり、これは回避する方法がないのでESP32を使っている以上諦めるしか無い気がします。

自宅環境などは直指定のスケッチで設定、外の環境に持っていって書き換える場合にはESP-TOUCH(SmartConfig)などで投げるのが良さそうです。

ESP32のデータシートの読み方

概要

ESP32のデータシートを読み直してみました。複数のデータシートがあるので、読む順番などを考えてみました。

データシートの場所

上記にデータシート一覧があります。ずばり読みたいものが決まっている場合には、ここから探すのが早いです。しなしながら、量が多すぎてなにから読めばいいのかわかりにくいですね。

Get Started

最初はこのはじめかたのページから読み始めるのがおすすめです。Development Board Overviewsのところに、開発ボード名が並んでいます。

このページは、ざざっとどんなことが書いてあるのか流し読みで大丈夫です。最初は理解する必要はないと思います。

上記で、シリーズ、SoC、モジュール、ボードという区分があるとわかりましたが、調べる場合にはよりボードに近いほうから読んだほうがわかりやすいです。

現在は、上記4つのボードがありました。私はM5StickCのことを調べたいので、ESP32-PICO-KITを見ます。とはいえ、実際のところ、どこみてもほとんど書いてあることは変わりません、、、

ターゲットボードのGet Started

このページも流し読みで大丈夫です。

Related Documents

一番下に参考文献の一覧があります。

上から目を通していくことをおすすめします。とりあえずは開いてみるだけで、どんなことが書いてあるのかを把握するだけでいいと思います。

ちなみに上から3つはボードの設計に関することなので、あまり関係ありません。

ESP32-PICO-D4 Datasheet

やっと、本命のデータシートです。このデータシートはESP32全体ではなく、PICOに特化した簡易版のデータシートになります。

PICOはモジュールがないので、ちょっと特殊なのですが、ESP32-WROOM-32 Datasheet相当のことが書かれています。

どちらも外見とアンテナの特性が若干異なる程度で、書いてあることはほぼ一緒です。

これ以降はこのデータシートの概要を解説します。

1. Overview

ESP32-PICO-D4の概要が書かれています。ここによるとPICOはSoCじゃなくてSiPらしいです。ESP32のSoCとフラッシュメモリが一緒になっているけれど、アンテナなどはないのでモジュールではないってことだと思います。

ESP32-WROOM-32との差は、ここの文章と技適などの認証周りになります。ESP32-WROOM-32はモジュールなので技適などの認証を取得していますが、PICOは組み込み用のSiPなので商品に合わせてアンテナなどを自由にレイアウト可能ですが、個別に技適を取得する必要があります。

CategoriesItemsSpecifications
CertificationBluetooth certificationBQB
Wi-FiProtocols802.11 b/g/n (802.11n up to 150 Mbps)
A-MPDU and A-MSDU aggregation and 0.4 µs guard interval support
Frequency range2.4 ~ 2.5 GHz
BluetoothProtocolsBluetooth V4.2 BR/EDR and Bluetooth LE specification
RadioNZIF receiver with –97 dBm sensitivity
Class-1, class-2 and class-3 transmitter
AFH
AudioCVSD and SBC
HardwareModule interfacesADC, DAC, touch sensor, SD/SDIO/MMC Host Controller, SPI, SDIO/SPI Slave Controller, EMAC, motor PWM, LED PWM, UART, I2C, I2S, infrared remote controller, GPIO, pulse counter
On-chipsensor Hall sensor
Integrated crystal40 MHz crystal
Integrated SPIflash 4 MB
Operating voltage/Power supply3.0 V ~ 3.6 V
Operating currentAverage: 80 mA
Minimum current delivered by power supply500 mA
Operating temperature range–40 °C ~ 85 °C
Package size(7.000±0.100) mm×(7.000±0.100) mm×(0.940±0.100) mm
Moisture sensitivity level (MSL)Level 3

2. Pin Definitions

ピンレイアウトと、ピンの説明があります。ピンレイアウトはあまり使いませんが、ピンの説明は重要です。とはいえ、ESP32シリーズであればほとんどピンの機能は変わらないです。

上記のようなことを調べるのは、ここのページなどを確認します。あとはStrapping Pinsの項目で特殊なピンについての説明があります。

起動時に特定のピンの状態を設定することで、動きを変えることができます。一番使うのが起動時にGPIO0をGNDに落とすことで、書き込みモードに設定します。

3. Functional Descriptions

機能説明のページです。CPUやメモリなどの概要がかかれています。ここはシリーズごとにことなりますので、ESP32シリーズとESP32-S2シリーズではまったく異なっています。

4. Peripherals and Sensors

周辺機器とセンサーのページです。細かいことはESP32シリーズのデータシートを読めと書いてあります。

5. Electrical Characteristics

電気的特性のページです。ここの項目は結構重要なので、把握しておいたほうがいいです。

最大定格

SymbolParameterMinMaxUnit
VDD33Power supply voltage–0.33.6V
IoutputCumulative IO output current1,100mA
TstoreStorage temperature–40150°C

3.6Vまでしか電源をつなげるなと書いてあります。乾電池2本だと大丈夫ですが、3本つなげるとオーバーしてしまいます。DCDCなどが必要ですね。

IOで出力できる電流は、全部のGPIOで合計して1.1Aみたいです。1つのピンからはそれほど流せないので、ここまで流すことはほとんどないと思います。

推奨動作条件

SymbolParameterMinTypicalMaxUnit
VDD33Power supply voltage33.33.6V
IV DDCurrent delivered by external power supply0.5A
TOperating temperature–4085°C

基本的には最大値は無視して、推奨値で動かす必要があります。電源は3.3Vで0.5A以上の電源を接続する必要がありそうです。USB給電の場合0.5Aは一般的に保証されているので、それぐらいは必要ってことですね。

DC特性 (3.3 V, 25 °C)

SymbolParameterMinTypMaxUnit
CINPin capacitance2pF
VIHHigh-level input voltage0.75×VDD1VDD1+0.3V
VILLow-level input voltage–0.30.25×VDD1V
IIHHigh-level input current50nA
IILLow-level input current50nA
VOHHigh-level output voltage0.8×VDD1V
VOLLow-level output voltage0.1×VDD1V
IOHHigh-level source current VDD3P3_CPU power domain40mA
High-level source current VDD3P3_RTC power domain40mA
High-level source current VDD_SDIO power domain20mA
IOLLow-level sink current28mA
RP UResistance of internal pull-up resistor45
RPDResistance of internal pull-down resistor45
VIL_nRSTLow-level input voltage of CHIP_PU to power off the chip0.6V

25度で3.3V給電した場合のDC特性です。重要なのは以下に列挙します。

VIH(High-level input voltage)

デジタル入力のときにHIGHと判定する電圧。0.75×VDD1からVDD1+0.3の電圧をHIGHと判定します。VDD1は動作電圧なので3.3V。つまり、2.475Vから3.6VまでをHIGHと判定します。

VIL(Low-level input voltage)

デジタル入力のときにLOWと判定する電圧。-0.3から0.25*VDD1の電圧をLOWと判定します。 つまり-0.3Vから0.825‬VまでをLOWと判定します。

VIHとVILの間の0.825Vから2.475Vまでは未定義ですので、判定に利用しないでください。

IOH(High-level source current)

HIGHを出力した場合の電流の上限です。MAXは記述がありませんので、VDD_SDIOは20mA、それ以外は40mAまでを推奨しているようです。

IOL(Low-level sink current)

LOWを出力して、吸い込む電流の上限です。MAXは記述がありませんので、28mAまでを推奨しているようです。

トレラント(Tolerant)は?

ESP32のデータシート上で気をつけないといけないことは、Tolerantの記述がないことです。トレラントとは、何Vまでの入力を入れて大丈夫かを表します。

一般的に3.3Vで動くモジュールなどの場合には、5Vの信号を入力しても大丈夫な場合、5Vトレラントなどと表現されています。

ESP32の場合にはトレラントの表記がありません。そのため5Vのボードなどと直結して壊れないかがわかりません。VIHが3.6Vまでなので、データシート上では5Vを入力すると範囲外になります。

無線系と回路図やパッケージについて

流し読みで問題ありませんので、飛ばします。

9. Learning Resources

ここが重要なページで、次に読むべき資料を紹介してくれています。シリーズのデータシートを最初に読んでしまうと、詳細情報が出てきますが細かいドキュメントの一覧がリンクされていません。

ざっくりと、次に読むべき資料を紹介します。とはいえ、Arduino IDEで普通にプログラムを組みだけであれば、以下の資料は読まなくてもよいと思います、、、

ESP32 Series Datasheet

ESP32シリーズ全体のデータシートです。細かい動作はこちらを読むことで理解できると思います。内容的には今まで読んだデータシートを詳細にしたものになります。

ESP32 Technical Reference Manual

ハードウエアの機能を直接制御しようとしたときに必要になるマニュアルです。DMA転送だとかULPまわりを自分で触りたい場合には、参考になると思います。

Espressif Product Ordering Information

Espressifの製品一覧が書かれています。個人で頼む人は少ないはずですが、直接注文も可能なようです。

変更履歴をみると、どんな商品がいつごろ追加されたのがわかるのでおすすめです。

まとめ

一番読みやすいESP32のモジュール相当のデータシートを読んでみました。ESP32シリーズのデータシートも読み込みたいのですが、流し読みぐらいしかできていません。

Espressif(ESP32他)の製品群を調査する

概要

ESP32のデータシート読み直そうと思ったところで、商品群も把握し直したほうがよいと思い、調査してみました。

主にEspressifの商品群を調べています。

シリーズとしてのESP32

Espressifでは以下の3シリーズを販売しています。

ESP32シリーズ

Espressif社が開発している商品であり、ESP32の中にはCPUやメモリなど最低限必要なものをセットにしたものです。

  • Xtensa® 32ビット LX6 マイクロプロセッサー 最高240MHz
  • 448 KBのROM
  • 520 KBのSRAM
  • 8 KB of SRAM(RTC FAST Memory)
  • 8 KB of SRAM(RTC SLOW Memory)
  • 1 Kbit of eFuse
  • Wi-Fi & Bluetoot

商品によってちょっと違いますが、上記は最低限搭載しています。たんにESP32といった場合にはシリーズ名なのか、特定の商品なのかは判別できません。

シングルコアのSoC以外、基本的にはデュアルコアになります。

ESP32-S2シリーズ

まだ未発売の商品ですが、別シリーズが発表されています。

  • Xtensa® 32ビット LX7 マイクロプロセッサー 最高240MHz
  • 128 KBのROM
  • 320 KBのSRAM
  • 8 KB of SRAM(RTC FAST Memory)
  • 8 KB of SRAM(RTC SLOW Memory)
  • 4 Kbit of eFuse
  • Wi-Fi
  • Full speed USB OTG

コアプロセッサがLX6からLX7に変更されています。これに伴いコプロセッサーも変わり、これまでのULPから、RISC-Vベースのもう少し一般的な命令が使えるようになりました。こちらのシリーズはシングルコアしかありません。

また、Bluetootがなくなり、その代わりにUSBが使えるようになっています。

まだ販売されていないので、今後どうなるかはわかりませんが急激にESP32-S2に変わることはないと思いますが、USBが使いたい場合には選択肢に入りそうです。

ESP8266/ESP8285シリーズ

ESP32の前に販売されていたシリーズです。

  • Xtensa® 32ビット LX106 マイクロプロセッサー 最高160MHz
  • 160 KBのRAM
  • Wi-Fi

Wi-Fiが利用でき、安価なシリーズです。ESP8266とESP8285はフラッシュメモリを内蔵しているかで、基本的な機能は一緒です。

SoCとしてのESP32

SoCとはSystem-on-a-chipの略で、ESP32のようにプロセッサーの他にメモリなどをセットしたものを言います。

ESP32シリーズには複数のSoCがあり、用途に合わせて使い分ける必要があります。基本的な機能は同じことが多いのですが、細かい動作が違うので、データシートを確認する場合には気をつけてください。

ESP32-D*系列

一番標準的なESP32のSoCです。通常使うESP32はこの系列を利用していることが多いです。複数種類があり、チップのサイズが違ったり、フラッシュメモリが内蔵されていたり、製造工程が変わったりしています。

フラッシュメモリは基本的には内蔵されていないものがありますが、一部内蔵しているSoCも存在しています。フラッシュメモリの容量とサイズ以外は基本的に同じ動きをすると思います。

ESP32-S0*,ESP32-U*系列

シングルコアのESP32です。ESP32-S0WDしかなかったのですが、フラッシュメモリが内蔵されているESP32-U4WDHが増えました。ESP32-S0*とESP32-S2は間違いやすいので注意してください。ESP32-Sって商品もありますが、中身はESP32-D*系列です、、、

シングルコア版はあまり採用している例を見たことはありません。

ESP32-PICO-*

ESP32-D*を小型化したSoCです。M5StickCにはESP32-PICO-D4が利用されています。小さい以外にはESP32-D*と特に機能差はありません。

ESP32-PICO-V3が2020年1月に追加されましたが、基本的な機能は同じですが、次で触れるモジュール化に対応したものになります。

PICOは厳密にはSoCではなく、SiP(System in Package)と呼ばれる、複数のチップをパッケージ化したものみたいです。ESP32系のSoCとフラッシュメモリなどを内部で結線して、一つのチップにパッケージ化してあるからだと思いますが、あまり気にしなくても良いとは思います。

ESP32-S2

まだ未発売のSoCです。シングルコアでROMやSRAMもESP32-D*系列より小さいです。ESP32とは別系列の商品群です。

特徴としては、Bluetootがなくなり、かわりにUSB OTGが搭載されており、USBが利用できるようになっています。あとはメインコア以外のコプロセッサーがULPからRISC-Vベースの命令セットに変更になっています。

ESP8266/ESP8285

こちらはESP32ではなく、ESP32の前に発売したSoCで、ESP32とは別系統の商品群です。

シングルコアでよい用途で、Wi-Fiを利用したい場合には非常に安いので最適です。しかしながら、日本だとESP32との価格差があまりないので採用例は少ないです。

ESP8266にフラッシュメモリも内蔵させたのがESP8285になります。

モジュールとしてのESP32

ESP32同等のものが必要なときには、SoCを利用しなくても自分でLX6 マイクロプロセッサーとメモリなどを実装すれば、理論上動きます。しなしながらそこから準備するのは面倒なのでSoCを利用しています。

モジュールとは、SoCの他に必要となる部品を追加し、さらにパッケージ化した製品です。フラッシュメモリが内蔵していないSoCではフラッシュメモリも追加し、アンテナなどの最低限必要な物がパッケージ化しています。

一般的に日本で利用しているのはモジュールになります。これは無線機能を使う場合には、技適を取得する必要があります。(厳密には無線設備の工事設計認証など)

技適を取得する場合には、モジュール単位か、製品単位で取得する必要があります。M5Stack社は製品単位で取得しているので、新しい製品が出るたびに検査をして取得しています。(厳密には同じ形の製品は、無線まわりを共通化して技適を取得しています)

検査自体非常にお金がかかるので、一般人には難しいです。そのため無線部分をモジュール化して、モジュール単位で技適を取得したものがあります。

技適取得済みモジュールを使えば、モジュールを自分の好きにカスタムはできないですが、かんたんに利用することができます。

一般的に技適取得モジュールは、汎用的にする分、少し大きくなるのでM5Stack社は自社で製品に最適なモジュールを作って、製品単位で技適を取得しています。

Espressif ESP32-WROOM-32*

一番標準的なESP32モジュールです。ESP32-D*系列のSoCを利用したモジュールです。日本でESP32を使う場合には基本的にはこのモジュールが多いです。アンテナが外付けか内蔵かで後ろの型番が少し違いますが、それ以外は基本的に同じ機能です。

Espressif ESP32-WROVER*

ESP32-WROOM-32モジュールに8MBの外付けPSRAMを増設したものです。PSRAMはモジュールの外に増設することも可能ですので、このモジュールを採用しなければ行けない理由はありませんが、PSRAMを使う場合にはESP32-WROVERを利用したほうが楽ではあります。

PSRAMを内蔵している分モジュールのサイズは大きくなっています。またこちらもアンテナが外付けか内蔵かで後ろの型番が少し違っていますが、それ以外は基本的に同じ機能です。

Espressif ESP32-SOLO-1

ESP32-S0*系列のSoCを利用したモジュールです。シングルコアですのでESP32-WROOM-32*系のモジュールより安いです。数十円の差なので趣味の電子工作ではほとんど利用されていません。

Espressif ESP32-PICO-V3-ZERO

現在未発売のESP32-PICO-V3のSoCを搭載したモジュールです。小型用とのESP32-PICO-D4のSoCはモジュールが販売されていなかったので、別SoCでモジュールの販売をするようです。詳細はまだ発表されていないのでわかりません。

機能的にはESP32-PICO-D4をモジュール化したものと変わらないようです。

Espressif ESP32-S2-WROOM/ESP32-S2-WROVER

現在未発売のESP32-S2のSoCを搭載したモジュールです。WROVERはPSRAMを搭載しています。

ESP32-S2のSoC自体が大きいので、WROVERでもWROOMでもサイズは変わりません。

Espressif ESP-WROOM-02*/ESP-WROOM-S2

ESP8266EXのSocを搭載したモジュールです。S2の方はESP32に近い使い方ができるモジュールですが、技適を取得していないので日本国内では利用できません。

ESP-WROOM-02は技適を取得しているので、若干採用例がありますが150円程度の金額差ですので趣味の電子工作ではESP32を利用することが多いようです。

Ai-Thinker ESP32-S

去年の年末に技適を取得したモジュールです。アンテナ周り以外はESP32-WROOM-32と互換性があるようです。外部アンテナを利用する場合にはアンテナ端子の横にある0Ωの抵抗を外す必要があるようです。

ESP-SのSはシングルコアのSではないので注意しましょう。

ESP32-WROOM-32より安いため、海外では利用例が多いモジュールですが、国内では出回っていませんでした。ただ技適を取得しましたが、まだ技適の表示されているモジュールが出回っていないため、日本では利用することができません。

この他にオーディオ周りを強化したESP32-A1Sモジュールもありますが、こちらもまだ技適やFCCを取得していないようでした。

Ai-Thinker以外にもモジュールを作成しているメーカーはありますが、技適を取得しているものはなかったようです。

ESP-07S/ESP-12F

ESP8266のSoCを採用したモジュールで、技適を取得しているものが何個かあります。しかしながら一般には販売されていないようでして、手に入れることはできません。これ以外にESP8266のSoCを採用したモジュールは、いろいろな形のものが存在しています。

ESP8266のSoCは基本的に製品単位で技適を取得しているパターンが多いですので、モジュールで使う場合にはESP-WROOM-02*系モジュール一択となります。

ボードとしてのESP32

ESP32のSoCやモジュールを搭載したボードをESP32と呼ぶ場合があります。一般的に趣味で利用する場合にはモジュールではなく、ボードなどの製品を購入することが多いと思います。

一般的には書き込み用のUSBシリアルや書き込み回路、電源周りの回路、外部接続用の端子などが搭載しています。

Espressif ESP32-DevKitC

ESP32-WROOM-32のモジュールを搭載したリファレンス設計のボードです。基本的にはこのボードを参考にして、製品などが設計されています。

Espressif社の純正ボード以外にも、スイッチサイエンスさんのESPr® Developer 32や、DOIT型の開発ボードなど、いろいろなボードが販売されています。

サイズや、外部に出ているピン数やレイアウトなどが違うことがあるので注意してください。

Espressif ESP-WROVER-KIT

ESP32-WROVERのモジュールを搭載したリファレンス設計のボードです。こちらはちょっと高いので、あまり普及していないようです。

Espressif ESP32-PICO-KIT

ESP32-PICO系のSoCを採用したリファレンス設計のボードです。こちらはモジュールではなく、SoCを採用しているので、このボード自体で技適を取得しています。

しかしながら、技適の表示付きボードがまだ出回っていないので、日本で利用することはできません。(厳密には技適の特例制度を利用することで半年まで利用可能です)

Espressif ESP32-S2-Saola

現在未発売です。ESP32-S2のモジュールを利用したリファレンス設計のボードです。

Espressif ESP8266-DevKitC

ESP-WROOM-02のモジュールを利用したリファレンス設計のボードです。あまり販売しているのを見たことがありません。スイッチサイエンスさんのESPr® Developerなどが国内では定番だと思います。

Espressif その他のボード

カメラ付きや、オーディオ用途など実はかなりのリファレンスボードを販売しています。技適付きのモジュールを利用しているので、多くは日本でも利用可能です。

しかしながら非常に高価なことが多いので、あまり普及していません。

M5Stackの製品

M5StackやM5StickCなど、いろいろな開発用のボードを販売しています。基本的にはケースに入って、液晶やバッテリーも内蔵していることが多いので、利用しやすいです。

基本的に有名な製品は技適を取得しているはずです。

LilyGo TTGOの製品

M5Stack社と並んで、いろいろな製品を出しています。ケースなしが多いですがとにかく実験的な製品が多いです。

技適付きモジュールを採用しているボードと、独自モジュールのボードがあるので、技適付きモジュール搭載ボードしか日本で利用することはできません。上のVGA付きのボードは技適なしですので注意してください。

個人的な感想で、定量的なものではありませんが、M5Stack社の製品が70%ぐらいの完成度で販売しているとすると、TTGOは60%ぐらいなイメージがあります。。。

LOLINの製品

かつてWeMosブランドで販売していたメーカーです。模倣品が多いですが、正規品は非常に安定している製品だと思います。しかしながら、最近はあまり製品を出していないように思えます。

一覧

シリーズCPUSoCPSRAMモジュールボード
ESP32デュアルコアESP32-D*無しESP32-WROOM-32*ESP32-DevKitC
有りESP32-WROVER*ESP-WROVER-KIT
ESP32-PICO-*無しESP32-PICO-V3-ZEROESP32-PICO-KIT
シングルコアESP32-S0* / ESP32-U*無しESP32-SOLO-1ESP32-DevKitC
ESP32-S2シングルコアESP32-S2無しESP32-S2-WROOMESP32-S2-Saola
有りESP32-S2-WROVER
ESP8266 / ESP8285シングルコアESP8266 / ESP8285無しESP-WROOM-02* / ESP-WROOM-S2ESP8266-DevKitC

ざっくりした早見表です。

まとめ

ブログを書いていて、書き方がブレやすいSoCやモジュール、ボードなどの区分を調べ直してみました。よく理解していないときのBlogではチップなどの表記で逃げていたと思います。

Grove端子(HY2.0-4P)について調べてみた

概要

最近使われることが多くなってきたGrove端子について調べてみました。

Grove端子とは?

Grove端子とはSeeed社が展開している4端子の汎用コネクタです。「Grove is an open source」とリンク先のページの最初に書いてありますが、個人的にはわりとクローズな規格だと思っています。。。

電源とGNDの他に2本信号線が来ており、センサーなどとケーブルで接続することでかんたんに利用することができます。

Arduino UNOだとGroveシールドが安いので、非常に使いやすいシステムだと思います。また、M5Stackなどのボードでも互換コネクタが採用されています。

Raspberry Pi用HATなどもありますが、ボード自体にADCが搭載されていない場合、シールド側にADCなどを搭載している関係で非常に高くなっています。

Groveの電源電圧

実はGroveの電源はセンサーなどにより対応電圧が違います。基本的にはArduino UNO用に5Vで動くものが多いですが、3.3Vに対応しているものもあります。

ただし、個別にセンサーなどのページで調べないと対応電圧がわかりません。一度一覧を作ろうと準備しましたが、商品によって記述方法がばらばらだったので諦めました。

ただし、M5Stack社の製品に関してはすべて5Vに統一されています。ESP32だと3.3Vで動いているに、戻りの信号が5Vになるのは本当は良くないと思いますが、その仕様で販売しているようです。

Groveの信号線

接続するセンサーなどによって違いますが、どんな種類で動いているのかがわかりにくいです。特にSeeed社の製品はちゃんと書いていないので、サンプルプログラムを見てどうアクセスしているのかで確認する必要があります。

アナログ

距離センサーや、明るさセンサーなどはアナログ信号の場合が多いです。2本ありますが外側の1本のみ利用して、analogRead()で値を取得するパターンが多いです。

アナログ出力も可能ですが、利用しているのを見たことがありません。DAC出力から外付けスピーカーなどを接続することも可能です。

デジタル

ボタンなどの入力装置の場合にはデジタル入力が多いです。出力も利用可能ですが、それなりに電流を流したい場合には電源端子から電源を供給して、信号線をLOWに落として吸い込む方向で利用したほうが安定します。

こちらも外側の1本のみ利用するパターンが多いです。

I2C

少し複雑な信号をやり取りする場合に利用します。I2C HUBなどを利用すると複数台並行に接続することができます。たくさんつなぎすぎると信号がなまるので限度がありますが、I2Cの切り替えができるスイッチや中継機を入れることで信号を安定させることができます。

I2Cなので信号線は2本とも利用します。

UART(Serial)

無線通信やカメラなどでI2Cより高速で通信を行いたい場合に利用されます。UARTの場合送信と受信で信号がクロスするので、ピンアサインを気にしながら利用する必要があります。

UARTなので信号線は2本とも利用します。

その他

Grove端子の他に専用ケーブルで接続するという、Groveの理念ってなんだろうみたいなセンサーもあります。。。せめて複数本のGroveケーブルで接続してほしかった、、、

Groveの端子について

オス端子(ボード側)

普通のと、L型に90度曲がったタイプの端子が市販されています。L型は秋月電子さんが安いです。

メス端子(ケーブル側)

ケーブル自体は売られていますが、端子は販売されていません!

Groveと互換性のある端子(HY2.0-4P)

やっと本題ですが、Groveと互換性がある端子としてHY2.0-4Pがあります。これはHY型コネクタのピッチ2.0ミリの4端子を意味しています。

おそらく一般的なのが、左にある白いXH型コネクタか、似た形のPH型コネクタ、黒いデュポン(QI)コネクタがよく使われています。しかしながら、GroveはHY型を採用しています。

また、ブレッドボードなどは2.54ミリピッチで設計されていますが、Groveは2.0ミリと少しコンパクトになっています。コネクタが小さくなる利点はありますが、ブレッドボードにはピッチがあわないので、そのまま挿すことができません。

HY自体は4P以外にもっといろいろな端子数がありますが、Grove互換として使う場合には4Pのみ知っていれば問題ないと思います。

GroveとHY2.0-4Pの違い

左が純正Groveケーブルで、黄色が外側になるのが目印です。真ん中がM5Stack用のケーブルで、白が外側にあるのが目印です。右側がAliExpressで購入したHY2.0-4Pコネクタです。

ケーブルの色に意味はないので、この配列じゃなくてもかまわないと思います。

裏側です。GroveとM5Stackのケーブルは同じ形状をしていますが、私の購入したHY2.0-4Pは爪がついています。基本的には普通に購入できるコネクタは爪ありだと思います。

ボードのメス側コネクタはGroveでも爪用のでっぱりがでているので、ケーブルを固定できます。ただし、M5Stackなどの横から隙間に指すタイプだと爪が邪魔になってケーブルが奥まで差し込むことができないようですので注意してください。

オス端子(ボード側)

私が買ったコネクタは基板に固定するタイプじゃないので長いです。Groveの純正と同じタイプのコネクタも販売していました。

こんな感じでオスとメスを接続すると17.5ミリぐらいあります。

コンタクトピンは長めの12ミリでした。コネクタの形状じゃなくて、コンタクトピンの形状がボード側がオスなので、オス端子と呼ぶみたいです。

ケーブル側のコンタクトピン(メス端子)

6ミリぐらいの短いピンですね。YST200とかの型番だと思います。

自作ケーブルに必要な機材

圧着ペンチ

外国産の安いのもありますが、エンジニアのPA-09かPA-20、PA-21あたりが定番です。ペンチでケーブルの品質が左右されるので、なるべく国産を選んだほうが安全です。

私はPA-09を購入しました。微妙に型番によって得意としている端子が違うので注意してください。

普通のペンチで潰して、はんだ付けでもなんとかなりますが、コネクタ自体の性能はかなり落ちると思います。

ワイヤーストリッパ

この辺は少し安いのにしました。TWS-1を持っていますが、普通の電子工作だと細線用のTWS-2のほうが適している気がします。

ケーブル

ケーブルは太さがいろいろあります。太い方が安心できますが、取り回しが面倒になったり固くなったりします。

私のワイヤーストリッパはAWG24より太い線しか使えないので、AWG24の複数色のケーブルと、電力用の少し太いAWG20のケーブルを用意しています。

AWGはケーブルの太さの単位で、数字が少ないほど太くなります。通常はAWG30ぐらいの細さから、AWG20ぐらいの範囲までを使って、標準の太さはAWG24か26ぐらいになります。

安いケーブルは見た目は太いのですが、中身の電線自体は細かったりするので気をつけましょう!

コネクタ

Grove純正品はAmazonなどでも購入できますが、ケーブル側コネクタは国内だと手に入らないみたいです。

aitendoさんで取り扱っていた時期もありますが、今はないかな?

私が購入したのは上のショップです。ここ以外でも取り扱いがあります。下のほうが安いですが、上の店だとメスだけ10個とかも頼めるみたいです。

ただし、メス10個にした場合どっちのコネクタが来るのか怖いので頼んだことありません。ほしいのはケーブル側なのでメス端子なんですが、外のハウジングはオスだけれど、コネクタピンはメスって組み合わせです。間違って到着しそうな気がプンプンするのでセットで頼んでいます、、、

最初に1つ頼んでみて、使えそうだったので追加2つで届いたのが上の写真です。中国からなのでそれなりに時間かかります。

コンタクトピン

既存のケーブルを切って、好きな長さのケーブルを作りたい場合にはコンタクトピンだけあればハウジングは流用できます。

上記以外にも取り扱いがありますが、100個単位での購入だと結構高いです。コネクタ込みで購入してもそう変わらない気がします。

箱には15,000個入っているので、もう少し大きな単位でAlibabaで頼むとかしたほうが安いかな?

端子リリースツール

ハウジングからコンタクトピンを取り外すためのツールです。なくてもなんとかなりますが、あったほうが便利かもしれません。

参考サイト

まとめ

ケーブルを極限まで短くして、Grove端子にUnitが生えているような端子を作っている人がいます!

自分で作れるようになると工作の幅が広がるのでぜひチャレンジしてみてください。ただし、私は機材はあるけれど、もったいないのでブレッドボードにジャンパー線で回路組んでいるので、ユニバーサル基板とかケーブルの在庫が泣いています、、、

続編

Bluetooth小型ポータブルレシートプリンタ「PAPERANG」でシール印刷

概要

先月購入したレシートプリンタ用のシール用紙を購入してみたので、試してみました。

購入物

上記の10メートルが3ロールで300円以下でした。あれ、普通の用紙より安いような、、、

印刷例

上記が本体についてきたレシート紙です。それなりに印刷できるかな?

シール紙で印刷してみましたが、画質変わらず!

少し悪くなると思ったのですが、シールでも普通の紙でも同じ品質で印刷できますね。

シール紙なので、こんな感じで裏紙をはがせばシールとして貼り付けることができます。シール自体は普通の粘着力だったので、一度貼り付けると剥がしにくいです。

今後やりたいこと

上記みたいにデジカメにしたいのです。スマホアプリを使えば実現できるのですが、アプリから撮影すると、印刷した画像が保存されない残念な仕様です。。。

それだったら、シャッター押した瞬間に印刷してほしいです。なのでESP32あたりでカメラ接続して撮影後に即印刷っての作りたいと思っています!

まとめ

シール紙はランニングコスト的にも便利そうでした。お薬手帳みたいなのに貼るシール印刷とかでも使えそうですね。

とはいえ、Python用ライブラリを移植しないと使えないので、そっちの作業からやらないといけない気もします。

プリンター本体は、2980円でじゃんぱらに再入荷していたので、気になる人は購入してみてはどうでしょうか?

Raspberry PiなどのPythonが動く端末やスマホからであれば、かんたんに印刷ができると思います。

ESP32のGPIO研究

概要

Arduino IDEでのGPIOまわりを調査しなおしてみました。pinMode()まわりやアナログ入力のときにはどんなGPIOかを意識して利用する必要があります。

GPIO一覧

GPIO種類アナログ入力タッチ入力RTCPULLpinMode初期値
GPIO0I/OADC2_CH1TOUCH1RTC_GPIO11PULLUP
GPIO1I/OPULLUP
GPIO2I/OADC2_CH2TOUCH2RTC_GPIO12PULLDOWN
GPIO3I/OPULLUP
GPIO4I/OADC2_CH0TOUCH0RTC_GPIO10PULLDOWN
GPIO5I/OPULLUP
GPIO6I/OPULLUP
GPIO7I/OPULLUP
GPIO8I/OPULLUP
GPIO9I/OPULLUP
GPIO10I/OPULLUP
GPIO11I/OPULLUP
GPIO12I/OADC2_CH5TOUCH5RTC_GPIO15PULLDOWN
GPIO13I/OADC2_CH4TOUCH4RTC_GPIO14INPUT
GPIO14I/OADC2_CH6TOUCH6RTC_GPIO16INPUT
GPIO15I/OADC2_CH3TOUCH3RTC_GPIO13PULLUP
GPIO16I/OINPUT
GPIO17I/OINPUT
GPIO18I/OINPUT
GPIO19I/OINPUT
GPIO21I/OINPUT
GPIO22I/OINPUT
GPIO23I/OINPUT
GPIO25I/OADC2_CH8RTC_GPIO6
GPIO26I/OADC2_CH9RTC_GPIO7
GPIO27I/OADC2_CH7TOUCH7RTC_GPIO17INPUT
GPIO32I/OADC1_CH4TOUCH9RTC_GPIO9
GPIO33I/OADC1_CH5TOUCH8RTC_GPIO8
GPIO34IADC1_CH6RTC_GPIO4×
GPIO35IADC1_CH7RTC_GPIO5×
GPIO36IADC1_CH0RTC_GPIO0×
GPIO37IADC1_CH1RTC_GPIO1×
GPIO38IADC1_CH2RTC_GPIO2×
GPIO39IADC1_CH3RTC_GPIO3×

種類

GPIO34以降は入力専用です。PULLUPやPULLDOWNもできません。

アナログ入力

アナログ入力する場合にはADCに接続されているGPIOを利用する必要があります。ただしWi-Fiなどの無線を利用する場合にはADC2が利用できなくなります。一度でも無線を利用すると、再起動するか内部レジスタを書き換えない限りADC2は利用できないので、注意してください。

タッチ入力

タッチセンサーを利用できるGPIOです。タッチセンサーは無線を利用しても、利用できるGPIOが制限されません。

ただし、実際に使ってみるとGPIO32とGPIO33が逆なようですが、おそらく修正されないので実験してから使ってみてください。

RTC

RTCは内部的なGPIOへのアクセス方法で、ULPからアクセスする場合に利用します。ADCが使えるGPIOは、RTCでも接続できます。つまり、ULPからアクセスする場合にはアナログ入力が可能であるGPIOである必要があります。

RTCの端子番号の並びがGPIOとも、ADCとも違うので注意してください。

PULL

ESP32はGPIOに対して、PULLUPとPULLDOWNを設定することができます。細かい設定はpinMode()で解説を行いますが、入力専用端子のGPIO34以降はPULL系の機能も使えないので注意してください。

pinMode初期値

GPIOの起動直後のピンモード設定です。ただしArduinoや、その他ライブラリなどにて再初期化されている場合があるので注意してください。

pinMode()一覧

モード入力接続入力値範囲LOW出力HIGH出力
INPUT0 – 1
INPUT_PULLUP3.3Vに抵抗経由で接続0 – 1
INPUT_PULLDOWNGNDに抵抗経由で接続0 – 1
ANALOG0 – 4095
OUTPUT0V(GND)3.3V
OUTPUT_OPEN_DRAIN0V(GND)Hi-Z

INPUT

デジタル入力としてLOW(0)かHIGH(1)を入力します。すべてのGPIOで利用可能です。ESP32は3.3V動作ですので、25%の0.825Vより低い電圧の場合LOWになり、75%の2.475Vより高いとHIGHになります。26%から74%までの間はどちらになるかわかりません。ただし、実測では大体50%の電圧で判定しています。

閾値に近い電圧の場合には、ノイズなので入力値が変わったりするのでなるべくGNDか3.3Vかを入力されるようにしてください。

未接続(フローティング)のGPIOに対して入力を行うと、端子の残っている静電気のようなものを入力してしまうので、値が信用できなくなりますので注意してください。

INPUT_PULLUP

INPUTと同じく、デジタル入力としてLOW(0)かHIGH(1)を入力します。入力専用のGPIO34以降のGPIOだと利用できないので注意してください。

電源の3.3Vに抵抗経由で接続されている端子で、通常はHIGHの入力になりますが、スイッチなどでGNDに落とすことでLOWに変化します。

未接続のGPIOでもHIGHが安定して入力することができます。通常がHIGHなので、ノイズの影響を受けにくいために利用されることがあります。

INPUT_PULLDOWN

INPUTと同じく、デジタル入力としてLOW(0)かHIGH(1)を入力します。入力専用のGPIO34以降のGPIOだと利用できないので注意してください。

GNDに抵抗経由で接続されている端子で、通常はLOWの入力になりますが、スイッチなどで電源に接続することでHIGHに変化します。

未接続のGPIOでもLOWが安定して入力することができます。通常のINPUTに比べるとノイズの影響は受けにくいです。

ANALOG

GPIOADC無線未使用時無線使用時
GPIO0ADC2_CH1
GPIO2ADC2_CH2
GPIO4ADC2_CH0
GPIO12ADC2_CH5
GPIO13ADC2_CH4
GPIO14ADC2_CH6
GPIO15ADC2_CH3
GPIO25ADC2_CH8
GPIO26ADC2_CH9
GPIO27ADC2_CH7
GPIO32ADC1_CH4
GPIO33ADC1_CH5
GPIO34ADC1_CH6
GPIO35ADC1_CH7
GPIO36ADC1_CH0
GPIO37ADC1_CH1
GPIO38ADC1_CH2
GPIO39ADC1_CH3

アナログ入力として0 – 4095の値を入力します。入力範囲はanalogReadResolution()にてADC取得解像度設定で、より荒い値に変更することができます。

0は0V、4095は3.3Vに対応していますが、精度はそれほど高くないので注意して利用してください。

ADCに接続されているGPIOでのみアナログ入力が可能です。GPIO32以降であればどんな状況でも利用可能です。無線を利用しない場合にはADC2に接続されている端子でも利用が可能です。

OUTPUT

デジタル出力としてLOW(0V, GND)かHIGH(3.3V)を出力します。入力専用を除き、GPIO0からGPIO33までのGPIOで利用可能です。

LOWの0Vに出力は、GPIOに接続されている端子をGNDとつなげることを意味します。外部からの電圧をそのGPIOに吸い込む方向になります。LEDなどのマイナス側をGPIOに接続して、LOWにすることでLEDを点灯させることが可能です。

OUTPUT_OPEN_DRAIN

デジタル出力としてLOW(0V, GND)かHIGH(Hi-Z, 未接続)を出力します。入力専用を除き、GPIO0からGPIO33までのGPIOで利用可能です。

HIGHのHi-Z(ハイインピーダンス)は未接続の状態です。I2Cなどで別の回路からPULLUPされている場合などにLOWを出力することでGNDに接続し、HIGHを出力した場合にはなにもしません。これは、PULLUPされている場合に3.3V出力をしても電源の無駄になるので、未接続にします。

未接続GPIOに注意

GPIOに何も接続していない場合に入力値が不安定になります。これはADCに静電気のような電荷が残っており、その値を取得しているため正しい値ではありません。

この値は不安定で、変わることもありますし、直前に入力したGPIOの値に引きづられることもあります。接続されていないGPIOの値は読み取らないか、INPUT_PULLUPかINPUT_PULLDOWNなどで内部的に接続することで、値が安定します。

また、ESP32のタッチセンサーは電荷をチャージして、その後入力値を取得することで、触っている場合には電荷が人に移動してLOWになったことを確認していたりします。

pinMode()の未設定に注意

アナログ入力の場合、pinMode()を設定していない場合、内部でアナログ入力モードに切り替わっておらず、デジタル入力相当の0か4095しか戻ってこないGPIOがあります。

起動時にアナログ入力に初期化されているGPIOもあるのですが、安全のためすべてのGPIOを利用前にpinMode()の設定をすることを心がけてください。

参考になるかもしれないページ

外部参考ページ

ハードウエア解説

関数解説

まとめ

あまり日本語でESP32のpinMode()に関する資料がありません。とくに未設定の場合にアナログ入力が動いているように見えて、おかしな数値が戻ってくるので注意してください。

また、GPIO34以降の入力専用GPIOはPULLUPやPULLDOWNが利用できないのもはまりやすいので注意してください。

M5StickC UIFlow V1.4.4更新差分調査(V1.4.2との差分)

概要

気がついたらバージョンが2つあがっていたので、V1.4.2からの差分を調べてみました。

UIFlow Desktop

2020/01/22現在まだV1.4.3が使える1.0.6しかありません。メニューのCheckUpdateからバージョンチェックして、更新があればそのまま更新できます。

更新したあとにFirewareBurnerを使って、該当バージョンのUIFlowファームウェアも転送しなおしましょう。

モジュール追加

クラス追加

詳細な変更点

まとめ

細かいところまで追いきれていません。とりあえずファーム焼いて、内部のモジュールとクラス構造を自動取得したところまでです。

Powercに電圧とか電流とか取得できるようになっていますが、内部バッテリーの値だけ取得しているのだろうか?