Espruino on ESP32 でWi-Fiルータ(or AP)に接続する
ESP32をWi-Fiルータ または アクセスポイントに接続します。
以下のプログラムを実行すると、Wi-Fiルータに接続できます。
var SSID_NAME = "SSID名"; var SSID_PASS = "パスワード"; // Wi-Fi アクセスポイントへの接続 var wifi = require('Wifi'); wifi.connect(SSID_NAME, {password: SSID_PASS}, function() { wifi.save(); // 次回以降、起動時に再接続されるように設定 console.log('Connected to Wifi. IP address is:', wifi.getIP().ip); });
一度実行すると、次回以降の起動時に自動的に再接続してくれます(電源OFFでも有効)。
ただし、SPI-Flashを全イレースした場合は再実行する必要があります。
Espruino本体だけ書き換えた場合は設定が残っているようです。
また、保存した接続情報を無効にしたい(自動で再接続しないようにする)場合は以下のように実行します。
wifi.save("clear")
以下のようなファイルをオンラインストレージに/node_modules/MyWifi.js というファイル名で保存しておき、
var SSID_NAME = "SSID名"; var SSID_PASS = "パスワード"; // Wi-Fi アクセスポイントへの接続 var wifi = require('Wifi'); var wifi_sta = wifi.getStatus(); if (wifi_sta.station != 'connected') { wifi.connect(SSID_NAME, {password: SSID_PASS}, function() { wifi.save(); // Next reboot will auto-connect console.log('Connected to Wifi. IP address is:', wifi.getIP().ip); }); } else { console.log('already Connected to Wifi. IP address is:', wifi.getIP().ip); } exports = wifi;
Wi-Fiモジュールを読み込む
var wifi = require('Wifi');
の代わりに
var wifi = require('MyWifi');
とすることで、接続されていなければ接続してくれるので、便利です。
SSIDパスワードが平文でストレージに残っているのが嫌な人にはオススメできませんが。。。
Espruino on ESP32 でぽちっとな
前回はLED出力したので、次はボタンをぽちっと押してみます。
GPIO端子(下の例ではIO21)にpull-up抵抗とスイッチを接続し、Active Lowな入力にします。
以下のプログラムを実行すると、スイッチを押したり放したりするとONとかOFFとか表示される
// initialize for switch var sw1 = Pin(21); sw1.mode("input" ); function switch_handler(e) { stat = e.state ? 'OFF':'ON'; console.log("switch : " + stat); } setWatch(switch_handler, sw1, {repeat:true, edge:"both", debounce:100}); // repeat: true/false(default) // edge:'rising'/'falling'/'both' // debounce: default=10
はずなんですが。。。。
ASSERT(channel>=EV_EXTI0 && channel<=EV_EXTI_MAX) FAILED AT ...
とのたまって、お亡くなりになります。
どうやら、channelがEV_EXTI0 から EV_EXTI_MAX の範囲に入ってないとおっしゃっているようです。
ということで、以下の修正をします。
diff --git a/src/jsdevices.h b/src/jsdevices.h index ec03511..3f67ad4 100644 --- a/src/jsdevices.h +++ b/src/jsdevices.h @@ -51,7 +51,35 @@ typedef enum { EV_EXTI13, // External Interrupt 13 EV_EXTI14, // External Interrupt 14 EV_EXTI15, // External Interrupt 15 +#ifdef ESP32 + EV_EXTI16, // External Interrupt 16 + EV_EXTI17, // External Interrupt 17 + EV_EXTI18, // External Interrupt 18 + EV_EXTI19, // External Interrupt 19 + EV_EXTI20, // External Interrupt 20 + EV_EXTI21, // External Interrupt 21 + EV_EXTI22, // External Interrupt 22 + EV_EXTI23, // External Interrupt 23 + EV_EXTI24, // External Interrupt 24 + EV_EXTI25, // External Interrupt 25 + EV_EXTI26, // External Interrupt 26 + EV_EXTI27, // External Interrupt 27 + EV_EXTI28, // External Interrupt 28 + EV_EXTI29, // External Interrupt 29 + EV_EXTI30, // External Interrupt 30 + EV_EXTI31, // External Interrupt 31 + EV_EXTI32, // External Interrupt 32 + EV_EXTI33, // External Interrupt 33 + EV_EXTI34, // External Interrupt 34 + EV_EXTI35, // External Interrupt 35 + EV_EXTI36, // External Interrupt 36 + EV_EXTI37, // External Interrupt 37 + EV_EXTI38, // External Interrupt 38 + EV_EXTI39, // External Interrupt 39 + EV_EXTI_MAX = EV_EXTI39, +#else EV_EXTI_MAX = EV_EXTI15, +#endif EV_SERIAL_START, EV_LOOPBACKA = EV_SERIAL_START, EV_LOOPBACKB,
動くようになりましたが、何度かスイッチを押していると
今度はcoreを吐いてお亡くなりになります。
調べてみると、
GPIO割り込み発生
→ _xt_lowint1
→ gpio_intr_handler
→ jshPushIOWatchEvent
→ jshGetSystemTime
→ gettimeofday
→ _gettimeofday_r
→ get_boot_time
→ _lock_acquire
と動いたところでmutexのロックに失敗してabort()しているらしいです。
# mutexのロックに失敗したくらいでcore吐くな、と言いたいところをぐっっっと我慢して
つまり、jshGetSystemTime()の中でgettimeofdayをコールしないようにすれば良いわけです。
で、石の上で3年考えた結果、以下のような修正を入れてみました。
diff --git a/targets/esp32/jshardware.c b/targets/esp32/jshardware.c index 2126573..7cef8b6 100644 --- a/targets/esp32/jshardware.c +++ b/targets/esp32/jshardware.c @@ -23,6 +23,9 @@ * approval from all the stakeholders. In addition, the semantics of the * functions should follow the expected conventions. */ + +#define IPPEI_CHANGE // ######################################################################################## + #include <stdio.h> #include "jshardware.h" @@ -499,20 +502,41 @@ JsVarFloat jshGetMillisecondsFromTime(JsSysTime time) { return (JsVarFloat) time / 1000.0; } +#ifndef IPPEI_CHANGE // ####################################################################################### +#else // ######################################################################################## +static JsSysTime offsetSystemTime = 0; +static TickType_t prevRtosTick = 0; +#endif // ######################################################################################## /** * Return the current time in microseconds. */ JsSysTime CALLED_FROM_INTERRUPT jshGetSystemTime() { // in us -- can be called at interrupt time +#ifndef IPPEI_CHANGE // ######################################################################################## struct timeval tm; gettimeofday(&tm, 0); return (JsSysTime)(tm.tv_sec)*1000000L + tm.tv_usec; +#else // ######################################################################################## + TickType_t curRtosTick = xTaskGetTickCountFromISR(); + // RTOS tick counter overflow check + if (prevRtosTick == 0) { + // offsetSystemTime already modified + } + else if (prevRtosTick > curRtosTick) { + // RTOS tick counter overflowed!! + offsetSystemTime += 0x0000000100000000* portTICK_PERIOD_MS * 1000; + } + prevRtosTick = curRtosTick; + JsSysTime cur = (JsSysTime)curRtosTick * portTICK_PERIOD_MS * 1000; + return cur + offsetSystemTime; +#endif // ######################################################################################## } /** * Set the current time in microseconds. */ void jshSetSystemTime(JsSysTime newTime) { +#ifndef IPPEI_CHANGE // ######################################################################################## struct timeval tm; struct timezone tz; @@ -521,6 +545,11 @@ void jshSetSystemTime(JsSysTime newTime) { tz.tz_minuteswest=0; tz.tz_dsttime=0; settimeofday(&tm, &tz); +#else // ######################################################################################## + // printf("system time set to %lld\n", newTime); + JsSysTime cur = (JsSysTime)xTaskGetTickCountFromISR() * portTICK_PERIOD_MS * 1000; + offsetSystemTime = newTime - cur; +#endif // ######################################################################################## } void jshUtilTimerDisable() {
要は、gettimeofdayで時刻を取得する代わりに、
xTaskGetTickCountFromISRでfreeRTOSのtickカウンタから時刻を取得するようにしています。
ただし、このカウンタは32bitしかありません。
1msecでカウントアップしているので、約49日でオーバフローしてしまいます。
# お亡くなりになった方の行先を閻魔様がお決めになるまでと同じ日数ですね。
そこで、ごちゃごちゃと対処療法を行っています。
これで最初のプログラムを実行すると、以下のような出力が得られます。
switch : ON switch : OFF switch : ON switch : OFF switch : ON switch : OFF
めでたしめだたし。
いちお、プログラムの解説らしきものも。
// initialize for switch var sw1 = Pin(21); sw1.mode("input" ); function switch_handler(e) { stat = e.state ? 'OFF':'ON'; console.log("switch : " + stat); } setWatch(switch_handler, sw1, {repeat:true, edge:"both", debounce:100}); // repeat: true/false(default) // edge:'rising'/'falling'/'both' // debounce: default=10
最初に端子を入力モードに設定しています。
switch_hanlerがスイッチがON/OFFされたときに呼び出される関数です。
パラメータeのプロパティstateに端子の状態(highのときtrue、lowのときfalse)が入っています。
入力はActive Lowなので、trueのとき、スイッチの状態はOFFです。
setWatchでハンドラを登録しています。
パラメータedgeで立ち上がりエッジ、立ち下りエッジ、両エッジを指定します。
パラメータdebounceでチャタリング除去時間を設定します。単位はmsec。
値が小さいと反応速度は上がりますが、チャタリングを除去しきれないかもしれません。値が大きいと確実にチャタリングを除去できますが、反応速度が遅くなります。(大きすぎると、スイッチを押して放したこと自体見落としてしまうかもしれません)
Espruino on ESP32 でLチカ
ということで、今更ながらLチガ。
GPIO端子(下の例ではIO23)にLEDと電流制限用抵抗を接続。
Active HighでもActive LowでもOK(点滅するので、どっちになってても分からない)。
点灯するだけではチカと言い難いので、点滅させてみました。、
で、以下のプログラムを実行すると、LEDが1秒ごとに点滅します。
// Initialize for LED1 var led1 = Pin(23); led1.mode("output"); led1.reset(); // cyclic execution setInterval(function(){ led1.toggle(); },1000);
EspruinoのオンボードストレージをLinux上で作成する
「まずはLチカやろ?」というツッコミを覚悟の上で、いきなりオンラインストレージです。
ESP32のEspruinoはオンボードのSPI-Flash上にオンボードストレージを持っています。
オンボードストレージはSPI-Flashのアドレス0x300000 から1Mバイトに配置されています。
通常は以下のような手順で使用します。
フォーマットする
E.flashFatFS({format:true});
ファイルを書き込む
var fs = require("fs"); var dat = "hogehoge"; fs.writeFileSync("/hoge.txt", dat);
ファイルを読み出す
var read_data=fs.readFileSync("/hoge.txt");
これだと、大きなファイルを書き込むのにはちょっと不便です。
以下は、このディスクイメージをLinux上で作成し、SPI-Flashに書き込む方法です。
モジュールファイルなどをあらかじめ書き込んでおくのに有効です。
(Linux上での操作です)
まず、空のバイナリファイルを作成し、FATでフォーマットします。
dd if=/dev/zero bs=1K count=1024 | tr "\000" "\377" > esp_diskimage.bin mkfs.fat -f 1 -F 12 -s 1 -S 4096 -M 0xF0 esp_diskimage.bin
ディスクイメージファイルをマウントします。
mkdir mnt sudo mount -o loop esp_diskimage.bin mnt
書き込みたいファイルをコピーします。パーミッションの関係で、sudoで実行する必要があります。
sudo cp 《コピーするファイル》 mnt/
書き込みが終わったらマウント解除します。
sudo umount mnt
出来上がったディスクイメージファイルをESP32に書き込みます。
/work2/esp/Espruino/esp-idf/components/esptool_py/esptool/esptool.py \ --chip esp32 --port /dev/ttyUSB0 --baud 921600 \ write_flash --flash_mode "dio" --flash_freq "40m" \ 0x300000 esp_diskimage.bin
ちなみに、現在のオンボードストレージをバッグアップしたい場合は以下のように実行します。
/work2/esp/Espruino/esp-idf/components/esptool_py/esptool/esptool.py \ --chip esp32 --port /dev/ttyUSB0 --baud 921600 \ read_flash 0x300000 0x100000
ESP32でEspruinoを試す
ESP32でJavascriptのtiny版 Espruino を試す
ESP32で色々プログラムを書いてみるのに、arduino IDEを使うと逐一Flashの書き換えを行うことになってなにかと不便なので、スクリプト言語を使ってみることにします。
で、やっぱりスクリプト言語ならpythonだよね~。。。とならないのが天邪鬼(^^ゞ
Javascriptのtiny版 Espruino を使ってみます。
ちなみに、EspruinoのEspとESP32のESPは何の関係もありません(たぶん)。
Espruinoの基本的な使い方は以下にあります。
私は開発環境として、Windows10上の Virtualbox + Ubuntu 16.04 を使用しています。
コンパイルだけなら、Windows Subsystem for Linux(WSL)でも出来るかもしれませんが、WSLではCOMポートにアクセスできないみたいなので、Flashの書き込みができません。
以下、作業ディレクトリに/work2を使用しているものとして例を記載します。
(/workや/work1でないのは大人の事情です。。。)
また、動作を確認したEspruinoはversion1.93です。
mkdir -p /work2/esp cd /work2/esp git clone https://github.com/espruino/Espruino.git cd Espruino source scripts/provision.sh ESP32
次回から環境設定を自動にするため、~/.profileに以下を追加しておきます。
export PATH=$PATH:/work2/esp/Espruino/xtensa-esp32-elf/bin export ESP_IDF_PATH=/work2/esp/Espruino/esp-idf export ESP_APP_TEMPLATE_PATH=/work2/esp/Espruino/app
~/.profileを修正したら、変更を有効にするため、いったんログオフして再度ログインします。
makeするには、
BOARD=ESP32 make
とすればよいのですが、毎回BOARD=ESP32と指定するのは面倒なので、Makefileに以下の修正を入れておきます。これでespディレクトリ下で作業していると自動的にESP32が選択されます(変更内容には次回以降のネタも一部含みます)。
diff --git a/Makefile b/Makefile index c0acf8d..1ca3968 100755 --- a/Makefile +++ b/Makefile @@ -135,8 +135,22 @@ ifeq ($(BOARD),) #$(info *************************************************************) #$(info * To build, use BOARD=my_board make *) #$(info *************************************************************) - BOARD=LINUX - DEFINES+=-DSYSFS_GPIO_DIR="\"/sys/class/gpio\"" + #BOARD=LINUX + #DEFINES+=-DSYSFS_GPIO_DIR="\"/sys/class/gpio\"" + GET_PLATFORM_DIR = echo $(CURDIR) | sed -e 's/^.*\/\(.*\)\/.*$$/\1/' + PLATFORM_DIR = $(shell $(GET_PLATFORM_DIR)) + ifeq ($(PLATFORM_DIR), esp) + BOARD=ESP32 + $(info ###############################################################) + $(info ######## default board choose ESP32 ###########################) + $(info ###############################################################) + else + BOARD=LINUX + DEFINES+=-DSYSFS_GPIO_DIR="\"/sys/class/gpio\"" + $(info ###############################################################) + $(info ######## default board choose Linux ###########################) + $(info ###############################################################) + endif endif endif @@ -543,6 +557,10 @@ libs/crypto/mbedtls/library/sha1.c \ libs/crypto/mbedtls/library/sha256.c \ libs/crypto/mbedtls/library/sha512.c +ifdef USE_HMACSHA1 + DEFINES += -DUSE_HMACSHA1 +endif + ifdef USE_TLS USE_AES=1 DEFINES += -DUSE_TLS @@ -776,14 +794,16 @@ endif clean: @echo Cleaning targets - $(Q)find . -name \*.o | grep -v arm-bcm2708 | xargs rm -f + $(Q)find . -name \*.[od] | grep -v arm-bcm2708 | xargs rm -f $(Q)rm -f $(ROOT)/gen/*.c $(ROOT)/gen/*.h $(ROOT)/gen/*.ld $(Q)rm -f $(ROOT)/scripts/*.pyc $(ROOT)/boards/*.pyc + $(Q)rm -f $(PROJ_NAME) $(Q)rm -f $(PROJ_NAME).elf $(Q)rm -f $(PROJ_NAME).hex $(Q)rm -f $(PROJ_NAME).bin $(Q)rm -f $(PROJ_NAME).srec $(Q)rm -f $(PROJ_NAME).lst + $(Q)rm -f $(PROJ_NAME).map # start make like this "make varsonly" to get all variables created and used during make process without compiling # this helps to better understand linking, or to find oddities
ESP32ボードをPCに接続し、virtualbox側に認識させます。
ESP32ボードは/dev/ttyUSB0に接続されているものとします。
一旦Flashをイレースします。
make erase_flash
バイナリを書き込みます。
make flash
書き込みが終わったらシリアルターミナル(Ubuntuならgtkterm、miniterm.pyなど、windowsならteratermなど)を起動し、ESP32のリセットボタンを押します。
設定は以下の通り
ボーレート | 115200bps |
データ長 | 8bit |
パリティ | none |
ストップ | 1bit |
フロー制御 | Xon/Xoff |
あとはシリアルターミナルからプログラムを入力すれば実行できます。
>console.log("Hello Espruino!") Hello Espruino! =undefined
お約束の断り書きです。
このブログに記載されている内容は私の作業メモであり、動作を保証するものではありません。
これらの情報を利用することによって生ずるいかなる損害に対して一切の責任を負いません。
ESP32をポチってしまった
世の中の流行に乗って(?)ESP32を使ってみる
以前、ESP8266が流行っていると聞いたが、天邪鬼な私は手を出さないでいました。
そうこうしているうちに、その後継ESP32が出たとの情報。
て、ちょっと魔がさして弄ってみようと思ってしまい、ポチってしまいました。
ポチったのはこちら
ESP-WROOM-32ブレイクアウトSD+ - スイッチサイエンス
Espressif純正のESP32-DevKitCは電源回りがあやしいとの情報があり、ちょっと高いけど、こっちを選択しました。
SDカードスロットも付いてて便利かな~と思ったけど、micropythonやEspruinoではSPI接続のSDカードドライバしかサポートしていなく、SDHC接続のこのボードではそのまま使えません(T_T)