いっぺーちゃんの いろいろやってみよ~

Espruino on ESP32 で Twitter(その3)

Espruino on ESP32 でぽちっとな - いっぺーちゃんの いろいろやってみよ~Espruino on ESP32 で Twitter(その2) - いっぺーちゃんの いろいろやってみよ~ を組み合わせてTweetボタンを作ってみましょう。

大雑把に言うと Espruino on ESP32 でぽちっとな - いっぺーちゃんの いろいろやってみよ~ でスイッチのステータスを表示していた部分をtweet処理に変えるだけです。

 

 以下、そのソースです。

consumer_key    = '取得した Consumer key';
consumer_secret = '取得した Consumer secret';
access_token    = '取得した Access Token';
access_secret   = '取得した Access Token Secret';

if (typeof(ESP32) ==='function') {
    // platform is espruino on ESP32
    // Wi-Fi アクセスポイントへの接続
    var wifi = require('MyWifi');
    // set time zone  fixed to 'JST'
    E.setTimeZone(9);

    // Initialize for switch
    var sw1  = Pin(21);
    sw1.mode("input" );
} else {
    throw "not ESP32";
}

// モジュール読み込み
var tiny_twitter = require("tiny_twitter");

// 初期化
var tw = new tiny_twitter(consumer_key, consumer_secret, access_token, access_secret, true);

tw.on("connect", function() {
    console.log("%%%% CONNECTED %%%%");
});
tw.on("response_header", function(data, code, msg) {
    if (code != 200) {
        console.log("%%%% RESPONSE ERROR!!!   " + code.toString() + " : " + msg);
    }
    console.log("%%%% RESPONSE_HEADER %%%%\n" + data + "\n%%%%%%%%%%%%%%%%%%%%%%%%%");
});
tw.on("data", function(data) {
    console.log("%%%% DATA %%%%");
    console.log(JSON.stringify(data,  null, '\t'));
    // エラー時はdata.errors.code data.errors.message に情報が入る
    console.log("%%%%%%%%%%%%%%%%%%%%%%%%%");
});
tw.on("end", function() {
    console.log("%%%% END %%%%");
});

function button_down(e) {
    var msg;
    var date = new Date();
    h = date.getHours();
    if (h >=4 && h < 12) {
        msg = 'Good morning, everyone!!';
    } else if (h >=12 && h < 17){
        msg = 'Good afternoon, everyone!!';
    } else if (h >=17 && h < 21){
        msg = 'Good evening, everyone!!';
    } else {
        msg = 'Good night, everyone!!';
    }
    tw.tweet(msg, {"trim_user":true,"include_entities":false});
}

setWatch(button_down, sw1, {repeat:true, edge:"falling", debounce:100});

 

 このプログラムを実行すると、Pin21に接続されたスイッチを押すたびにその時刻に応じた挨拶がtweetされます。

スイッチを押してからtweet完了まで4~5秒程度かかります。

tweet完了前に再度スイッチを押したときの動作は未確認です(メモリ不足で落ちそうな気がするけど)。

tweet開始~endイベントまで次のtweet要求をブロックすれば良いのかな?
ま、ぽんぽん押さなければ良いだけなので、そのままにしておこう。

 

 以下処理の説明です。

ESP32の場合はWi-Fiに接続してタイムゾーンを設定し、スイッチ用の端子を初期化します。
それ以外の環境ではスイッチが使えないのでエラー終了します。

if (typeof(ESP32) ==='function') {
    // platform is espruino on ESP32
    // Wi-Fi アクセスポイントへの接続
    var wifi = require('MyWifi');
    // set time zone  fixed to 'JST'
    E.setTimeZone(9);

    // Initialize for switch
    var sw1  = Pin(21);
    sw1.mode("input" );
} else {
    throw "not ESP32";
}

 

モジュール読み込み~イベントハンドラは前回と同じです。

 

スイッチが押されたときのハンドラです。
現在時刻を取得し、時刻に応じた挨拶をtweetしています。
時刻の区切りには異論もあるでしょうけど。。。
日本語が使えれば、もうちょっと気の利いたtweetができるんですが。。。。

function button_down(e) {
    var msg;
    var date = new Date();
    h = date.getHours();
    if (h >=4 && h < 12) {
        msg = 'Good morning, everyone!!';
    } else if (h >=12 && h < 17){
        msg = 'Good afternoon, everyone!!';
    } else if (h >=17 && h < 21){
        msg = 'Good evening, everyone!!';
    } else {
        msg = 'Good night, everyone!!';
    }
    tw.tweet(msg, {"trim_user":true,"include_entities":false});
}

 

スイッチにハンドラを割り当てます。
今回はスイッチが押されたときだけ処理したいので、「edge:"falling"」としています。

setWatch(button_down, sw1, {repeat:true, edge:"falling", debounce:100});

 

 これで、わざわざESP32からtweetする価値が出てきたかな?

温度センサの値を読んで、定期的(10分に1回とか)にtweetする、なんて使い方もできそうですね。

 


 

次回に続く。。。

 

Espruino on ESP32 で Twitter(その2)

以下がtwitterにアクセスするためのモジュールです。
さらっと書いてますが、血と汗と涙の結晶です。

====2017/08/03追記 ここから====

 Espruino on ESP32 で Twitter にも日本語を - いっぺーちゃんの いろいろやってみよ~で新しいバージョンを公開しました

====2017/08/03追記 ここまで====

 

====2017/08/03削除 ここから====

tiny_twitter.zip

zipファイルを解凍してオンボードストレージの /node_modules ディレクトリに格納してください。

 

====2017/07/30追記 ここから====

 Node.jsで実行するとエラーになりました。
Espruino用HMACSHA1モジュールの仕様を変更したのを忘れてました(^^ゞ
以下の修正で使用できるようになります。

--- tiny_twitter.js.old2	2017-07-30 11:37:47.540869675 +0900
+++ tiny_twitter.js	2017-07-30 11:40:36.641894478 +0900
@@ -113,7 +113,11 @@
     // make signature
     var signing_key = RFC3986_encode(this.consumer_secret) + '&' + RFC3986_encode(this.access_secret);
     // console.log("key:" + signing_key);
-    var sig = hmacsha1.b64(signing_key, sigbase_str);
+    if (platform == 'node') {
+        var sig = hmacsha1(signing_key, sigbase_str);
+    } else {
+        var sig = hmacsha1.b64(signing_key, sigbase_str);
+    }
     this.degug_print('sig: ' + sig);
 
     var oauth_head  = '        OAuth oauth_consumer_key="'    + RFC3986_encode(this.consumer_key) + '",\n' 

====2017/07/30追記 ここまで====

====2017/08/03削除 ここまで====

 

また、twitterにアクセスするにはアカウントのほか、4つのキーを取得する必要があります。
(Consumer key、Consumer secret、Access Token、Access Token Secret)
自分で手順書くのが面倒なので、先人の成果をパクり、、いやいや参照させて頂くことにします。
ここの手順に従ってキーを取得してください。

TwitterのAPIを使用するために必要なキーを取得する手順 - Hello API

これらのキー(特にsecretと付く方)はパスワードみたいなものなので、取り扱いには十分注意しましょう。
普段使っているアカウントでいきなり変なtweetをすると、フォロワーがびっくりするので、別のアカウントを用意するのが良いかもしれません。

 

 

まずはtweetしてみます。
以下のプログラムを実行してみましょう。

consumer_key    = '取得した Consumer key';
consumer_secret = '取得した Consumer secret';
access_token    = '取得した Access Token';
access_secret   = '取得した Access Token Secret';

if (typeof(ESP32) ==='function') {
    // platform is espruino on ESP32
    // Wi-Fi アクセスポイントへの接続
    var wifi = require('MyWifi');
}
if (typeof(E) ==='function') {
    // platform is espruino
    // set time zone  fixed to 'JST'
    E.setTimeZone(9);
}

// モジュール読み込み
var tiny_twitter = require("tiny_twitter");

// 初期化
var tw = new tiny_twitter(consumer_key, consumer_secret, access_token, access_secret, true);

tw.on("connect", function() {
    console.log("%%%% CONNECTED %%%%");
});
tw.on("response_header", function(data, code, msg) {
    if (code != 200) {
        console.log("%%%% RESPONSE ERROR!!!   " + code.toString() + " : " + msg);
    }
    console.log("%%%% RESPONSE_HEADER %%%%\n" + data + "\n%%%%%%%%%%%%%%%%%%%%%%%%%");
});
tw.on("data", function(data) {
    console.log("%%%% DATA %%%%");
    console.log(JSON.stringify(data,  null, '\t'));
    // エラー時はdata.errors.code data.errors.message に情報が入る
    console.log("%%%%%%%%%%%%%%%%%%%%%%%%%");
});
tw.on("end", function() {
    console.log("%%%% END %%%%");
});

// tweetする
var message = "test tweet";
var param   = {"trim_user":true,"include_entities":false};
tw.tweet(message, param);

 

成功すれば、「test tweet《改行》(Thu Jul 24 2017 12:29:46 GMT+0900)」のようにtweetされるはずです。

もちろん、ESP32がインターネットにアクセスできるWi-Fiルータにつながってなければ、失敗します。
もし、時刻があってないと(そんなに厳密でなくてもかまいません。何時間とか何日レベルでずれている場合)は、エラーになりますので、時刻合わせを行っておいてください。
tweetするメッセージに時刻を追加するようにしてあります。これは同じメッセージを短い時間内にtweetすると「それ、さっき呟いたやんけ~」とエラーになるため、それを回避するためにtweet処理の中で時刻を付加しています。

調子に乗って何回もtweetしてると、アカウントをロックされることがありますので、注意してください。

 

以下処理の説明です。

ESP32の場合はWi-Fiに接続します。
接続状態を維持しているならここはなくても構いません。前に書いたMyWifi.jsを使用しています。
ESP32上のEspruinoだと、「ESP32」というbuilt-in moduleが存在するので、それで判断しています。

if (typeof(ESP32) ==='function') {
    // platform is espruino on ESP32
    // Wi-Fi アクセスポイントへの接続
    var wifi = require('MyWifi');
}

 

 Espruinoの場合はタイムゾーンを設定します。
Espruinoだと、「E」というbuilt-in moduleが存在するので、それで判断しています。
ここはLinux版Espruinoの場合も実行されます。
Espruinoでしか使わないならいきなりタイムゾーン設定でも構いません。

if (typeof(E) ==='function') {
    // platform is espruino
    // set time zone  fixed to 'JST'
    E.setTimeZone(9);
}

 なぜ、こんなコードを入れているのか?
いい質問ですねぇ~。
このプログラム、実はnode.jsでも動くんです。
デバッグしやすいので、最初node.jsでデバッグしてた名残です。
せっかくなので、残しておきました。
node.js⇔Linux版Espruinoを行き来しながらデバッグして、最後にESP32で動作確認、な感じでデバッグしてました

 

 モジュールを読み込みます。
これをしないと始まりません。

var tiny_twitter = require("tiny_twitter");

 

 tiny_twitterインスタンスを生成します。
パラメータの最初の4つは↑で取得したキーです。最後のパラメータはモジュール内部のdegug_print (あ、typoだ。めんどいからそのままで)を表示するか否かを指定しています。
色々表示されてうっとうしい場合はfalseにしてください。

var tw = new tiny_twitter(consumer_key, consumer_secret, access_token, access_secret, true);

 

 サーバに接続されたときのイベントハンドラです。
特に処理はないので、接続した旨表示しているだけです。

tw.on("connect", function() {
    console.log("%%%% CONNECTED %%%%");
});

 

 受信データのうち、ヘッダ部分を受信したときのイベントハンドラです。
codeが200以外のときは、接続エラーなので、エラーと表示しています。
また、デバッグ用にヘッダ全体を表示しています。

tw.on("response_header", function(data, code, msg) {
    if (code != 200) {
        console.log("%%%% RESPONSE ERROR!!!   " + code.toString() + " : " + msg);
    }
    console.log("%%%% RESPONSE_HEADER %%%%\n" + data + "\n%%%%%%%%%%%%%%%%%%%%%%%%%");
});

 

 受信データのうち、データの中身を受け取ったときのイベントハンドラです。
データはJSON形式で送られてくるので、JSON.stringifyで文字列化して表示しています。

tw.on("data", function(data) {
    console.log("%%%% DATA %%%%");
    console.log(JSON.stringify(data,  null, '\t'));
    // エラー時はdata.errors.code data.errors.message に情報が入る
    console.log("%%%%%%%%%%%%%%%%%%%%%%%%%");
});

 

 サーバとの接続が切断されたときのイベントハンドラです。
特に処理はないので、切断した旨表示しているだけです。

tw.on("end", function() {
    console.log("%%%% END %%%%");
});

 

 実際にtweetする部分です。
paramは APIリファレンス を参照してください。指定しても動くかどうかわかりませんが。。。
この設定だと、受信するデータがちょっと小さくなります。

var message = "test tweet";
var param   = {"trim_user":true,"include_entities":false};
tw.tweet(message, param);

 

 今回はここまで。

 

次回に続く。。。

Espruino on ESP32 で Twitter(その1)

Espruinoで何か面白いことができないかなぁ~?とネット上を徘徊していると、

TI DSP ソフトウェア設計のファームロジックス | MicroPython + ESP8266 で、ネイティブ tweet bot を作る

というページを見つけました。

ESP8266上のMicropythonでTwitterに直接ポストする、というものらしいです。RasPiのようなLinuxマシンからアクセスしたり、Linuxマシン上のブリッジを介してにTwitterにアクセスするような記事は見かけるのですが、非Linuxマシンから直接というのはあまり見ません。

参照先では、ESP8266はRAMが少ないのでちょっとトリッキーな処理を行っていますが、ESP32ならRAMも豊富なのでらくしょーと思っていたら、そうもいきませんでした。

以下奮闘の結果です。

 

まずはEspruinoの改造です。

今回は修正量が多いのでパッチファイルを用意しました。このパッチファイルには前回までの修正内容も含まれています。

Espruino_patch1.zip

使用するには、 zipファイルを解凍してpatchコマンドでパッチをあててください。

 

以下に変更内容の抜粋を示します。

 

HMACSHA1のコンフィギュレーションフラグに関する変更です。

diff --git a/Makefile b/Makefile
~中略~
@@ -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
~中略~

 

Heapメモリサイズの変更とHMACSHA1のコンフィギュレーションフラグに関する変更です。
何回かtweetする処理を行うとHeapメモリが不足するようなので、増やしておきます。

diff --git a/boards/ESP32.py b/boards/ESP32.py
~中略~
@@ -19,7 +19,7 @@ info = {
  'espruino_page_link'       : 'ESP32',
  'default_console'          : "EV_SERIAL1",
  'default_console_baudrate' : "115200",
- 'variables'                : 5000,
+ 'variables'                : 8000,
  'binary_name'              : 'espruino_%v_esp32.bin',
  'build' : {
    'optimizeflags' : '-Og',
@@ -28,6 +28,7 @@ info = {
      'NET',
      'GRAPHICS',
      'CRYPTO',
+     'HMACSHA1',
      'TLS',
      'TELNET',
      'NEOPIXEL',

 

HMACSHA1の処理本体とHMACSHA1のstaticmethodの定義です。
HMACSHA1の処理をjavascriptで記述すると、実行にかなりの時間がかかります。
そこで、nativeコードでEspruinoにbuil-in moduleとして組み込んで、処理時間の短縮を図ります。
内容は長いのでここでは省略します。上記のパッチファイルを参照してください。

「/* JSON {」から「}*/」で囲まれた部分はCプログラムからはコメントですが、Espruinoのbuilt-in moduleを作るのに必要な情報なので、省略してはいけません。

diff --git a/libs/crypto/jswrap_crypto.c b/libs/crypto/jswrap_crypto.c
~内容省略~
diff --git a/libs/crypto/jswrap_crypto.h b/libs/crypto/jswrap_crypto.h
~内容省略~

 また、Linux版Espruinoでも同様のプログラムが実行できるように修正を入れてあります。その他のプラットフォームはいじっていません。

 

次回に続く。。。

Espruino on ESP32 起動時に時刻合わせを行う

ESP32のRTCはバッテリバックアップされていないようなので、電源OFFで時刻を忘れてしまうようです。
あ、以前(Espruino on ESP32 でぽちっとな - いっぺーちゃんの いろいろやってみよ~) gettimeofday使わないようにしたので、RTCがバッテリバックアップされていてもダメか。

時刻合わせを行うには、SNTPでタイムサーバから時刻を取得するのが一般的ですが、EspruinoはUDP通信をサポートしていない(!!!?)ので、SNTP処理をJavascriptで記述できません。

次の記事では、時刻合わせをしておく必要があるので、とりあえず、起動時にSNTPを実行して時刻合わせを行うような処理を追加しておきます。

 

targets/esp32/main.c に以下の修正を加えます。

diff --git a/targets/esp32/main.c b/targets/esp32/main.c
index 00a9dcf..024ad6b 100644
--- a/targets/esp32/main.c
+++ b/targets/esp32/main.c
@@ -41,6 +41,47 @@ static void timerTask(void *data) {
   }
 }
 
+// ADD start [IPPEI]
+#include "apps/sntp/sntp.h"
+static void sntpTask(void *data) {
+    int t_flag = 0;
+    time_t now = 0;
+    int retry = 0;
+    const int retry_count = 10;
+
+    jsiConsolePrintf("SNTP_task: Initializing SNTP\n");
+    sntp_setoperatingmode(SNTP_OPMODE_POLL);
+    sntp_setservername(0, "pool.ntp.org");
+    sntp_init();
+    // wait for time to be set
+    retry = 0;
+    while(++retry < retry_count) {
+        // jsiConsolePrintf("SNTP_task: Waiting for system time to be set... (%d/%d)\n", retry, retry_count);
+        vTaskDelay(2000 / portTICK_PERIOD_MS);
+        time(&now);
+        //printf("SNTP_task: time :%ld\n", now);
+        if (now >= 946684800) {
+            // 2000/1/1 00:00:00 ~
+            t_flag = 1;
+            // set system time
+            JsSysTime stime = (JsSysTime)now * (JsSysTime)(1000*1000);
+            jsiLastIdleTime = stime;
+            jshSetSystemTime(stime);
+            break;
+        }
+    }
+    if (!t_flag) {
+        jsiConsolePrintf("SNTP_task: **** system time is NOT set ****\n>");
+    }
+    else {
+        jsiConsolePrintf("SNTP_task: system time is set\n>");
+    }
+    // jsiConsolePrintf("SNTP_task: stop SNTP\n");
+    sntp_stop();
+    vTaskDelete(NULL);
+}
+// ADD end [IPPEI]
+
 
 static void espruinoTask(void *data) {
   PWMInit();
@@ -91,10 +132,12 @@ int app_main(void)
   task_init(espruinoTask,"EspruinoTask",20000,5,0);
   task_init(uartTask,"ConsoleTask",2000,20,0);
   task_init(timerTask,"TimerTask",2048,19,0);
+  task_init(sntpTask,"sntpTask",2048,19,0);
 #else
   xTaskCreatePinnedToCore(&espruinoTask, "espruinoTask", 20000, NULL, 5, NULL, 0);
   xTaskCreatePinnedToCore(&uartTask,"uartTask",2000,NULL,20,NULL,0);
   xTaskCreatePinnedToCore(&timerTask,"timerTask",2048,NULL,19,NULL,0);
+  xTaskCreatePinnedToCore(&sntpTask,"sntpTask",2048,NULL,19,NULL,0);
 #endif
   return 0;
 }

 

処理としては、起動時に sntpTask を起動し、その中でSNTP処理を起動、時刻が設定されるのを待ちます。
時刻が設定されたら(2000年1月1日 00:00:00 以降の時刻を取得できたら設定済みとみなす)、システム時刻を設定し、SNTP処理を終了してタスクを終了します。
時刻が設定されていなければ、2秒待ってリトライ。retry_count回(あ、retry_count-1回か?サンプルプログラムから拾ってきたままだ(^^ゞ。。。その辺はあまり重要ではないので。)行ってもダメなら設定を諦めます。
使用するタイムサーバを変更したいときは、sntp_setservernameの第2パラメータを変更してください。
メインタスクで処理を行わず専用のタスクを起動しているのは、時刻設定待ちでシステム起動を中断しないためです。

無事時刻が設定されれば、コンソールに「SNTP_task: system time is set」と表示されます。

時刻を取得するには、

var date=new Date()

と実行すれば現在の時刻が取得できます。

時刻を年月日時分秒で確認するには以下のように実行します。

console.log(date.toString())
Sun Jul 23 2017 03:45:00 GMT+0000

。。。時刻が違うって?
デフォルトではグリニッジ標準時(GMT)です。
(UTCの方が正しいと思うけど、気にしない)

日本時間を表示するには、以下のようにtimezoneを設定します。

E.setTimeZone(9);

再度表示してみます。

console.log(date.toString())
Sun Jul 23 2017 12:45:00 GMT+0900

はい、できました。

 

Espruino on ESP32 モジュールインストーラ

モジュールを用意した後、逐一オンラインストレージのディスクイメージを作成して書き込むのは面倒なので、
node.jsのnpmのように、モジュールをサーバからインストールできるプログラムを用意します。
名前はなんでも良いのですが、mnpmとしました。
"mini-"npmというより、"もどき"npm かな?

 

以下のプログラムをオンラインストレージに /node_modules/mnpm.js というファイル名で保存しておきます。

SERVER_ADDRの設定は自分の環境に合わせて変更しておいてください。

 

/* NOTE:
    var mnpm = require("mnpm")後、
    mnpm("モジュール名")
   を実行する
*/

exports = function(moduleName) {
    var fs   = require("fs");
    var http = require("http");

    //var SERVER_ADDR = "http://www.espruino.com/modules/";
    var SERVER_ADDR = "http://192.168.XX.XX/modules/";              // サーバのアドレス
    var DIST_DIR    = "/node_modules";                              // インストール先

    var dist_file = DIST_DIR + '/' + moduleName + ".js";

    // 既にDIST_DIRが存在するか確認
    var st=fs.statSync(DIST_DIR);
    if (!st) {
        // 存在しなかったら作成する
        console.log("create directory: " + DIST_DIR);
        fs.mkdirSync(DIST_DIR);
        // 再度DIST_DIRが存在するか確認
        st=fs.statSync(DIST_DIR);
    }
    if (!st) {
        // 作成できなかった
        console.log(DIST_DIR + " is not exist!!");
        return;
    } else if (!st.dir) {
        // DIST_DIRがディレクトリでない
        console.log(DIST_DIR + " is not directory!!");
        return;
    } else {
        // DIST_DIRが存在する
        console.log(DIST_DIR + " check OK!!");
    }

    // 古いファイルを削除
    try {
        fs.unlinkSync(dist_file);
    } catch(e) {
        // エラーは無視
    }

    // サーバから取ってくる
    // File not foundでもエラーにならないから注意(そういう画面を受け取ってしまうので)
    http.get(SERVER_ADDR + moduleName + ".js", function(res) {
        res.on('data', function(data) { 
                // 受信したデータを追加書き込み
                fs.appendFileSync(dist_file, data);
        });
        res.on('close', function() { 
            console.log("DONE");
        });
    });
}

 

 以下のように実行すると、モジュールをインストールできます。

var mnpm = require("mnpm")
mnpm("モジュール名")

インストールが成功すると DONE と表示されます。

 

なお、一度読み込んだモジュールを再度mnpmで再インストールした場合は、
ESP32を一旦リセットするか、

Modules.removeCached("モジュール名")
または
Modules.removeAllCached()

 と実行して、モジュールキャッシュからデータを削除してから使用してください。 そうしないと、モジュールキャッシュに残った前回のモジュールが実行されてしまいます。

 

 

Espruino on ESP32 でADC

ESP32でアナログ電圧を取得するのにADC(Analog-Digital Converter) を使用できます。

ESP32のADCのアナログ入力の電圧範囲は0~VDD(3.3V)です(ESP8266とは異なります)。

ADC入力端子として、Pin36、Pin37、Pin38、Pin39、Pin32、Pin33、Pin34、Pin35 を指定できます。
(それ以外の端子は現状サポートされていないようです)

取得できる値は、(入力電圧)/(VDD電圧) の 0~1.0の実数値です。

 

アナログ入力値を取得するには以下のプログラムを実行します。

var ADC0 = Pin(36);         // 端子指定
var val = analogRead(ADC0);

 

アナログ電源を分離してないので、精度は悪いでしょうね。。。
分解能12bitもあるのに、もったいない。。。

 

Espruino on ESP32 でPWM & DAC

ESP32ではアナログ出力にPWM(Pulse Width Modulation)とDAC(Digital-Analog Converter)を使用できます。
PWM出力はDAC出力用端子を除く任意の出力端子に割り当てることができます。
DAC出力はPin25とPin26のみ指定できます。

 

PWM出力

PWMは合計8本使用できます。
指定できる周波数はデフォルト周波数(5000Hz)とその他の周波数3種類の合計4種類です。
ただし、その他の周波数は同じ周波数を設定しても使用した分カウントされます。
つまり、1000Hzを3つ指定すると、それ以上デフォルト周波数以外では使用できません。

出力値は0.0~1.0の数値でパルス周期のうちのHigh区間の比率で指定します。
1.0を指定すると全区間Highとなります。
1.0を超えた部分は無視されます。つまり、1.1を指定すると0.1を指定したとみなされます。

このPWM出力はハードウェアで生成されるので、ソフトウェアの動作に影響を受けません。

var pwm0 = Pin(19)          // 端子指定
pwm0.mode('af_output');     // ペリフェラル出力(なくても良いが念のため)

var val = 0.5;              // 出力値
var freq = 1000;            // PWM 周波数 単位Hz
// PWM出力
analogWrite(pwm0, val, {freq: freq});

以下のように周波数指定を省略 or 0を指定 すると、周波数はデフォルト周波数となります。

analogWrite(pwm0, val);
または
analogWrite(pwm0, val, {freq: 0});

デフォルト周波数は  Espruino/targets/esp32/jshardwarePWM.c の以下の部分を修正することで変更できます。
サーボモータを複数接続したい場合など、同一の周波数でたくさんPWMを使いたい場合はここを修正するのた良いでしょう。

#define PWMFreqDefault 5000

 

 

 

DAC出力

アナログ出力にPin25 or Pin26を指定すると、PWMではなく、DAC出力となります。
出力値は0.0 ~ 0.99 でVDDに対する比率で指定します。
PWMと異なり、1.0を指定すると"0V"になるので注意が必要です。
それ以上の1.0を超えた部分はPWM同様無視されます。つまり、1.1を指定すると0.1を指定したとみなされます。
DAC出力なので、周波数を指定しても当然無視されます。

var DAC0 = Pin(25);         // 端子指定
DAC0.mode('af_output');     // ペリフェラル出力(なくても良いが念のため)

var val = 0.5;              // 出力値
// DAC出力
analogWrite(DAC0, val);     // DACは周波数関係ないので設定しない

 

 

 

設定値の飽和処理を入れて欲しかったなぁ。。。