エンジニア風味 (Engineer-taste)

電子系エンジニアのメモ帳

ESP-WROOM-02で無線LANチャイムをつくる(ISR not in IRAM! 編)

前に作成したESP-WROOM-02による無線LANチャイムを改造していたら、なぜかハマってしまったので書きます。。

今回、やろうとしたところは「送信機と受信機のコネクションが切れたときに、わかるようにしよう」ということです。
もともとのプログラムにはシリアル出力で「DisConnect」のステータスを出す機能があるのですが、通常はPCに接続して使わないので、わかりません。

(略)
void webSocketEvent(WStype_t type, uint8_t * payload, size_t lenght) {

    switch(type) {
        case WStype_DISCONNECTED:
            Serial.printf("[WSc] Disconnected!\n");// <---------シリアルにしかDisConnect情報が行かない。
            ConnectFlag = false;
            break;

        case WStype_CONNECTED:
            {
(略)

こんな感じだったので、送信機についているLEDをDISCONNECTEDの時には、点滅させるようにしようと思いました。

(略)
void webSocketEvent(WStype_t type, uint8_t * payload, size_t lenght) {

    switch(type) {
        case WStype_DISCONNECTED:
            Serial.printf("[WSc] Disconnected!\n");
            ConnectFlag = false;
//ここから追加(LEDチカチカ)
            digitalWrite(led1, HIGH);
            delay(100);
            digitalWrite(led1, LOW);
            delay(100);
            digitalWrite(led1, HIGH);
            delay(100);
            digitalWrite(led1, LOW);
            delay(100);
            digitalWrite(led1, HIGH);
            delay(100);
            digitalWrite(led1, LOW);
            delay(2000);
//ここまで追加
            break;

        case WStype_CONNECTED:
            {
(略)

こんな感じです。

で、前回から日数が経っていて、Arduino IDEも新しくなっていてVersionが1.8.9で、一方ESP8266のライブラリ(esp8266 by ESP8266 Community)もver.2.5.2にバージョンアップしています。
これを使ってコンパイル&インストールしてみました。

ところが、うまく動いてくれません。
Arduino IDEのシリアルモニタをみると、下のようになっています。

f:id:engineer-paju:20190922143451p:plain
エラー内容

この"ISR not in IRAM"というのを、とりあえずググってみますと、ESP8266のライブラリを新しくすると出るようです。古いバージョンなら大丈夫だということ。
さらに調べると、Arduino Forumで、関係するスレッドがみつかりました。

端的に書きますと、この問題は「ESPのバージョン2.5.1以降で、割り込み処理を行う場合に発生する」、ということです。
新しいバージョンにおけるバグではなく、古いバージョンにおいてIRAMにISRがない(予測不可能なリスクを持っている)ことを回避するために必要だそうです。

解決策としては、ICACHE_RAM_ATTRを追加すればよいそうです。これを追加してプログラムを修正してみました。
void ICACHE_RAM_ATTR intFunc(); で、intFuncが割り込みルーチンです。

#include <ESP8266WiFi.h>
#include <WebSocketsClient.h> //https://github.com/Links2004/arduinoWebSockets
#include <Ticker.h>

#define btn1 4 //スイッチ1のGPIO

//LED
#define led1 13 //LED1のGPIO

//ServerIP
const char* ServerIp("192.168.xx.xx"); //サーバーに割り当てるアドレス
int ServerPort = 8000;

//Ssid, pass
const char* Ssid = "xxxx"; //ルータのID
const char* Password = "xxxx"; //ルータのpassword

int b; //ボタン状態


WebSocketsClient webSocket;

//タイマ初期化
Ticker ticker;

//Connected Flag 接続されるとtrue
bool ConnectFlag = false;

//Timer callback
void doBlockingIO() {
    Serial.println(b);
    if(ConnectFlag){
       if(b){
        webSocket.sendTXT("1"); //Send Message
        b=0;
        digitalWrite(led1, HIGH);
        delay(500);
        digitalWrite(led1, LOW);
        delay(500);
        digitalWrite(led1, HIGH);
        delay(500);
        digitalWrite(led1, LOW);
       }
       else{
        webSocket.sendTXT("0"); //Send Message
       }
    }
}

void webSocketEvent(WStype_t type, uint8_t * payload, size_t lenght) {

    switch(type) {
        case WStype_DISCONNECTED:
            Serial.printf("[WSc] Disconnected!\n");
            ConnectFlag = false;
//DisConnect対策を追加
            digitalWrite(led1, HIGH);
            delay(100);
            digitalWrite(led1, LOW);
            delay(100);
            digitalWrite(led1, HIGH);
            delay(100);
            digitalWrite(led1, LOW);
            delay(100);
            digitalWrite(led1, HIGH);
            delay(100);
            digitalWrite(led1, LOW);
            delay(2000);
//DisConnect対策ここまで
            break;

        case WStype_CONNECTED:
            {
                Serial.printf("[WSc] Connected to url: %s\n",  payload);
                ConnectFlag = true;
            }
            break;

        case WStype_TEXT:
           Serial.printf("[WSc]Recv: %s\n", payload);

            // send data to back to Server
            //webSocket.sendTXT(payload, lenght);
            break;

        case WStype_BIN:
            Serial.printf("[WSc] get binary lenght: %u\n", lenght);
            hexdump(payload, lenght);

            // echo data back to Server
            webSocket.sendBIN(payload, lenght);
            break;

        case WStype_ERROR:
            Serial.printf("We received an error \n");
            break;

        default:
            break;
    }
}

void ICACHE_RAM_ATTR intFunc();// <-----------------これを追加する

void setup() {
    pinMode(led1, OUTPUT);
    Serial.begin(115200);
    Serial.print("\nStart\n");

    pinMode(btn1, INPUT_PULLUP); //btn1は入力でプルアップされている
    attachInterrupt(btn1,intFunc,FALLING); //btn1がHigh→Lowで割り込みルーチンへ

    WiFi.begin(Ssid, Password);
    while(WiFi.status() != WL_CONNECTED) {
        Serial.print('.');
        delay(500);
        }

    Serial.println();
    Serial.printf("Connected, IP address: ");
    Serial.println(WiFi.localIP());

    //server ip
    webSocket.begin(ServerIp, ServerPort);
    webSocket.onEvent(webSocketEvent);
    Serial.println("Client started\n");
    digitalWrite(led1, HIGH);
    delay(500);
    digitalWrite(led1, LOW);
    delay(500);
    digitalWrite(led1, HIGH);
    delay(500);
    digitalWrite(led1, LOW);
    delay(500);
    digitalWrite(led1, HIGH);
    delay(500);
    digitalWrite(led1, LOW);

    //set timer
    ticker.attach(2, doBlockingIO); //2sec
}


void loop() {
    webSocket.loop();
}

void intFunc(){
  b=1; //ボタンが押されたステータス発行
}

これで、コンパイルかけて、書き込み、実行させると、

f:id:engineer-paju:20190922150503p:plain
エラー解消

正常に動作するようになりました。

お疲れ様でした。