Arduino делает фотографию и загружает на Google Drive

Arduino делает снимок с камеры и загружает его на Google Drive через Google Drive API, процесс входа в систему через OAuth 2.0.

Шаг 1. Комплектующие

Детали, которые мы будем использовать в этом проекте перечислены ниже.

  • Arduino Mega 2560 × 1
  • PHPoC WiFi Shield 2 для Arduino × 1
  • Seeed Grove - Серийный комплект камеры × 1
  • Кнопка × 1

Шаг 2. Как это всё работает?

  1. Войдите в учетную запись Google через OAuth 2.0 для устройств IoT, чтобы получить access_token. Процесс входа в систему мы более подробно опишем в ближайших уроках.
  2. Когда кнопка нажата, Arduino получает изображение с камеры, а затем загружает на Google Drive с помощью access_token через Google Drive API.

Google Drive API для загрузки файла описан в документации Google.

Шаг 3. Соединяем все комплектующие

Фото выше показывает соединение всех комплектующих для данного проекта.

Шаг 4. Процесс

  • Создайте проект Google на портале разработчиков Google и получите GOOGLE_CLIENT_ID и GOOGLE_CLIENT_SECRET.
  • Замените GOOGLE_CLIENT_ID и GOOGLE_CLIENT_SECRET в коде Arduino ниже.
  • Загрузите файл login.php в PHPoC Shield. Смотрите инструкцию.
  • Скомпилируйте и загрузите код Arduino через Arduino IDE.
  • Посмотрите ip_address PHPoC щита на последовательном мониторе.
  • Войдите на страницу входа на PHPoC Shield: http://ip_address/login.php и войдите в свою учетную запись Google.
  • Нажмите кнопку, чтобы сделать снимок.
  • Проверьте ваш Google Drive через две секунды, вы увидите сфотографированный кадр на вашем диске.

Шаг 5. Код проекта

Код проекта и все нужные файлы вы можете скачать или скопировать ниже.

#include <Phpoc.h>
#include <Arduino_JSON.h>
#include "grove_camera.h"

// Replace your GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET here
String GOOGLE_CLIENT_ID      = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.apps.googleusercontent.com";
String GOOGLE_CLIENT_SECRET  = "xxxxxxxxxxxxxxxxxxxxxxxx";
PhpocServer websocket_server(80);

String http_resp_hearder(PhpocClient &client){
	String hearder = "";
	while(1){
		if(client.available()){
			String line = client.readLine();

			if(line == "\r\n")
				break;
			else
				hearder += line;
		}

		if(!client.connected()){
			client.stop();
			break;
		}
	}

	return hearder;
}

String http_resp_body(PhpocClient &client){
	String body = "";
	while(1){
		if(client.available()){
			 char c = client.read();
			 body += c;
		}

		if(!client.connected()){
			client.stop();
			break;
		}
	}

	return body;
}

String access_token                  = "";
String refresh_token                 = "";
unsigned long access_token_expire_at = 0;

void websocket_send(String msg)
{
	char wbuf[256];
	msg.toCharArray(wbuf, msg.length() + 1);
	websocket_server.write(wbuf, msg.length());
}

void googleDeviceOAuthLogin(){
	PhpocClient client;

	// Step 1: Request device and user codes
	if(client.connectSSL("accounts.google.com", 443)){
		Serial.println(F("Connected to server"));

		String body = F("client_id=");
		body += GOOGLE_CLIENT_ID;
		body += F("&scope=https://www.googleapis.com/auth/drive.file");

		client.println(F("POST /o/oauth2/device/code HTTP/1.1"));
		client.println(F("Host: accounts.google.com"));
		client.println(F("Connection: close"));
		client.println(F("Accept: */*"));
		client.println(F("Content-Type: application/x-www-form-urlencoded"));
		client.print(F("Content-Length: ")); client.println(body.length());
		client.println();

		client.print(body);

		String response_hearder = http_resp_hearder(client);
		String response_body = http_resp_body(client);
		//Serial.println(response_hearder);
		//Serial.println(response_body);

		JSONVar body_json = JSON.parse(response_body);
		if(JSON.typeof(body_json) == "undefined"){
			Serial.println("Parsing input failed!");
			return;
		}

		// Step 2: Handle the authorization server response
		String device_code      = "";
		String user_code        = "";
		long expires_in          = 0;
		int interval            = 0;
		String verification_url = "";
		bool is_valid = true;

		if(body_json.hasOwnProperty("device_code"))
			device_code = body_json["device_code"];
		else
			is_valid = false;

		if(body_json.hasOwnProperty("user_code"))
			user_code = body_json["user_code"];
		else
			is_valid = false;

		if(body_json.hasOwnProperty("expires_in"))
			expires_in = (long) body_json["expires_in"];
		else
			is_valid = false;

		if(body_json.hasOwnProperty("interval"))
			interval = (int) body_json["interval"];
		else
			is_valid = false;

		if(body_json.hasOwnProperty("verification_url"))
			verification_url = body_json["verification_url"];
		else
			is_valid = false;

		if(is_valid){
			// Step 3: Display the user code
			Serial.print(F("Next, visit "));
			Serial.print(verification_url);
			Serial.print(F(" on your desktop or smartphone and enter this code: "));
			Serial.println(user_code);
			String msg;
			
			msg  = "{\"provider\": \"google\",";
			msg += "\"action\": \"LOGIN\",";
			msg += "\"verification_url\": \"" + verification_url + "\",";
			msg += "\"user_code\": \"" + user_code + "\"}";
			websocket_send(msg);

			// Step 5: Poll authorization server
			int poll_max = expires_in / interval;

			body = F("client_id=");
			body += GOOGLE_CLIENT_ID;
			body += F("&client_secret=");
			body += GOOGLE_CLIENT_SECRET;
			body += F("&code=");
			body += device_code;
			body += F("&grant_type=http://oauth.net/grant_type/device/1.0");

			for(int poll_count = 0; poll_count < poll_max; poll_count++){
				if(client.connectSSL("www.googleapis.com", 443)){
					client.println(F("POST /oauth2/v4/token HTTP/1.1"));
					client.println(F("Host: www.googleapis.com"));
					client.println(F("Connection: close"));
					client.println(F("Accept: */*"));
					client.println(F("Content-Type: application/x-www-form-urlencoded"));
					client.print(F("Content-Length: ")); client.println(body.length());
					client.println();

					client.print(body);

					response_hearder = http_resp_hearder(client);
					response_body = http_resp_body(client);
					//Serial.println(response_hearder);
					//Serial.println(response_body);

					body_json = JSON.parse(response_body);
					if(JSON.typeof(body_json) == "undefined"){
						Serial.println("Parsing input failed!");
						return;
					}

					long token_expires_in = 0;
					bool is_authorized = true;

					if(body_json.hasOwnProperty("access_token"))
						access_token = body_json["access_token"];
					else
						is_authorized = false;

					if(body_json.hasOwnProperty("expires_in"))
						token_expires_in = (long) body_json["expires_in"];
					else
						is_authorized = false;

					if(body_json.hasOwnProperty("refresh_token"))
						refresh_token = body_json["refresh_token"];
					else
						is_authorized = false;

					if(is_authorized){
						access_token_expire_at = millis() + token_expires_in * 1000;
						//Serial.print("access_token:");
						//Serial.println(access_token);

						// send success message to web
						msg  = "{\"provider\": \"google\",";
						msg += "\"action\": \"SUCCESS\"}";
						websocket_send(msg);
						break;
					}
				}

				delay(interval * 1000);
			}
		}
		else
			Serial.println(F("Invalid resonse from Google"));
	}
	else
		Serial.println(F("NOT Connected to server"));
}

void cameraToGoogleDrive()
{
	if(access_token == ""){
		Serial.println(F("access_token is invalid"));
		return;
	}

	long picture_len = cameraGetPicture();
	if(picture_len)
	{
		PhpocDateTime datetime;
		PhpocClient client;
		String file_name;
		String metadata;
		String jpeg_boundary;
		String end_boundary;
		

		datetime.date(F("YmdHis"));
		file_name = datetime.date();

		metadata  = F("--foo_bar_baz\r\n");
		metadata += F("Content-Type: application/json; charset=UTF-8\r\n\r\n");
		metadata += "{\"title\": \"ARDUINO_" + file_name + "\"}\r\n\r\n";
		jpeg_boundary  = F("--foo_bar_baz\r\n");
		jpeg_boundary += F("Content-Type: image/jpeg\r\n\r\n");
		end_boundary = F("\r\n--foo_bar_baz--");

		unsigned long body_len =metadata.length() + jpeg_boundary.length() + picture_len + end_boundary.length();

		int total = 0;
		if(client.connectSSL("www.googleapis.com", 443)){
			Serial.println(F("Connected to server"));

			String body = F("client_id=");
			body += GOOGLE_CLIENT_ID;
			body += F("&scope=https://www.googleapis.com/auth/drive.file");

			client.println(F("POST /upload/drive/v2/files?uploadType=multipart HTTP/1.1"));
			client.println(F("Host: www.googleapis.com"));
			client.println(F("Connection: close"));
			client.println(F("Accept: */*"));
			client.println(F("Content-Type: multipart/related; boundary=foo_bar_baz"));
			client.print(F("Content-Length: ")); client.println(body_len);
			client.print(F("Authorization: Bearer ")); client.println(access_token);
			client.println();

			client.print(metadata);
			client.print(jpeg_boundary);

			int i;
			int packet_num = cameraPacketNum();
			char packet[PIC_PKT_LEN] = {0};

			for(i = 0; i < packet_num; i++)
			{
				long packet_len = cameraGetPacket(i, packet);
				client.write((const uint8_t *)&packet[4], packet_len - 6);
				total += packet_len - 6;
			}
			
			cameraGetPacket(i, packet);
			client.print(end_boundary);

			String response_hearder = http_resp_hearder(client);
			String response_body = http_resp_body(client);
			//Serial.println(response_hearder);
			Serial.println(response_body);
		}
	}
	else
	{
		Serial.print("picture_len:");
		Serial.println(picture_len);
	}
}

int buttonState;
int lastButtonState = LOW;
unsigned long lastDebounceTime = 0;
unsigned long debounceDelay = 50;
bool isButtonPressed(int pin)
{
	int reading = digitalRead(pin);

	if (reading != lastButtonState)
		lastDebounceTime = millis();

	if ((millis() - lastDebounceTime) > debounceDelay) {
		if (reading != buttonState) {
			buttonState = reading;
			if (buttonState == HIGH) {
				return true;
			}
		}
	}

	lastButtonState = reading;
	return false;
}



void setup(){
	Serial.begin(115200);
	while(!Serial)
		;

	Phpoc.begin(PF_LOG_SPI | PF_LOG_NET);
	websocket_server.beginWebSocket("login");
	Serial.print("WebSocket server address : ");
	Serial.println(Phpoc.localIP());

	pinMode(2, INPUT);
	cameraInit(CT_JPEG, PR_160x120, JR_640x480);
}

void loop(){
	 PhpocClient client = websocket_server.available();

	if (client) {
		String ws_str = client.readLine();

		if(ws_str == "google\r\n")
		{
			googleDeviceOAuthLogin();
		}
	}

	if(isButtonPressed(2))
	{
		if(access_token != "" && access_token_expire_at > millis())
			cameraToGoogleDrive();
		else
			Serial.println("access_token is invalid, please login again");
	}
}

На этом всё.

11 апреля 2019 в 22:09 | Обновлено 14 июля 2020 в 18:35 (редакция)
Опубликовано:
Уроки, , ,

Добавить комментарий

Ваш E-mail не будет никому виден. Обязательные поля отмечены *