ESP32上でJavascriptを動かすための環境構築と、Javascriptを埋め込むための開発環境を構築します。
ESP32で動作するJavascriptエンジンは、QuickJSをESP32で動作するようにポーティングされたesp32quickjsです。
そこに、GPIOやWireといったESP32のインタフェース、SDやM5Unitといったデバイスを操作するためのライブラリを追加してあります。
【参考】
https://github.com/bellard/quickjs
https://github.com/binzume/esp32quickjs
もろもろはこちらのGitHubに公開しています。
また、ESP32用のJavascript APIリファレンスはこちらです。是非お手に取ってみてください。
ESP32上でJavascriptを動かすための環境構築
まずは、ESP32モジュールに、Javascriptが動作するための実行環境を備えたファームウェアを書き込む必要があります。
主に以下の2つを使います。
・Visual Studio Code
・PlatformIO IDE
やっていることは、GitHubからダウンロードしたファイルをPlatformIOで開き、接続するESP32モジュールを選択して、Visual Studio Code(以降VSCode)上からBuild・Uploadするだけです。以降で説明します。
Visual Studio Codeのインストール
まだインストールしていない場合は、以下から、VSCodeをダウンロードしてインストールします。
必要に応じて、以下のVSCodeの拡張機能もインストールしておくとよいです。
・Japanese Language Pack for Visual Studio Code
Visual Studio Codeの拡張機能 PlatformIO IDEのインストール
VSCodeでESP32を扱うための以下のVSCodeの拡張機能をインストールします。
・PlatformIO IDE
インストール後、VSCodeを再起動します。
Javascript実行エンジンのファームウェアのダウンロード
まずは以下からZIPダウンロードします。
これが、ESP32で動作するJavascriptの実行環境です。各ESP32モジュールに対して、1度だけファームウェアを書き込んでおく必要があります。
適当なフォルダにZIP展開します。
その後、VSCodeからフォルダを開きます。開くフォルダは展開後にある以下のフォルダです。
QuickJS_ESP32_IoT_Device_M5Unified-main\QuickJS_ESP32_Firmware
ESP32モジュールを接続
ESP32モジュールをPCのUSBポートに接続します。
もし、ESP32モジュールがCOMポートとして認識しない場合、USBドライバのインストールが必要です。以下のサイトの「USB Driver」にあります。
通常は、CP210X_VCP_XXXXをインストールすればよいでしょう。
認識後、ポート番号を確認しておきます。
次に、VSCodeから接続されたモジュールに該当する選択肢を選びます。
下の方にあります。
これらは、platformio.iniに定義されています。
私の方で確認しているESP32モジュールの選択肢は以下の通りです。
| 選択肢 | 対応するESP32モジュール |
|---|---|
| m5stick-c | M5StickC、M5StickCPlus |
| m5stack-sticks3 | M5StickS3 |
| m5stack-core2 | M5Stack Core2 |
| m5camera | M5Camera、M5CameraX |
| m5stack-fire | M5Stack Fire |
| m5stack-atom | M5Atom Lite、M5Atom Matrix、M5AtomEcho |
| m5stack-atoms3 | M5AtomS3 |
| m5stack-atoms3r | M5AtomS3R |
| m5stack-stampc3 | M5StampC3 Mate ※ |
| m5stack-stampc3u | M5StampC3U Mate ※ |
| m5stack-nanoc6 | M5Stack NanoC6 |
※M5StampC3の場合は、earlephilhower/ESP8266Audio と m5stack/M5UnitSynth を lib_depsから外す必要があるようです
また、platformio.iniの選択したモジュールにおいて、確認しておいたCOMポートに変更します。
以下の部分です。
upload_port = COM7
monitor_port = COM7
Build、Uploadを実行します。これも下の方にあります。チェックマークがBuild、→矢印がUploaです。
これには、分オーダーで時間がかかるので、辛抱強く待ってください。
Uploadが完了したら、起動しているはずです。Serial Monitorを起動して、コンソール出力を確認します。以下の操作で表示できます。
Ctrl+Shift+Pを押下 → PlatformIO: Serial Monitor を選択
Serial Monitorでは、最初に起動したときにWiFiアクセスポイントへの接続情報(SSID、パスワード)の入力が促されます。
auto detect board:3
[display_count=1]
[initializing]
WiFi Connenting[ 1616][E][WiFiSTA.cpp:317] begin(): connect failed! 0x300a
input SSID:
input PASSWORD:
SSID=XXXXXX PASSWORD=****************
WiFi Connenting....
接続が完了すると、以下のように表示され、IPアドレスが割当たっていることがわかります。後で、このIPアドレスを使います。
Connected : IP=192.168.1.248 Mac=XX:XX:XX:XX:XX:XX
MDNS_NAME: QuickJS_ESP32_M5StickC
serivce_name: http, TCP_PORT: 80
[ 16091][E][vfs_api.cpp:105] open(): /littlefs/main.js does not exist, no permits for creation
[can't load main]
jsfile load failed
もし、流れてしまって、表示しそこなった場合は、ESP32モジュールを再起動してください。
動作確認
Javascriptのプログラムを書き込んだり、デバイスを操作したりするための簡単なWebコンソールを用意しています。ブラウザで、以下のようにESP32モジュールのIPアドレスを開いてみてください。HTTPSではなく、HTTPです。
http://【ESP32のIPアドレス】
そうすると、以下のURLにリダイレクトされます。
https://poruruba.github.io/QuickJS_ESP32_IoT_Device_M5Unified/QuickJS_ESP32_Firmware/data/html/?base_url=http%3A%2F%2F【ESP32のIPアドレス】
本体は以下にあります。
デバイスの操作タブから、「IPAdress & MacAddress」ボタンを押下します。
ブラウザから「ローカルネットワーク上の他のデバイスにアクセスするか」と聞かれますので、許可します。これで準備完了です。
それではさっそく、Javascriptコードを書いてみましょう。
console.log("hello world");
アップロード ボタン押下します。Serial Monitorに以下のように表示されます。
save_jscode
[now restarting]
[executing]
hello world
期待通りですね。
効率的なJavascriptコード開発環境の構築
Web管理コンソールからもJavascsriptコードを作成できますが、もっと効率的にするため、VSCodeで作成できるようにします。
VSCodeからJavascriptを作成・ESP32にアップロードするために、以下のVSCodeの拡張機能を用意しました。
・QuickJs_ESP32_Console
(参考)
・https://marketplace.visualstudio.com/items?itemName=poruruba.QuickJS-ESP32-Console
・https://github.com/poruruba/QuickJS-ESP32-Console
発行元を信頼してインストールしてください。
ESP32モジュールのIPアドレスが促されますので入力します。
以降、接続したいESP32モジュールを変えたい場合は、「QuickJS_ESP32」をクリックしてSettingをクリックし、IPアドレスを入力しなおす
testフォルダに新規にファイルを作成します。
test.js
以下例です。
setInterval(() =>{
console.log("hello world");
}, 1000);
左下のQuickJS_ESP32と表示されているところの近くの下矢印ボタンを押下します。そうすると、先ほど入力したIPアドレスのESP32モジュールに、今書いたJavascriptコードがアップロードされます。
Serial Monitorに以下のように表示されます。
save_jscode
[now restarting]
[executing]
hello world
hello world
hello world
ESP32からのコンソール出力を集約する
通常はESP32のSerialを使ってコンソールに出力されます。
それをSyslogとして出力することで、COMポートでの接続が不要となるのと、複数のESP32モジュールの出力をSyslogServerに集約することができます。
簡易なSyslogServer兼ログビューアを用意しました。
その前にNode.jsが必要です。
Node.jsをインストールします。
Ver.20以降が必要です。
以下のサイトから、SyslogServer兼ログビューアをダウンロードします。ZIPでダウンロードし、適当なフォルダに展開します。
まずは、server.jsを変更します。以下例です。
{
"messageServer": {
"port": 6689,
"host": "localhost"
},
"httpServer": {
"port": 6688,
"host": "localhost"
},
"debug": false
}
.envを作成します。以下例です。
PORT=20514
LOGIO_USER=test
LOGIO_PASSWORD=Abcdefg
パスワードは秘匿な値にします。
もし別のSyslogサーバが立ち上がっていないのであれば、PORTは514でもよいです。
Webコンソールから、syslog host/portの設定とenableSyslogConsoleをOnに設定します。
syslog hostは、先ほどのSyslogServer兼ログビューアを立ち上げたPCのホスト名です。
enableSyslogConsoleは、console.logにおいてSerialへの出力と同時にSyslogも送信するための設定です。
.envに記載したユーザ名とパスワードを入力します。
こんな感じで、コンソールに出力されているのと同じ内容がSyslogとして受信できています。
Lチカサンプル
今度は恒例のLチカサンプルです。
testフォルダに新規にファイルを作成しまs。
test_led.js
以下例です。M5StickCは、GPIO10にLEDが接続されています。
import * as gpio from "Gpio";
import * as input from "Input";
const PIN_LED = 10;
var led = false;
function setup(){
gpio.pinMode(PIN_LED, gpio.OUTPUT);
gpio.digitalWrite(PIN_LED, led ? gpio.LOW : gpio.HIGH);
console.log('setup finished');
}
function loop(){
esp32.update();
if( input.wasPressed(input.BUTTON_A) ){
console.log('Pressed');
led = !led;
gpio.digitalWrite(PIN_LED, led ? gpio.LOW : gpio.HIGH);
}
}
ボタンを押すたびに、LEDが点灯・消灯を繰り返します。
AIを使ったJavascript開発支援
Google NotebookLmを使って賢くJavascriptコーディングします。
入力となるのは、以下のサイトで購入したPDFです。
使い方やJavascript APIリファレンスをまとめてあります。
以下のURLを開きます。
NotebookLMを試す をクリックします。
ログインすると、NotebookLmを使えるようになります。
後でアクセスしやすいように、PWAとしてインストールします。
右上のPCマークをクリックすると、インストールできます。
ノートブックを作成し、購入したPDFを入力します。
さっそくお願いしてみましょう。
「LCDに時刻を表示するサンプルを作って」と入力しました。すると、こんな感じで回答が返ってきます。
M5StackのLCDに現在時刻を表示するサンプルプログラムを紹介します。この機能を実現するには、表示用のLcdライブラリと、時刻取得用のRtcライブラリを使用します。
LCDに時刻を表示するサンプルコード
以下のコードを「main.js」として書き込むことで、10秒間隔でLCDの時刻表示を更新できます。
import * as lcd from "Lcd";
import * as rtc from "Rtc";
function setup(){
// フォントの設定(12または32ドットが標準で利用可能)
lcd.setFont(32);
// 表示倍率の設定
lcd.setTextSize(2);
}
async function sleep(msec){
return new Promise(resolve => setTimeout(resolve, msec) );
}
async function loop(){
// RTCから現在時刻を取得
var time = rtc.getTime();
// 画面を黒で塗りつぶしてリセット
lcd.fillScreen(0x000000);
// 文字の開始位置を指定
lcd.setCursor(0, 0);
// 時:分を2桁形式で表示
lcd.print(fixed(time.Hours) + ":" + fixed(time.Minutes) );
// 10秒間待機
await sleep(10000);
}
// 数値を2桁の文字列に固定する補助関数
function fixed(d){
return ("00" + d).slice(-2);
}
プログラムのポイント
• ライブラリのインポート: Lcdライブラリで描画を、Rtcライブラリで日付と時刻の取得を制御します。
• 自動時刻同期: 日付と時刻は、WiFi起動時に自動的にNTPサーバ("ntp.nict.jp"など)と同期される仕組みになっています。
• フォントの選択: 本ファームウェアには、メモリ制限の関係で12ドットと32ドットの2種類のフォントが埋め込まれています。setTextSize()を使うことで、これらのフォントをベースにサイズを細かく調整することも可能です。
• 非同期処理: sleep関数をawaitで呼び出すことで、CPUの実行権を他のタスクに渡しながら並行処理を行うことができます。
このサンプルは、M5Stick-C、M5Stick-C Plus、M5Stack Core2など、LCDを搭載したモデルでの利用が推奨されています。
ちゃんとPDFの内容を見て完璧に回答してくれました。
もうちょっと複雑な例です。
「M5UnitのEnvⅣをM5StickCに接続して、毎秒コンソールに温度・湿度を出力して」と聞いてみました。
M5StickCにEnvⅣユニット(SHT40センサ搭載)を接続し、毎秒コンソールに温度と湿度を出力するためのJavaScriptサンプルプログラムを紹介します。
このプログラムでは、I2C通信を制御するWireライブラリと、温湿度センサを扱うEnvライブラリを使用します。
サンプルプログラム (main.js)
import * as env from "Env";
import * as wire from "Wire";
// M5StickCのGrove端子(I2C)のピン番号設定
const SDA = 32;
const SCL = 33;
function setup() {
// I2Cバスの初期化
wire.begin(SDA, SCL);
// Env IV (SHT40) ユニットの初期化
env.sht40_begin();
console.log("Env IV Logging Start...");
}
// 1秒(1000ミリ秒)ごとに実行
setInterval(() => {
// SHT40から温度と湿度を取得
var data = env.sht40_get();
if (data) {
// コンソール(UART)に出力
console.log("Temp: " + data.temperature.toFixed(2) + " °C, " +
"Humi: " + data.humidity.toFixed(2) + " %");
} else {
console.log("Failed to read from Env IV");
}
}, 1000);
プログラムの解説
• ライブラリのインポート: EnvライブラリはSHT40などの温湿度センサを簡単に扱うために使用し、WireライブラリはI2C通信の初期化に必要です。
• ピン設定: M5StickCのGrove端子は、SDAが32番、SCLが33番に割り当てられています。
• センサの取得: env.sht40_get() メソッドを呼び出すことで、温度 (temperature) と湿度 (humidity) を含むオブジェクトを取得できます。
• 定期実行: setInterval(func, 1000) を使用することで、Arduino方式のloop()を使わずに、Javascriptの標準的な機能で1秒ごとの処理を簡潔に記述できます。
• コンソール出力: console.log() で出力された内容は、USB接続されたPCのシリアルモニタ(UART)で確認できます。
実行のヒント
もし非常に大きなサイズのプログラムを書き込む際にエラーが出る場合は、main_config.h のバッファサイズ設定を確認してください。また、より高度なグラフ表示などを行いたい場合は、Ambientなどの外部サービスと連携させることも可能です。
以上











