Industriële fabricage
Industrieel internet der dingen | Industriële materialen | Onderhoud en reparatie van apparatuur | Industriële programmering |
home  MfgRobots >> Industriële fabricage >  >> Manufacturing Technology >> Productieproces

Arduino Mega: Foto's Maken en Automatisch Uploaden naar Google Drive

Onderdelen en benodigdheden

Arduino Mega 2560 × 1
PHPoC WiFi Shield 2 voor Arduino × 1
Seeed Grove - Serial Camera Kit × 1
Seeed Grove base shield × 1
Knop × 1

Over dit project

Als je beginner bent, kun je hier meer over Arduino leren.

Demonstratie

Hoe het werkt

1. Log in op Google-account via OAuth 2.0 voor IoT-apparaten om access_token te verkrijgen. Het inlogproces wordt beschreven in dit project op Hackster.

2. Wanneer de knop wordt ingedrukt, haalt de Arduino een foto op van de camera en uploadt deze naar Google Drive met behulp van de access_token via de Google Drive API. De Google Drive API voor het uploaden van bestanden wordt beschreven in de Google-documentatie.

Hoe te bouwen

  • Maak een Google-project aan via Google Developer Portal en verkrijg GOOGLE_CLIENT_ID en GOOGLE_CLIENT_SECRET
  • Vervang GOOGLE_CLIENT_ID en GOOGLE_CLIENT_SECRET in de Arduino-code
  • Upload login.php-bestand naar PHPoC Shield. Zie instructies
  • Compileer en upload Arduino-code via Arduino IDE
  • Bekijk IP-adres van PHPoC shield op Serial Monitor
  • Ga naar inlogpagina op PHPoC Shield: https://ip_adres/login.php en log in op je Google-account
  • Druk op de knop om een foto te maken
  • Controleer je Google Drive na twee seconden, je ziet de gemaakte foto in je Drive

Andere hardwareplatforms

Ik heb hetzelfde project voor een ander hardwareplatform gemaakt.

De beste Arduino Starter Kit voor beginners

Als je op zoek bent naar een Arduino-kit, zie "The Best Arduino Kit for Beginners".

Functiereferenties

  • Serial.begin()
  • Serial.println()
  • delay()
  • millis()
  • for-lus
  • while-lus
  • if-else
  • loop()
  • setup()
  • String.toInt()
  • String.substring()
  • String.indexOf()
  • String.remove()
  • String.equals()

Code

ArduinoGoogleDrive (Hoofd Arduino-code)
#include <Phpoc.h>
#include <Arduino_JSON.h>
#include "grove_camera.h"

// Vervang je GOOGLE_CLIENT_ID en GOOGLE_CLIENT_SECRET hier
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;
  // Stap 1: Vraag device en user codes aan
  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);
    
    JSONVar body_json = JSON.parse(response_body);
    if (JSON.typeof(body_json) == "undefined") {
      Serial.println("Parsing input failed!");
      return;
    }
    
    // Stap 2: Verwerk autorisatieserver response
    String device_code = "", user_code = "", verification_url = "";
    long expires_in = 0;
    int interval = 0;
    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) {
      // Stap 3: Toon 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 = "{\"provider\": \"google\",";
      msg += "\"action\": \"LOGIN\",";
      msg += "\"verification_url\": \"" + verification_url + "\",";
      msg += "\"user_code\": \"" + user_code + "\"}";
      websocket_send(msg);
      
      // Stap 5: Poll autorisatieserver
      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=https://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);
          
          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;
            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, metadata, jpeg_boundary, 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"));
      
      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, packet_num = cameraPacketNum();
      char packet[512] = {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_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");
  }
}
login.php (PHP - upload naar 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>
body { text-align:center; }
.center { margin: auto; position: absolute; left:0; right:0; text-align: center; top: 20%; }
.hearder { width: 100%; max-width:400px; color: #008B8B; padding: 5px; border-bottom: solid; margin-bottom: 5px; font-size: 200%; display: inline-block; }
.wc_text, .loader { display: inline-block; width: 100%; max-width:300px; line-height: 150%; }
.code { font-family: "Courier New", Courier, monospace; font-size: 150%; font-weight: bold; color: #A52A2A; }
.success {font-weight: bold; color: #A52A2A;}
.lds-roller { display: inline-block; position: relative; width: 64px; height: 64px; }
.lds-roller div { animation: lds-roller 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite; transform-origin: 32px 32px; }
.lds-roller div:after { content: " "; display: block; position: absolute; width: 6px; height: 6px; border-radius: 50%; background: #A52A2A; margin: -3px 0 0 -3px; }
@keyframes lds-roller {0% {transform: rotate(0deg);}100% {transform: rotate(360deg);}}
</style>
<script>
var ws;
function init() {
  ws = new WebSocket("ws://<?echo _SERVER(\"HTTP_HOST\")?>/login", "text.phpoc");
  ws.onopen = ws_onopen;
  ws.onclose = ws_onclose;
  ws.onmessage = ws_onmessage;
}
function ws_onopen() { if(ws && (ws.readyState == 1)) ws.send('google\r\n'); }
function ws_onclose() { alert('CANNOT connect to device. Please reload webpage'); ws = null; }
function ws_onmessage(e_msg) {
  e_msg = e_msg || window.event;
  var obj = JSON.parse(e_msg.data);
  var wc_text = document.getElementById('wc_text');
  if(obj.action == 'LOGIN') {
    wc_text.innerHTML = 'Next, visit <a href="' + obj.verification_url + '" target="_blank">' + obj.verification_url + '</a> and enter this code:<br>';
    wc_text.innerHTML += '<span class="code">' + obj.user_code + '</span>';
  } else if(obj.action == 'SUCCESS') {
    document.getElementById('loader').style.display = 'none';
    wc_text.innerHTML = '<span class="success">Success!</span><br>';
    wc_text.innerHTML += 'You are now logged in from Arduino via PHPoC Shield';
  }
}
window.onload = init;
</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 (C/C++ - Bibliotheek voor Grove Camera)
#define PIC_PKT_LEN 512
#define CAM_ADDR 0

#define CT_GRAYSCALE_2 0x01
#define CT_GRAYSCALE_4 0x02
#define CT_GRAYSCALE_8 0x03
#define CT_COLOR_12 0x05
#define CT_COLOR_16 0x06
#define CT_JPEG 0x07

#define PR_80x60 0x01
#define PR_160x120 0x03

#define JR_80x64 0x01
#define JR_160x128 0x03
#define JR_320x240 0x05
#define JR_640x480 0x07

const byte camera_address = (CAM_ADDR << 5);
unsigned 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 < cmd_len; i++) Serial.print(cmd[i]);
}

unsigned int cameraPacketNum() { return camera_packet_num; }

void cameraInit(int color_type, int preview_resolution, int jpeg_resolution) {
  char cmd[] = {0xaa, 0x0d | camera_address, 0x00, 0x00, 0x00, 0x00};
  unsigned char resp[6];
  Serial.setTimeout(500);
  while (1) {
    cameraSendCmd(cmd, 6);
    if (Serial.readBytes((char *)resp, 6) != 6) continue;
    if (resp[0] == 0xaa && resp[1] == (0x0e | camera_address) && resp[2] == 0x0d && resp[4] == 0 && resp[5] == 0) {
      if (Serial.readBytes((char *)resp, 6) != 6) continue;
      if (resp[0] == 0xaa && resp[1] == (0x0d | camera_address) && resp[2] == 0 && resp[3] == 0 && resp[4] == 0 && resp[5] == 0) break;
    }
  }
  // ... (verder initialisatiecode)
}

Schema's

Arduino Mega: Foto s Maken en Automatisch Uploaden naar Google Drive

Productieproces

  1. Bluetooth-gecontroleerde Arduino Spybot bouwen
  2. FlickMote: Gebarengecontroleerde IR-afstandsbediening voor Slimme Huizen
  3. DIY Arduino TV-B-Gone: Universele TV-uit-schakelaar
  4. Bouw een Aangepaste LED Hoofdklok met Alarm – Zichtbaar vanaf 12 Meter
  5. Find Me: Slimme Item Locator met Arduino en Bluetooth
  6. Geoptimaliseerde Voedingsoplossingen voor Arduino Projecten
  7. Arduino Boter-Kaas-Eieren met MAX7219 LED Matrix en Kartonnen Behuizing
  8. Bouw een Slimme Arduino Vierpotige Robot: Stap-voor-Stap Handleiding
  9. Spraakgestuurde huishoudapparaten: Bluetooth-besturing met Google Assistant
  10. Bouw een Aangepaste Arduino Joystick Stuurwiel voor Gaming
  11. Google's $450M Investering in ADT Versnelt IoT Beveiligingsadoptie