概要
前回に引き続き、写真データをクラウドに送信して保存するためのソフトウェアを構築する。
背景と目的
前回、ハードウェアの構築ができた。今回は、Unit-CAMのソフトウェアとクラウド側で写真データ受け取り部分を実装する。
詳細
重要部分のみ記載する。
1. 撮影装置
dekuo-03.hatenablog.jp
Unit-CAMで撮影するための基本的なソフトウェアは、arduino-esp32のWiFiCameraServerを基にした上記記事で実験済みなので、これを基本として構築する。
1.1 撮像部
撮像部については、上記記事のピン配置等も同様。カメラの細かい設定は以下とし、それ以外はarduino-esp32WiFiCameraServerのデフォルトとした。SVGA(800*600)は決して解像度が高いとは言えないのだが、PSRAMが載っていないため仕方ないのと今回の用途としては十分なのでとりあえず問題ではない。
config.pixel_format = PIXFORMAT_JPEG;
config.frame_size = FRAMESIZE_SVGA;
config.jpeg_quality = 10;
config.fb_count = 1;
1.2 送信部
今回は、ESP32側のメモリ使用量が少なくて済むようにjpgバイナリをそのまま送信したい。送信側ではContent-Typeヘッダにapplication/octet-streamを設定して送信する。
また、ESP32の送信用バッファのサイズの制約か、WiFiClientSecureオブジェクトのwriteメソッドで一度に大きなデータを書いてしまうとうまく動かないので、1kbyteに分割して書き込むという工夫をしている。
#include <WiFiClientSecure.h>
const int TIMEOUT_IN_MSEC = 5000;
void post_request(const char* host, const char* path, const uint8_t* payload, size_t payload_len, const char* machineId, char* respBody, unsigned long respBodyLen) {
char * ptr = (char *) malloc(sizeof(WiFiClientSecure));
WiFiClientSecure* client = new (ptr) WiFiClientSecure;
client->setInsecure();
if (!client->connect(host, 443)) {
client->stop();
return;
} else {
size_t contentLength = payload_len;
client->printf("POST %s HTTP/1.1\n", path);
client->printf("Host: %s\n", host);
client->println("Connection: close");
client->println("Content-Type: application/octet-stream");
client->printf("Content-Length: %d\n", contentLength);
client->println();
int m = 1024;
int i, j = 0;
uint8_t * p = (uint8_t *) malloc(m);
while (true) {
for (i = 0; i < m; i++) {
p[i] = payload[i + j * m];
if (i + j * m == contentLength - 1) break;
}
client->write(p, m);
client->flush();
if (i + j * m == contentLength - 1) break;
j++;
}
client->println();
Serial.printf("Waiting for reponse...");
unsigned long curtime = millis();
while (client->available() == 0) {
if (millis() - curtime > TIMEOUT_IN_MSEC) {
client->stop();
return;
}
}
unsigned long ci = 0;
bool started = false;
while (client->available()) {
char c = client->read();
if (c == '{') started = true;
if (!started) continue;
respBody[ci] = c;
ci++;
if (ci == respBodyLen) break;
}
respBody[ci] = '\0';
client->stop();
}
}
AWSの
を組み合わせる。
2.1 APIGateway
APIGatewayを介してLambdaがjpgバイナリを受け取るには、APIGateway側の設定において
が必要。具体的には、APIの設定は
となる。また、マッピングテンプレートでは、$input.bodyという変数にbase64エンコードされた画像データが格納されているので、テンプレートが定義されていないときのテンプレートを基に、body-json内にキー(ここでは、imgDataというもの)を定義し、$input.bodyを入れた。
2.2 Lambda
受け取ったbase64文字列をバイナリに戻す。あとは適宜S3等に書き込めばよい。
import base64
import boto3
s3 = boto3.client("s3")
img_binary = base64.b64decode(event["body-json"]["imgData"])
s3.put_object(
Bucket=バケット名,
Key=保存先,
Body=img_binary
)
3. 動作確認
動かしてみたところ、無事クラウド側に保存できた。
まとめと今後の課題
写真を撮って、クラウドに保存する仕組みが構築できた。いよいよ芽ねぎの栽培を始める。