Arduino Mega: съёмка фото и автоматическая загрузка в Google Drive
Компоненты и материалы
| Arduino Mega 2560 | 1 |
| PHPoC WiFi Shield 2 для Arduino | 1 |
| Seeed Grove - Serial Camera Kit | 1 |
| Seeed Grove Base Shield | 1 |
| Кнопка | 1 |
О проекте
Если вы новичок, начните с основ Arduino.
Демонстрация
Как это работает
- Вход в Google-аккаунт через OAuth 2.0 для IoT-устройств для получения access_token. Процесс описан в проекте на Hackster.
- При нажатии кнопки Arduino получает фото с камеры и загружает в Google Drive через Google Drive API, используя access_token. API описан в документации Google.
Пошаговая инструкция
- Создайте Google Project в Google Developer Portal, получите GOOGLE_CLIENT_ID и GOOGLE_CLIENT_SECRET.
- Замените их в коде Arduino.
- Загрузите файл login.php на PHPoC Shield (см. инструкцию).
- Скомпилируйте и загрузите скетч через Arduino IDE.
- В Serial Monitor узнайте IP-адрес PHPoC Shield.
- Откройте страницу входа:
https://ip_address/login.phpи авторизуйтесь в Google. - Нажмите кнопку для съёмки фото.
- Через 2 секунды фото появится в вашем Google Drive.
Другие аппаратные платформы
Аналогичный проект для другой платформы доступен здесь.
Лучший стартовый набор Arduino для новичка
Рекомендации по выбору набора: The Best Arduino Kit for Beginners.
Справочник функций
- Serial.begin(), Serial.println(), delay(), millis()
- for, while, if else, loop(), setup()
- String.toInt(), substring(), indexOf(), remove(), equals()
Код
Основной скетч Arduino (ArduinoGoogleDrive)
#include <Phpoc.h>
#include <Arduino_JSON.h>
#include "grove_camera.h"
String GOOGLE_CLIENT_ID = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.apps.googleusercontent.com";
String GOOGLE_CLIENT_SECRET = "xxxxxxxxxxxxxxxxxxxxxxxx";
PhpocServer websocket_server(80);
String http_resp_hearder(PhpocClient &client) { /* ... */ }
String http_resp_body(PhpocClient &client) { /* ... */ }
String access_token = "";
String refresh_token = "";
unsigned long access_token_expire_at = 0;
void websocket_send(String msg) { /* ... */ }
void googleDeviceOAuthLogin() { /* OAuth 2.0 Device Flow для Google */ }
void cameraToGoogleDrive() {
if (access_token == "") { Serial.println("access_token is invalid"); return; }
long picture_len = cameraGetPicture();
if (picture_len) {
PhpocDateTime datetime;
PhpocClient client;
String file_name, metadata, jpeg_boundary, end_boundary;
datetime.date("YmdHis");
file_name = datetime.date();
metadata = "--foo_bar_baz\r\nContent-Type: application/json; charset=UTF-8\r\n\r\n";
metadata += "{\"title\": \"ARDUINO_" + file_name + "\"}\r\n\r\n";
jpeg_boundary = "--foo_bar_baz\r\nContent-Type: image/jpeg\r\n\r\n";
end_boundary = "\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("Connected to server");
client.println("POST /upload/drive/v2/files?uploadType=multipart HTTP/1.1");
client.println("Host: www.googleapis.com");
client.println("Connection: close");
client.println("Accept: */*");
client.println("Content-Type: multipart/related; boundary=foo_bar_baz");
client.print("Content-Length: "); client.println(body_len);
client.print("Authorization: Bearer "); client.println(access_token);
client.println();
client.print(metadata);
client.print(jpeg_boundary);
int packet_num = cameraPacketNum();
char packet[PIC_PKT_LEN] = {0};
for (int 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(packet_num, packet);
client.print(end_boundary);
String response_hearder = http_resp_hearder(client);
String response_body = http_resp_body(client);
Serial.println(response_body);
}
} else { Serial.print("picture_len:"); Serial.println(picture_len); }
}
int buttonState, lastButtonState = LOW;
unsigned long lastDebounceTime = 0, 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");
}
}
login.php (загружается на PHPoC Shield)
<html><head><title>PHPoC / <?echo system("uname -i")?></title>
<meta content="initial-scale=1.0, maximum-scale=1.0, minimum-scale=0.5, width=device-width, user-scalable=yes" name="viewport">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto" type="text/css">
<style> /* CSS для UI входа */ </style>
<script> /* WebSocket-клиент для взаимодействия с Arduino */ </script>
</head><body>
<div class="center">
<div class="hearder"><div style="font-size: 150%">
<span style="color:#4285F4">G</span><span style="color:#EA4335">o</span><span style="color:#FBBC05">o</span>
<span style="color:#4285F4">g</span><span style="color:#34A853">l</span><span style="color:#EA4335">e</span>
</div>Login for Arduino</div><br><br>
<div class="wc_text" id="wc_text"></div><br><br>
<div class="loader" id="loader"><div class="lds-roller"><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div></div></div>
</div></body></html>
grove_camera.h (библиотека для Grove Camera)
#define PIC_PKT_LEN 512
#define CAM_ADDR 0
// Константы для настройки камеры (цвет, разрешение превью, JPEG)
const byte camera_address = (CAM_ADDR << 5);
unsigned int camera_packet_num, camera_last_packet_len;
void cameraClearRxBuf() { while (Serial.available()) Serial.read(); }
void cameraSendCmd(char cmd[], int cmd_len) { for (char i=0;i
Схемы

Производственный процесс
- Сборка Bluetooth-робота-шпиона на Arduino с управлением с телефона
- FlickMote: жестовое IR-пульт для умного дома на Arduino 101 и ультразвуковом датчике
- DIY Arduino TV-B-Gone: универсальный пульт для выключения любого телевизора
- Создание пользовательских LED-часов с будильником — видно с 12 метров
- Find Me: Умный поисковик вещей на Arduino и Bluetooth
- Оптимизированные решения питания для проектов на Arduino
- Крестики-нолики на Arduino с матрицей MAX7219 и картонным корпусом
- Пошаговое руководство: сборка умного четырёхногого робота на Arduino
- Голосовое управление бытовыми приборами: Bluetooth-управление через Google Assistant
- Инвестиции Google $450M в ADT ускоряют внедрение IoT-безопасности