リモートカメラシステム　（Arduino 部分）

次のシステムを構成するサブシステムです。
[Grove IoT スターターキット for SORACOM で作るリモートカメラシステム](https://qiita.com/ekzemplaro/items/541b82908cec46f6aa7b)

制作開始日

>2021年8月16日
>システムを構築している様子を報告していきます。
>必要な技術については見通しが立っているので、それを組み合わせていきます。

完成日

>2021年8月23日
>画像のアップロードを MQTT から HTTP POST に変えて完成しました。

![IMG_20210816_092202.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179446/924d590c-52a6-fe52-b049-51acd78a3751.jpeg)

機能
>HTTP GET でシャッター ON の信号を受け
>画像を撮影
>画像を HTTP で POST する。


要件
>SD カードを使わない

フォルダー構造

```text
$ tree remote_camera
remote_camera
├── camera_direct.ino
├── http_upload.ino
├── remote_camera.ino
└── setupLTE.ino
```

```text:remote_camera.ino
// ---------------------------------------------------------------
/*
	remote_camera.ino

				Aug/22/2021 AM 08:30

		Adafruit VC0706 TTL Serial Camera
*/
// ---------------------------------------------------------------
#include <WioLTEforArduino.h>
#include <Adafruit_VC0706.h>
#include <stdio.h>
#include <Base64.h>
#include <ArduinoJson.h>
#include <WioLTEClient.h>

#define cameraconnection Serial

#define URL  "https://example.com/mqtt/http_to_mqtt/http_to_mqtt.py"
#define URL_STATUS  "https://example.com/mqtt/mqtt_work/status.json"

// #define INTERVAL        (9000)
#define INTERVAL        (3000)
#define DATA_SIZE (512)

WioLTE Wio;
Adafruit_VC0706 cam = Adafruit_VC0706(&cameraconnection);
String device = "00000000";


WioLTEClient WioClient(&Wio);


int icount = 0;

// ---------------------------------------------------------------
// [4]:
void setup()
{
	setupLTE();
 
	Wio.PowerSupplyGrove(true);
	
	Wio.LedSetRGB(0, 0, 1);
	delay(500);
	Wio.LedSetRGB(0, 1, 0);
	delay(500);

	snap_upload_proc();

	SerialUSB.println("*** remote_camera.ino *** Aug/22/2021 AM 08:30 ***");
}

// ---------------------------------------------------------------
// [6]:
void loop()
{
	delay(INTERVAL);

	if (get_status_prc())
		{
		snap_upload_proc();
		}
	else
		{
		SerialUSB.println("*** status *** no ON ***");
		}

	icount++;
}

// ---------------------------------------------------------------
// [6-4]:
boolean get_status_prc()
{
	boolean rvalue = true;

	DynamicJsonDocument doc(512);
	char data[512];
	int dataSize;
	dataSize=sizeof(data);

	rvalue = Wio.HttpGet(URL_STATUS, data, dataSize);
	if (!rvalue)
		{
		SerialUSB.println("### ERROR! HttpGet ###");
		}
	else
		{
		int llx = strlen(data);
		SerialUSB.println("llx = " + String(llx));
		data[llx] = '\0';
		String msg = String(data);
 
		SerialUSB.println(msg);

		deserializeJson(doc, msg);

		const char* button = doc["button"];
		if (strstr(button,"on"))
			{
			rvalue = true;
 			SerialUSB.println("*** on ***");
			}
		else
			{
			rvalue = false;
			SerialUSB.println("*** off ***");
			SerialUSB.print("button = ");
			SerialUSB.println(button);
			}
		}

	SerialUSB.println("get_status_proc: rvalue = " + String(rvalue));

	return rvalue;
}

// ---------------------------------------------------------------
// [4-4]:
void snap_upload_proc()
{
	char str_json[8192];

	camera_direct_proc(str_json);
	http_upload_proc(str_json);
}

// ---------------------------------------------------------------
```

```text:camera_direct.ino
// ---------------------------------------------------------------
/*
	camera_dicrect.ino

				Aug/23/2021 AM 08:00
*/
// ---------------------------------------------------------------
// [4]:
void camera_direct_proc(char str_json[])
{
	char data_encoded[5000 + DATA_SIZE];
	char data_jpg[5000 + DATA_SIZE];


	SerialUSB.println("*** camera_direct_proc *** start ***");

	Wio.LedSetRGB(1, 1, 0);

	int length_jpg = image_retrieve_proc(data_jpg);
//	SerialUSB.println("length_jpg =  " + String(length_jpg));

	int encodedLen = base64_enc_len(length_jpg);
//	SerialUSB.println("encodedLen =  " + String(encodedLen));

	base64_encode(data_encoded,data_jpg,encodedLen);

	Wio.LedSetRGB(1, 1, 1);

	to_json_proc(data_encoded,str_json);
}

// ---------------------------------------------------------------
// [4-4]:
int image_retrieve_proc(char data_jpg[])
{
	SerialUSB.println("*** image_retrieve_proc ***");

	take_picture_proc();

	// Get the size of the image (frame) taken	
	uint16_t jpglen = cam.frameLength();
	SerialUSB.println("*** jpglen = " + String(jpglen));
	int rvalue = jpglen;
	SerialUSB.print("Storing ");
	SerialUSB.print(jpglen, DEC);
	SerialUSB.print(" byte image.");

	int ipos = 0;
	while (jpglen > 0) {
		// read 64 bytes at a time;
		uint8_t *buffer;
		uint8_t bytesToRead = min((uint16_t)64, jpglen); // change 32 to 64 for a speedup but may not work with all setups!

		buffer = cam.readPicture(bytesToRead);

//		strncpy(&data_jpg[ipos],(const char *)buffer,bytesToRead);

		for (int it=0; it<bytesToRead; it++)
			{
			data_jpg[ipos+it] = buffer[it];
			}

		ipos += bytesToRead;
		jpglen -= bytesToRead;
	}

	data_jpg[ipos] = '\0';
	SerialUSB.println("*** ipos = " + String(ipos));

	delay(500);

	SerialUSB.println("*** image_retrieve_proc *** end ***");

	return rvalue;
}

// ---------------------------------------------------------------
// [4-4-2]:
void take_picture_proc()
{
	// Try to locate the camera
	if (cam.begin()) {
		SerialUSB.println("*** Camera Found. ***");
	} else {
		SerialUSB.println("No camera found?");
		return;
	}

//	get_camera_version_proc();

	// Set the picture size - you can choose one of 640x480, 320x240 or 160x120 
	// Remember that bigger pictures take longer to transmit!
	
//	cam.setImageSize(VC0706_640x480);	// biggest
//	cam.setImageSize(VC0706_320x240);	// medium
	cam.setImageSize(VC0706_160x120);	// small

	Wio.PowerSupplyGrove(false);

	delay(500);
	
	Wio.PowerSupplyGrove(true);
	
	// You can read the size back from the camera (optional, but maybe useful?)
	uint8_t imgsize = cam.getImageSize();
	SerialUSB.print("Image size: ");
	if (imgsize == VC0706_640x480) SerialUSB.println("640x480");
	if (imgsize == VC0706_320x240) SerialUSB.println("320x240");
	if (imgsize == VC0706_160x120) SerialUSB.println("160x120");

	delay(200);

	if (! cam.takePicture()) 
		SerialUSB.println("*** Failed to snap! ***");
	else 
		SerialUSB.println("*** Picture taken! ***");
		uint16_t jpglen = cam.frameLength();
		SerialUSB.print("cam.frameLength: ");
		SerialUSB.print(jpglen, DEC);
		SerialUSB.println(" byte image.");
}

// ---------------------------------------------------------------
// [4-4-2-4]:
void get_camera_version_proc()
{
	char *reply = cam.getVersion();
	if (reply == 0)
		{
		SerialUSB.println("*** Failed to get version ***");
		}
	else
		{
		SerialUSB.println("-----------------");
		SerialUSB.print(reply);
		SerialUSB.println("-----------------");
		}
}

// ---------------------------------------------------------------
void to_json_proc(char data_in[],char str_json_tt[])
{
	char str_json[8192];
	DynamicJsonDocument doc_camera(10512);

	doc_camera["base64"] = String(data_in);
	doc_camera["aa"] = "Hello";
	doc_camera["version"] = "Aug/21/2021 PM 13:10";
	doc_camera["device"] = device;
	doc_camera["sizeofdata"] = strlen(data_in);

	serializeJson(doc_camera, str_json);

	SerialUSB.print(str_json);
	delay(200);
	SerialUSB.println("");

	for (int it=0; it < 8192; it++)
		{
		str_json_tt[it] = str_json[it];
		}
}

// ---------------------------------------------------------------
```

```text:http_upload.ino
// ---------------------------------------------------------------
/*
	http_upload.ino

				Aug/20/2021 AM 08:00
*/
// ---------------------------------------------------------------
// [6-4]:
void http_upload_proc(char url_target[], char str_json[])
{
	int status;

	SerialUSB.println("*** http_upload_proc *** start ***");

	if (!Wio.HttpPost(url_target, str_json, &status))
		{
		SerialUSB.println("### ERROR! HttpPost ###");
		}
	else
		{
		SerialUSB.print("Status: ");
		SerialUSB.println(status);
		}

	delay(200);

	SerialUSB.println("*** http_upload_proc *** end ***");
}

// ---------------------------------------------------------------
```

setupLTE.ino はこちら
[UDP で温度と湿度を Harvest に送る](https://qiita.com/ekzemplaro/items/690faa6f56003272299f)