前回の投稿「ESP32をJavascriptでLチカする」に引き続き、ESP32でJavascriptを動かします。
今回は、HTTPでJsonをPOSTします。
ESP32に以下の人感センサ PIR Hatを接続して、検出したらBeebotteにアップします。
PIR Hat
Beebotte
※Beebotteは、HTTPやMQTTでデータを集約して、グラフ化してくれるサービスです。
ソースコードもろもろは、前回同様以下に上げてあります。
随時、修正、拡張しています。
poruruba/QuickJS_ESP32
#HTTP通信
2つの通信方法を用意しました。
①HTTPSで中継サーバを介してPost/Get接続
ESP32でHTTPS通信するためには少々骨が折れるため、Node.jsを使った中継サーバをイントラネットに配置し、中継サーバまではHTTP、中継サーバから先をHTTPS通信するようにします。
httpモジュールとして用意しています。
import * as http from "http";
中継サーバは、GitHubからダウンロードしたファイルのnode.jsフォルダを立ち上げます。
また、main.cppの以下の部分を、立ち上げた中継サーバのURLに変更します。
const char *HTTPPROXY_URL = "http://【中継サーバのURL】/httpproxy-call";
ちなみに、中継サーバは以下のような実装になってます。
'use strict';
const HELPER_BASE = process.env.HELPER_BASE || '../../helpers/';
const Response = require(HELPER_BASE + 'response');
const TextResponse = require(HELPER_BASE + 'textresponse');
const BinResponse = require(HELPER_BASE + 'binresponse');
const { URLSearchParams } = require('url');
const fetch = require('node-fetch');
const Headers = fetch.Headers;
exports.handler = async (event, context, callback) => {
switch(event.path){
case '/httpproxy-call':{
var body = JSON.parse(event.body);
console.log(body.method, body.url, body.params, body.headers, body.response_type, body.method);
var result;
switch(body.method){
case 'POST':{
result = await do_post(body.url, body.params, body.headers, body.response_type);
break;
}
case 'GET':{
result = await do_get(body.url, body.params, body.headers, body.response_type);
break;
}
default:
throw "unknown method";
}
if (body.response_type == 'TEXT')
return new TextResponse("text/plain", result);
else if (body.response_type == 'BINARY')
return new BinResponse("application/octet-stream", Buffer.from(result));
else
return new Response(result);
}
}
};
function do_post(url, body, header, response_type = 'JSON') {
const headers = new Headers(header);
headers.append("Content-Type", "application/json");
console.log(headers);
console.log(body);
console.log(url);
return fetch(url, {
method: 'POST',
body: JSON.stringify(body),
headers: headers
})
.then((response) => {
if (!response.ok)
throw 'status is not 200';
if (response_type == 'TEXT')
return response.text();
else if (response_type == 'BINARY')
return response.arrayBuffer();
else
return response.json();
});
}
function do_get(url, qs, header, response_type = 'JSON') {
const params = new URLSearchParams(qs);
const headers = new Headers(header);
var append_params = params.toString();
if (append_params ){
url += (url.indexOf('?') >= 0) ? "&" : "?";
url += append_params;
}
console.log(url);
return fetch(url, {
method: 'GET',
headers: headers
})
.then((response) => {
if (!response.ok)
throw 'status is not 200';
if (response_type == 'TEXT')
return response.text();
else if (response_type == 'BINARY')
return response.arrayBuffer();
else
return response.json();
});
}
②HTTPで直接Post/Get接続
接続先サーバが、HTTPであれば、直接接続できるようにしています。
utilsモジュールに用意しています。
import * as utils from "utils";
①、②いずれも、パラメータの指定方法は同じです。
Post接続、JSON戻り
http.postJson(url, params, headers);
utils.httpPostJson(url, params, headers);
・url:接続先URL
・params(option):bodyにJSONとして渡す引数
・headers(options):headerに指定する引数
var response = http.postJson("http://api.beebotte.com/v1/data/write/pir", { records: [{ "resource": "pir", data: (pir != 0) }]}, { "X-Auth-Token": X_AUTH_TOKEN } )
Post接続、TEXT戻り
http.postText(url, params, headers);
utils.httpPostText(url, params, headers);
Get接続、JSON戻り
http.getJson(url, params, headers);
・url:接続先URL
・params(option):queryStringに指定する引数
・headers(options):headerに指定する引数
Get接続、TEXT戻り
http.getText(url, params, headers);
utils.httpGetText(url, params, headers);
ほかに有用なAPIとして以下があります。
・utils.urlencode(str)
・utils.base64Encode(str)
・utils.base64Decode(b64)
また、当然以下もあります。
・JSON.parse(str)
・JSON.stringify(obj)
#Javascriptソース
PIR Hatは、GPIO_36につながれています。
import * as http from "http";
import * as input from "input";
import * as utils from "utils";
import * as gpio from "gpio";
const X_AUTH_TOKEN = "【Beabotteチャネルトークン】 ";
async function setup()
{
var ipaddress = esp32.getIpAddress();
console.log("ipaddress=" + ((ipaddress >> 24) & 0xff) + "." + ((ipaddress >> 16) & 0xff) + "." + ((ipaddress >> 8) & 0xff) + "." + (ipaddress & 0xff));
gpio.pinMode(36, gpio.INPUT);
}
var pir = -1;
async function loop()
{
esp32.update();
if( input.isPressed(input.BUTTON_B) ){
esp32.restart();
}
var t = gpio.digitalRead(36);
if( t != pir ){
pir = t;
console.log("PIR=" + pir);
// var result2 = http.postJson("http://api.beebotte.com/v1/data/write/pir", { records: [{ "resource": "pir", data: (pir != 0) }]}, { "X-Auth-Token": X_AUTH_TOKEN } );
var result2 = utils.httpPostJson("http://api.beebotte.com/v1/data/write/pir", { records: [{ "resource": "pir", data: (pir != 0) }]}, { "X-Auth-Token": X_AUTH_TOKEN } );
console.log(JSON.stringify(result2));
}
}
#終わりに
かなり形になってきました。
異常系を整備しようかな。。。
以上