Arduino - 拍照 - 上传到 Google Drive
组件和用品
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 |
关于这个项目
如果您是初学者,您可以在这里了解 Arduino。
演示
工作原理
1. IoT 设备通过 OAuth 2.0 登录 Google 帐户以获取access_token。
在 Hackster 上的这个项目中描述了登录过程。
2. 当按下按钮时,Arduino 从相机获取图片,然后使用 access_token 上传到 Google Drive 通过 Google Drive API。
Google 文档中描述了用于上传文件的 Google Drive API。
如何
- 从 Google Developer Portal 创建 Google 项目并获取 GOOGLE_CLIENT_ID 和 GOOGLE_CLIENT_SECRET
- 替换 Arduino 代码中的 GOOGLE_CLIENT_ID 和 GOOGLE_CLIENT_SECRET
- 将 login.php 文件上传到 PHPoC Shield。查看说明
- 通过 Arduino IDE 编译和上传 Arduino 代码
- 在串口监视器上查看PHPoC shield的ip_address
- 访问 PHPoC Shield 上的登录页面:http://ip_address/login.php 并登录到您的 Google 帐户
- 按下按钮拍照
- 两秒钟后检查您的 Google 云端硬盘,您将在云端硬盘中看到拍摄的照片。
其他硬件平台
我在这里为另一个硬件平台做了同样的项目。
适合初学者的最佳 Arduino 入门套件
如果您正在寻找 Arduino 套件,请参阅适合初学者的最佳 Arduino 套件
函数参考
- Serial.begin()
- Serial.println()
- 延迟()
- millis()
- for 循环
- while 循环
- 否则
- 循环()
- 设置()
- String.toInt()
- String.substring()
- String.indexOf()
- String.remove()
- String.equals()
代码
- ArduinoGoogleDrive
- 登录.php
- grove_camera.h
ArduinoGoogleDriveArduino
这是主要的 Arduino 代码。#include#include #include "grove_camera.h"// 在这里替换你的 GOOGLE_CLIENT_ID 和 GOOGLE_CLIENT_SECRETString GOOGLE_CLIENT_ID ="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxcontent.apps"; String GOOGLE_CLIENT_SECRET ="xxxxxxxxxxxxxxxxxxxxxxxx";PhpocServer websocket_server(80);String http_resp_hearder(PhpocClient &client){ String listener =""; while(1){ if(client.available()){ String line =client.readLine(); if(line =="\r\n") 中断;否则听者 +=行; } if(!client.connected()){ client.stop();休息; } } 返回听者;}String http_resp_body(PhpocClient &client){ String body =""; while(1){ if(client.available()){ char c =client.read();身体 +=c; } if(!client.connected()){ client.stop();休息; } } 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 客户端; // 第 1 步:请求设备和用户代码 if(client.connectSSL("accounts.google.com", 443)){ Serial.println(F("Connected to server")); String body =F("client_id=");正文 +=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("主机:accounts.google.com")); client.println(F("连接:关闭")); client.println(F("接受:*/*")); client.println(F("Content-Type:application/x-www-form-urlencoded")); client.print(F("内容长度:")); client.println(body.length());客户端打印();客户端打印(主体); 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("解析输入失败!");返回; } // 第二步:处理授权服务器响应 String device_code =""; String user_code =""; long expires_in =0;整数间隔 =0;字符串验证_url =""; bool is_valid =true; if(body_json.hasOwnProperty("device_code")) device_code =body_json["device_code"];否则 is_valid =false; if(body_json.hasOwnProperty("user_code")) user_code =body_json["user_code"];否则 is_valid =false; if(body_json.hasOwnProperty("expires_in")) expires_in =(long) body_json["expires_in"];否则 is_valid =false; if(body_json.hasOwnProperty("interval")) interval =(int) body_json["interval"];否则 is_valid =false; if(body_json.hasOwnProperty("verification_url")) verify_url =body_json["verification_url"];否则 is_valid =false; if(is_valid){ // 第三步:显示用户代码 Serial.print(F("Next,visit ")); Serial.print(verification_url); Serial.print(F(" 在您的桌面或智能手机上输入此代码:")); Serial.println(user_code);字符串味精; msg ="{\"provider\":\"google\","; msg +="\"action\":\"登录\","; msg +="\"verification_url\":\"" + verify_url + "\","; msg +="\"user_code\":\"" + user_code + "\"}"; websocket_send(msg); // 第五步:轮询授权服务器 int poll_max =expires_in / interval; body =F("client_id=");正文 +=GOOGLE_CLIENT_ID; body +=F("&client_secret=");正文 +=GOOGLE_CLIENT_SECRET; body +=F("&code=");正文 +=设备代码; body +=F("&grant_type=http://oauth.net/grant_type/device/1.0"); for(int poll_count =0; poll_count debounceDelay) { if (reading !=buttonState) { buttonState =reading;如果(buttonState ==HIGH){ 返回真; } } } lastButtonState =阅读; return false;}void setup(){ Serial.begin(115200);而(!串行); Phpoc.begin(PF_LOG_SPI | PF_LOG_NET); websocket_server.beginWebSocket("登录"); Serial.print("WebSocket 服务器地址:"); Serial.println(Phpoc.localIP()); pinMode(2, 输入); cameraInit(CT_JPEG, PR_160x120, JR_640x480);}void loop(){ PhpocClient 客户端 =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 无效,请重新登录"); }}
login.phpPHP
此文件代码已上传到 PHPoC Shield。它为 Google 登录过程提供 Web 用户界面PHPoC / G o o g l eArduino 登录
grove_camera.hC/C++
Grove 相机库#define PIC_PKT_LEN 512 //每次读取的数据长度,不要设置太大,因为内存有限#define CAM_ADDR 0//颜色类型#define CT_GRAYSCALE_2 0x01#define CT_GRAYSCALE_4 0x02#define CT_GRAYSCALE_3#definex0 CT_COLOR_12 0×05#限定CT_COLOR_16 0×06#限定CT_JPEG 0x07的//预览分辨率#限定PR_80x60 0×01#限定PR_160x120×03 // JPEG分辨率#限定JR_80x64 0×01#限定JR_160x128×03#限定JR_320x240 0×05#限定JR_640x480 0x07const字节camera_address =(CAM_ADDR <<5 ); // addressunsigned int camera_packet_num;unsigned int camera_last_packet_len;void cameraClearRxBuf(){ while (Serial.available()){ Serial.read(); }}void cameraSendCmd(char cmd[], int cmd_len){ for (char i =0; i>8) &0xff,0};而 (1) { cameraClearRxBuf(); cameraSendCmd(cmd3, 6); if(Serial.readBytes((char *)resp, 6) !=6) 继续; if(resp[0] ==0xaa &&resp[1] ==(0x0e | camera_address) &&resp[2] ==0x06 &&resp[4] ==0 &&resp[5] ==0) 中断; }}long cameraGetPicture(){ char cmd[] ={ 0xaa, 0x04 |相机地址, 0x01, 0x00, 0x00, 0x00 };无符号字符响应[6];无符号长 picTotalLen =0; // 图片长度 while (1){ cameraClearRxBuf(); cameraSendCmd(cmd, 6); if(Serial.readBytes((char *)resp, 6) !=6) 继续; if(resp[0] ==0xaa &&resp[1] ==(0x0e | camera_address) &&resp[2] ==0x04 &&resp[4] ==0 &&resp[5] ==0){ Serial.setTimeout (1000); if(Serial.readBytes((char *)resp, 6) !=6) 继续; if(resp[0] ==0xaa &&resp[1] ==(0x0a | camera_address) &&resp[2] ==0x01){ picTotalLen =(resp[3]) | (resp[4] <<8) | (resp[5] <<16);休息; } } } camera_packet_num =(picTotalLen) / (PIC_PKT_LEN - 6); camera_last_packet_len =PIC_PKT_LEN; if((picTotalLen % (PIC_PKT_LEN-6)) !=0){ camera_packet_num +=1; camera_last_packet_len =picTotalLen % (PIC_PKT_LEN - 6) + 6; } return picTotalLen;}long cameraGetPacket(unsigned int i, char* buf){ char cmd[] ={ 0xaa, 0x0e |相机地址, 0x00, 0x00, 0x00, 0x00 }; Serial.setTimeout(100); if(i > 8) &0xff; cameraClearRxBuf(); cameraSendCmd(cmd, 6); int pkt_len; if(i <(camera_packet_num - 1)) pkt_len =PIC_PKT_LEN;否则 pkt_len =camera_last_packet_len; uint16_t cnt =Serial.readBytes((char *)buf, pkt_len);返回 cnt; } else { cmd[4] =0xf0; cmd[5] =0xf0; cameraSendCmd(cmd, 6); } return "";}
示意图
制造工艺