ESP32をJavascriptでLチカする：HTTP でJsonをPostする



前回の投稿「[ESP32をJavascriptでLチカする](https://qiita.com/poruruba/items/96db9d20e7e4ced7635d)」に引き続き、ESP32でJavascriptを動かします。
今回は、HTTPでJsonをPOSTします。

ESP32に以下の人感センサ PIR Hatを接続して、検出したらBeebotteにアップします。

PIR Hat

https://www.switch-science.com/catalog/5756/

Beebotte

https://beebotte.com/

※Beebotteは、HTTPやMQTTでデータを集約して、グラフ化してくれるサービスです。

ソースコードもろもろは、前回同様以下に上げてあります。
随時、修正、拡張しています。

poruruba/QuickJS_ESP32

https://github.com/poruruba/QuickJS_ESP32

#HTTP通信

2つの通信方法を用意しました。

①HTTPSで中継サーバを介してPost/Get接続

ESP32でHTTPS通信するためには少々骨が折れるため、Node.jsを使った中継サーバをイントラネットに配置し、中継サーバまではHTTP、中継サーバから先をHTTPS通信するようにします。

httpモジュールとして用意しています。

```js
import * as http from "http";
```

中継サーバは、GitHubからダウンロードしたファイルのnode.jsフォルダを立ち上げます。

また、main.cppの以下の部分を、立ち上げた中継サーバのURLに変更します。

```js
const char *HTTPPROXY_URL = "http://【中継サーバのURL】/httpproxy-call";
```

ちなみに、中継サーバは以下のような実装になってます。

```js:node.js/api/controllers/http-proxy/index.js
'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モジュールに用意しています。

```js
import * as utils from "utils";
```

①、②いずれも、パラメータの指定方法は同じです。

Post接続、JSON戻り

```js
http.postJson(url, params, headers);
utils.httpPostJson(url, params, headers);
```

・url：接続先URL
・params(option)：bodyにJSONとして渡す引数
・headers(options)：headerに指定する引数

```js:例
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戻り

```js
http.postText(url, params, headers);
utils.httpPostText(url, params, headers);
```

Get接続、JSON戻り

```js
http.getJson(url, params, headers);
```

・url：接続先URL
・params(option)：queryStringに指定する引数
・headers(options)：headerに指定する引数

Get接続、TEXT戻り

```js
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につながれています。

```js
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));
	}
}
```

#終わりに

かなり形になってきました。
異常系を整備しようかな。。。

以上