ESP32-WROOM-32で撮影した画像をAWS(API Gateway→Lambda→S3)へ送信 

#概要
題名のままですが、ESP32-WROOM-32で撮影した画像をAWS(API Gateway→Lambda→S3)へ送信してみました！

カメラはOV2640を使用しています。ESP32での制御方法は以前の記事にまとめています。本記事では既に回路図作成済み前提で話を進めます。
https://qiita.com/koki-iwaizumi/items/db81c46b0d1b39b01655

![DSC_0418.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426480/e67cc6b3-8520-3246-c98b-6d94b38b61e2.jpeg)

#S3バケット作成
画像保存先のS3のバケットを作成しておきます。バケット作成するだけなので詳細は割愛します。

#lambda設定

下記を参考にlambda関数を作成します。今回はAPIキーあり、Python3.7を使用して関数を作成しました。
https://dev.classmethod.jp/cloud/aws/getting-start-api-gateway/

関数作成後、その関数にS3のロールを割り当てる必要があるので下記を参考にS3を割り当てます。
https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/with-s3-example.html

下記がlambda_functionになります。BUCKET_NAMEはS3のバケットネームに変更してください。
処理の中身としては、json形式のデータ{"image":"base64で変換したjpeg"}を受信し、base64デーコード後、S3へランダム名で保存されるようになっています。※base64で変換できない場合などの例外処理などはしてないので、後で追加する予定です、、

トリガーをAPI Gatewayに設定し、APIキーありで保存します。

```python:lambda_function.py

import boto3
import base64
import uuid

BUCKET_NAME = '*'

def lambda_handler(event, context):

    response =  {
        "status": "failed",
    }

    print(event)

    if "image" not in event:
        return response


    s3 = boto3.resource('s3')
    bucket = s3.Bucket(BUCKET_NAME)
    bucket.put_object(
        Key=f'{uuid.uuid4()}.jpg',
        Body=base64.b64decode(event["image"]),
        ContentType='image/jpeg')

    response["status"] = "success"

    return response
```

#ESP-WROOM-32

下記のプログラムをESP-WROOM-32に書き込みます。全ファイルはgitに挙げています。
https://github.com/koki-iwaizumi/esp32-ov2640/tree/aws-lambda

wifi接続後、OV2640で画像撮影、jpegデータをbase64に変換、postメソッドでAPI Gatewayへリクエストしています。jpegデータはVGA(640px:480px)のサイズにしています。

下記の変数はそれぞれ任意の値を入力してください。
WIFI_SSID、WIFI_PASSWORD、AWS_ENDPOINT、AWS_API_KEY

```c:app_main.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "freertos/event_groups.h"

#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event_loop.h"
#include "esp_http_client.h"
#include "esp_log.h"
#include "esp_err.h"
#include "nvs_flash.h"

#include "driver/i2c.h"
#include "driver/gpio.h"
#include "camera.h"
#include "http_server.h"

#include "mbedtls/base64.h"

/********* config ***********/
#define WIFI_SSID "*"
#define WIFI_PASSWORD "*"
#define AWS_ENDPOINT "*"
#define AWS_API_KEY "*"

/********* i2c ***********/
#define I2C_CAMERA_TX_BUF_DISABLE 0
#define I2C_CAMERA_RX_BUF_DISABLE 0
#define I2C_CAMERA_FREQ_HZ 100000
#define I2C_CAMERA_NUM I2C_NUM_0
#define ACK_CHECK_EN 0x1
#define ACK_CHECK_DIS 0x0
#define ACK_VAL 0x0
#define NACK_VAL 0x1

#define CAMERA_SLAVE_ADDRESS_READ 0b01100001
#define CAMERA_SLAVE_ADDRESS_WRITE 0b01100000

/********* gpio ***********/
#define PIN_D0 35
#define PIN_D1 17
#define PIN_D2 34
#define PIN_D3 5
#define PIN_D4 39
#define PIN_D5 18
#define PIN_D6 36
#define PIN_D7 19
#define PIN_XCLK 27
#define PIN_PCLK 21
#define PIN_VSYNC 22
#define PIN_HREF 26
#define PIN_SDA 25
#define PIN_SCL 23
#define PIN_RESET 15

#define GPIO_OUTPUT_PIN_SEL  (1 << PIN_RESET)

static const char* TAG = "camera_demo app_main";

static EventGroupHandle_t s_wifi_event_group;
const int CONNECTED_BIT = BIT0;
static ip4_addr_t s_ip_addr;

/********* camera reg ***********/
static const uint8_t default_regs[][2] = {
	{0xFF, 0x00},
	{0x2C, 0xFF},
	{0x2E, 0xDF},

	{0xFF, 0x01},
	{0x3C, 0x32},
	{0x11, 0x80},
	{0x09, 0x02},
	{0x28, 0x00},
	{0x13, 0xE5},
	{0x14, 0x48},
	{0x15, 0x00},
	{0x2C, 0x0C},
	{0x33, 0x78},
	{0x3A, 0x33},
	{0x3B, 0xFB},
	{0x3E, 0x00},
	{0x43, 0x11},
	{0x16, 0x10},
	{0x39, 0x02},
	{0x35, 0x88},
	{0x22, 0x0A},
	{0x37, 0x40},
	{0x23, 0x00},
	{0x34, 0xA0},
	{0x06, 0x02},
	{0x06, 0x88},
	{0x07, 0xC0},
	{0x0D, 0xB7},
	{0x0E, 0x01},
	{0x4C, 0x00},
	{0x4A, 0x81},
	{0x21, 0x99},
	{0x24, 0x40},
	{0x25, 0x38},
	{0x26, 0x82},
	{0x48, 0x00},
	{0x49, 0x00},
	{0x5C, 0x00},
	{0x63, 0x00},
	{0x46, 0x00},
	{0x47, 0x00},
	{0x0C, 0x3A},
	{0x5D, 0x55},
	{0x5E, 0x7D},
	{0x5F, 0x7D},
	{0x60, 0x55},
	{0x61, 0x70},
	{0x62, 0x80},
	{0x7C, 0x05},
	{0x20, 0x80},
	{0x28, 0x30},
	{0x6C, 0x00},
	{0x6D, 0x80},
	{0x6E, 0x00},
	{0x70, 0x02},
	{0x71, 0x94},
	{0x73, 0xC1},
	{0x3D, 0x34},
	{0x5A, 0x57},
	{0x4F, 0xBB},
	{0x50, 0x9C},

	{0xFF, 0x00},
	{0xE5, 0x7F},
	{0xF9, 0xC0},
	{0x41, 0x24},
	{0xE0, 0x14},
	{0x76, 0xFF},
	{0x33, 0xA0},
	{0x42, 0x20},
	{0x43, 0x18},
	{0x4C, 0x00},
	{0x87, 0xD0},
	{0x88, 0x3F},
	{0xD7, 0x03},
	{0xD9, 0x10},
	{0xD3, 0x82},
	{0xC8, 0x08},
	{0xC9, 0x80},
	{0x7C, 0x00},
	{0x7D, 0x00},
	{0x7C, 0x03},
	{0x7D, 0x48},
	{0x7D, 0x48},
	{0x7C, 0x08},
	{0x7D, 0x20},
	{0x7D, 0x10},
	{0x7D, 0x0E},
	{0x90, 0x00},
	{0x91, 0x0E},
	{0x91, 0x1A},
	{0x91, 0x31},
	{0x91, 0x5A},
	{0x91, 0x69},
	{0x91, 0x75},
	{0x91, 0x7E},
	{0x91, 0x88},
	{0x91, 0x8F},
	{0x91, 0x96},
	{0x91, 0xA3},
	{0x91, 0xAF},
	{0x91, 0xC4},
	{0x91, 0xD7},
	{0x91, 0xE8},
	{0x91, 0x20},
	{0x92, 0x00},
	{0x93, 0x06},
	{0x93, 0xE3},
	{0x93, 0x03},
	{0x93, 0x03},
	{0x93, 0x00},
	{0x93, 0x02},
	{0x93, 0x00},
	{0x93, 0x00},
	{0x93, 0x00},
	{0x93, 0x00},
	{0x93, 0x00},
	{0x93, 0x00},
	{0x93, 0x00},
	{0x96, 0x00},
	{0x97, 0x08},
	{0x97, 0x19},
	{0x97, 0x02},
	{0x97, 0x0C},
	{0x97, 0x24},
	{0x97, 0x30},
	{0x97, 0x28},
	{0x97, 0x26},
	{0x97, 0x02},
	{0x97, 0x98},
	{0x97, 0x80},
	{0x97, 0x00},
	{0x97, 0x00},
	{0xA4, 0x00},
	{0xA8, 0x00},
	{0xC5, 0x11},
	{0xC6, 0x51},
	{0xBF, 0x80},
	{0xC7, 0x10},
	{0xB6, 0x66},
	{0xB8, 0xA5},
	{0xB7, 0x64},
	{0xB9, 0x7C},
	{0xB3, 0xAF},
	{0xB4, 0x97},
	{0xB5, 0xFF},
	{0xB0, 0xC5},
	{0xB1, 0x94},
	{0xB2, 0x0F},
	{0xC4, 0x5C},
	{0xA6, 0x00},
	{0xA7, 0x20},
	{0xA7, 0xD8},
	{0xA7, 0x1B},
	{0xA7, 0x31},
	{0xA7, 0x00},
	{0xA7, 0x18},
	{0xA7, 0x20},
	{0xA7, 0xD8},
	{0xA7, 0x19},
	{0xA7, 0x31},
	{0xA7, 0x00},
	{0xA7, 0x18},
	{0xA7, 0x20},
	{0xA7, 0xD8},
	{0xA7, 0x19},
	{0xA7, 0x31},
	{0xA7, 0x00},
	{0xA7, 0x18},
	{0x7F, 0x00},
	{0xE5, 0x1F},
	{0xE1, 0x77},
	{0xDD, 0x7F},
	{0xC2, 0x0E},

	{0x00, 0x00}
};

static const uint8_t svga_regs[][2] = {
	{0xFF, 0x01},
	{0x12, 0x40},
	{0x03, 0x0F},
	{0x32, 0x09},
	{0x17, 0x11},
	{0x18, 0x43},
	{0x19, 0x00},
	{0x1A, 0x4B},
	{0x3D, 0x38},
	{0x35, 0xDA},
	{0x22, 0x1A},
	{0x37, 0xC3},
	{0x34, 0xC0},
	{0x06, 0x88},
	{0x0D, 0x87},
	{0x0E, 0x41},
	{0x42, 0x03},

	{0xFF, 0x00},
	{0x05, 0x01},
	{0xE0, 0x04},
	{0xC0, 0x64},
	{0xC1, 0x4B},
	{0x8C, 0x00},
	{0x53, 0x00},
	{0x54, 0x00},
	{0x51, 0xC8},
	{0x52, 0x96},
	{0x55, 0x00},
	{0x57, 0x00},
	{0x86, 0x3D},
	{0x50, 0x80},
	{0xD3, 0x80},
	{0x05, 0x00},
	{0xE0, 0x00},

	{0x00, 0x00}
};

static const uint8_t jpeg_regs[][2] = {
	{0xFF, 0x00},
	{0xE0, 0x04},
	{0xDA, 0x18},
	{0xD7, 0x03},
	{0xE1, 0x77},
	{0x44, 0x0C},
	{0xE0, 0x00},

	{0x00, 0x00}
};

static const uint8_t framesize_low_regs[][2] = {
	{0xFF, 0x00},
	{0x05, 0x01},
	{0x5A, 0xA0},
	{0x5B, 0x78},
	{0x5C, 0x00},

	{0xFF, 0x01},
	{0x11, 0x83},

	{0x00, 0x00}
};

static const uint8_t framesize_high_regs[][2] = {
	{0xFF, 0x00},
	{0x05, 0x00},

	{0x00, 0x00}
};

static const uint8_t quality_regs[][2] = {
	{0xFF, 0x00},
	{0x44, 0x0F},

	{0x00, 0x00}
};

esp_err_t read_camera_config(uint8_t reg, uint8_t* data){

	i2c_cmd_handle_t cmd = i2c_cmd_link_create();
	i2c_master_start(cmd);
	i2c_master_write_byte(cmd, CAMERA_SLAVE_ADDRESS_WRITE, ACK_CHECK_EN);
	i2c_master_write_byte(cmd, reg, ACK_CHECK_EN);
	i2c_master_stop(cmd);
	esp_err_t err = i2c_master_cmd_begin(I2C_CAMERA_NUM, cmd, 10 / portTICK_RATE_MS);
	i2c_cmd_link_delete(cmd);

	if(err != ESP_OK){
		return err;
	}

	cmd = i2c_cmd_link_create();
	i2c_master_start(cmd);
	i2c_master_write_byte(cmd, CAMERA_SLAVE_ADDRESS_READ, ACK_CHECK_EN);
	i2c_master_read_byte(cmd, data, NACK_VAL);
	i2c_master_stop(cmd);
	err = i2c_master_cmd_begin(I2C_CAMERA_NUM, cmd, 10 / portTICK_RATE_MS);
	i2c_cmd_link_delete(cmd);

	if(err != ESP_OK){
		return err;
	}

    return ESP_OK;
}

esp_err_t write_camera_config(uint8_t reg, uint8_t data){

	i2c_cmd_handle_t cmd = i2c_cmd_link_create();
	i2c_master_start(cmd);
	i2c_master_write_byte(cmd, CAMERA_SLAVE_ADDRESS_WRITE, ACK_CHECK_EN);
	i2c_master_write_byte(cmd, reg, ACK_CHECK_EN);
	i2c_master_write_byte(cmd, data, ACK_CHECK_EN);
	i2c_master_stop(cmd);
	esp_err_t err = i2c_master_cmd_begin(I2C_CAMERA_NUM, cmd, 10 / portTICK_RATE_MS);
	i2c_cmd_link_delete(cmd);

	if(err != ESP_OK){
		return err;
	}

    return ESP_OK;
}

static esp_err_t event_handler(void *ctx, system_event_t *event){

    switch(event->event_id){
        case SYSTEM_EVENT_STA_START:
            esp_wifi_connect();
            break;
        case SYSTEM_EVENT_STA_GOT_IP:
            xEventGroupSetBits(s_wifi_event_group, CONNECTED_BIT);
            s_ip_addr = event->event_info.got_ip.ip_info.ip;
            break;
        case SYSTEM_EVENT_STA_DISCONNECTED:
            esp_wifi_connect();
            xEventGroupClearBits(s_wifi_event_group, CONNECTED_BIT);
            break;
        default:
            break;
    }
    return ESP_OK;
}

static void initialise_wifi(void){

    tcpip_adapter_init();
    s_wifi_event_group = xEventGroupCreate();
    esp_event_loop_init(event_handler, NULL);
    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    esp_wifi_init(&cfg);
    esp_wifi_set_storage(WIFI_STORAGE_RAM);
    wifi_config_t wifi_config = {
        .sta = {
            .ssid = WIFI_SSID,
            .password = WIFI_PASSWORD,
        },
    };
    esp_wifi_set_mode(WIFI_MODE_STA);
    esp_wifi_set_config(WIFI_IF_STA, &wifi_config);
    esp_wifi_start();
    esp_wifi_set_ps(WIFI_PS_NONE);
    ESP_LOGI(TAG, "Connecting to \"%s\"", wifi_config.sta.ssid);
    xEventGroupWaitBits(s_wifi_event_group, CONNECTED_BIT, false, true, portMAX_DELAY);
    ESP_LOGI(TAG, "Connected");
}

esp_err_t init_camera_clock(camera_config_t* config){

	periph_module_enable(PERIPH_LEDC_MODULE);

	ledc_timer_config_t timer_conf = {
        .duty_resolution = 1,
        .freq_hz = config->xclk_freq_hz,
        .speed_mode = LEDC_HIGH_SPEED_MODE,
        .timer_num = config->ledc_timer
	};
	esp_err_t err = ledc_timer_config(&timer_conf);
	if(err != ESP_OK){
		ESP_LOGE(TAG, "ledc_timer_config failed, err=%d", err);
		return err;
	}

	ledc_channel_config_t ch_conf = {
        .channel = config->ledc_channel,
        .timer_sel = config->ledc_timer,
        .intr_type = LEDC_INTR_DISABLE,
        .duty = 1,
        .speed_mode = LEDC_HIGH_SPEED_MODE,
        .gpio_num = config->pin_xclk
	};
	err = ledc_channel_config(&ch_conf);
	if(err != ESP_OK){
		ESP_LOGE(TAG, "ledc_channel_config failed, err=%d", err);
		return err;
	}

	return ESP_OK;
}

void init_i2c(camera_config_t* config){
	int i2c_master_port = I2C_NUM_0;
	i2c_config_t conf;
	conf.mode = I2C_MODE_MASTER;
	conf.sda_io_num = config->pin_sscb_sda;
	conf.sda_pullup_en = GPIO_PULLUP_ENABLE;
	conf.scl_io_num = config->pin_sscb_scl;
	conf.scl_pullup_en = GPIO_PULLUP_ENABLE;
	conf.master.clk_speed = I2C_CAMERA_FREQ_HZ;
	i2c_param_config(i2c_master_port, &conf);
	i2c_driver_install(i2c_master_port, conf.mode, I2C_CAMERA_RX_BUF_DISABLE, I2C_CAMERA_TX_BUF_DISABLE, 0);
}

esp_err_t _http_event_handle(esp_http_client_event_t *evt){
    switch(evt->event_id){
        case HTTP_EVENT_ERROR:
            ESP_LOGI(TAG, "HTTP_EVENT_ERROR");
            break;
        case HTTP_EVENT_ON_CONNECTED:
            ESP_LOGI(TAG, "HTTP_EVENT_ON_CONNECTED");
            break;
        case HTTP_EVENT_HEADER_SENT:
            ESP_LOGI(TAG, "HTTP_EVENT_HEADER_SENT");
            break;
        case HTTP_EVENT_ON_HEADER:
            ESP_LOGI(TAG, "HTTP_EVENT_ON_HEADER");
            printf("%.*s", evt->data_len, (char*)evt->data);
            break;
        case HTTP_EVENT_ON_DATA:
            ESP_LOGI(TAG, "HTTP_EVENT_ON_DATA, len=%d", evt->data_len);
            if( ! esp_http_client_is_chunked_response(evt->client)){
                printf("%.*s", evt->data_len, (char*)evt->data);
            }
            break;
        case HTTP_EVENT_ON_FINISH:
            ESP_LOGI(TAG, "HTTP_EVENT_ON_FINISH");
            break;
        case HTTP_EVENT_DISCONNECTED:
            ESP_LOGI(TAG, "HTTP_EVENT_DISCONNECTED");
            break;
    }
    return ESP_OK;
}

static void http_post_task(){

	esp_http_client_config_t http_config = {
		.url = AWS_ENDPOINT,
		.event_handler = _http_event_handle,
		.method = HTTP_METHOD_POST,
	};

	unsigned char image0[15000];
	char image[15000];
	size_t olen;

	mbedtls_base64_encode(image0, sizeof(image0), &olen, camera_get_fb(), camera_get_data_size());
	sprintf(image, "{\"image\":\"%s\"}", image0);
	const char *post_data = (const char*)image;

	esp_http_client_handle_t client = esp_http_client_init(&http_config);
	esp_http_client_set_post_field(client, post_data, strlen(post_data));
	esp_http_client_set_header(client, "x-api-key", AWS_API_KEY);
	esp_http_client_set_header(client, "Content-Type", "application/json");

	esp_err_t err = esp_http_client_perform(client);
	if(err == ESP_OK){
	   ESP_LOGI(TAG, "Status = %d, content_length = %d", esp_http_client_get_status_code(client), esp_http_client_get_content_length(client));
	}
	esp_http_client_cleanup(client);

	while(1);
}

void app_main(){

	esp_log_level_set("*", ESP_LOG_VERBOSE);

    esp_err_t err = nvs_flash_init();
    if(err != ESP_OK){
        nvs_flash_erase();
        nvs_flash_init();
    }

    gpio_install_isr_service(0);

    camera_config_t camera_config = {
		.ledc_channel = LEDC_CHANNEL_0,
		.ledc_timer = LEDC_TIMER_0,
		.pin_d0 = PIN_D0,
		.pin_d1 = PIN_D1,
		.pin_d2 = PIN_D2,
		.pin_d3 = PIN_D3,
		.pin_d4 = PIN_D4,
		.pin_d5 = PIN_D5,
		.pin_d6 = PIN_D6,
		.pin_d7 = PIN_D7,
		.pin_xclk = PIN_XCLK,
		.pin_pclk = PIN_PCLK,
		.pin_vsync = PIN_VSYNC,
		.pin_href = PIN_HREF,
		.pin_sscb_sda = PIN_SDA,
		.pin_sscb_scl = PIN_SCL,
		.pin_reset = PIN_RESET,

		.xclk_freq_hz = 6000000,
		.width = 640,
		.height = 480,
    };

	ESP_LOGE(TAG, "xclk_freq_hz=%d[Hz]", camera_config.xclk_freq_hz);

	//GPIO設定
	gpio_config_t io_conf;
	io_conf.intr_type = GPIO_PIN_INTR_DISABLE;
	io_conf.mode = GPIO_MODE_OUTPUT;
	io_conf.pin_bit_mask = GPIO_OUTPUT_PIN_SEL;
	io_conf.pull_down_en = 0;
	io_conf.pull_up_en = 0;
	gpio_config(&io_conf);

	//CAMERA クロック出力
	init_camera_clock(&camera_config);

	//I2C 初期化
	init_i2c(&camera_config);

	//CAMERA リセット
	gpio_set_level(PIN_RESET, 0);
	vTaskDelay(10 / portTICK_RATE_MS);
	gpio_set_level(PIN_RESET, 1);
	vTaskDelay(10 / portTICK_RATE_MS);

	//CAMERA OV2640確認
	uint8_t camera_pid_h;
	read_camera_config(0x0A, &camera_pid_h);
	ESP_LOGI(TAG, "camera_pid_h=0x%02X", camera_pid_h);
    if(camera_pid_h != 0x26){
        ESP_LOGE(TAG, "failed, camera_pid_h=%02X", camera_pid_h);
        return;
    }

	//CAMERA システムリセット
	write_camera_config(0xFF, 0x01);
	write_camera_config(0x12, 0x80);
	vTaskDelay(10 / portTICK_RATE_MS);

	//CAMERA 設定書き込み
    int i = 0;
    while(default_regs[i][0]){
		write_camera_config(default_regs[i][0], default_regs[i][1]);
        i++;
    }
	i = 0;
    while(jpeg_regs[i][0]){
		write_camera_config(jpeg_regs[i][0], jpeg_regs[i][1]);
        i++;
    }
	i = 0;
    while(framesize_low_regs[i][0]){
		write_camera_config(framesize_low_regs[i][0], framesize_low_regs[i][1]);
        i++;
    }
	i = 0;
    while(svga_regs[i][0]){
		write_camera_config(svga_regs[i][0], svga_regs[i][1]);
        i++;
    }
	i = 0;
    while(framesize_high_regs[i][0]){
		write_camera_config(framesize_high_regs[i][0], framesize_high_regs[i][1]);
        i++;
    }
	i = 0;
    while(quality_regs[i][0]){
		write_camera_config(quality_regs[i][0], quality_regs[i][1]);
        i++;
    }

	//CAMERA その他設定
    err = camera_init(&camera_config);
    if(err != ESP_OK){
        ESP_LOGE(TAG, "Camera init failed with error 0x%x", err);
        return;
    }

	//CAMERA wifi設定
    initialise_wifi();

	//写真撮影
    err = camera_run();
    if(err != ESP_OK){
        ESP_LOGD(TAG, "Camera capture failed with error = %d", err);
        return;
    }

	xTaskCreate(&http_post_task, "http_post_task", 50000, NULL, 5, NULL);

	vTaskDelay(10000 / portTICK_RATE_MS);
}
```

#実行
ESP32を実行すると、S3に撮影した画像が保存されます。

#参考まとめ
ゼロから作りながら覚えるAPI Gateway環境構築
https://dev.classmethod.jp/cloud/aws/getting-start-api-gateway/

lambda (トリガー：APIGateway)
https://blog.apar.jp/web/10804/

S3のロールを作成
https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/with-s3-example.html

APIGateway + Lambdaでbase64変換された画像ファイルをS3に保存する
https://qiita.com/yumakkt/items/1a62d279e81d9f6d0c63

[Python]Base64でエンコードされた画像データをデコードする。
https://qiita.com/TsubasaSato/items/908d4f5c241091ecbf9b

M5Cameraから画像をPOSTする
https://qiita.com/dinosauria123/items/149535349d98f51bd876