Manufactura industrial
Internet industrial de las cosas | Materiales industriales | Mantenimiento y reparación de equipos | Programación industrial |
home  MfgRobots >> Manufactura industrial >  >> Manufacturing Technology >> Proceso de manufactura

Arduino Mega: Captura Fotos y Súbelas Automáticamente a Google Drive

Componentes y suministros

Componente Cantidad
Arduino Mega 2560 1
PHPoC WiFi Shield 2 para Arduino 1
Kit Seeed Grove - Cámara Serial 1
Seeed Grove base shield 1
Botón 1

Sobre el proyecto

Si eres principiante, puedes aprender sobre Arduino aquí.

Demostración

Cómo funciona

1. Inicia sesión en tu cuenta de Google mediante OAuth 2.0 para dispositivos IoT y obtén el access_token. El proceso de login se describe en este proyecto en Hackster.

2. Al presionar el botón, Arduino obtiene la imagen de la cámara y la sube a Google Drive usando el access_token a través de la API de Google Drive. La API de Google Drive para subir archivos está descrita en la documentación de Google.

Cómo hacerlo

Otras plataformas de hardware

He realizado el mismo proyecto para otra plataforma de hardware aquí.

El mejor kit de inicio Arduino para principiantes

Si buscas un kit Arduino, consulta El mejor kit Arduino para principiantes.

Referencias de funciones

Código

ArduinoGoogleDrive (Arduino)

Este es el código principal de Arduino.

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

// Reemplaza tu GOOGLE_CLIENT_ID y GOOGLE_CLIENT_SECRET aquí
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;
  // Paso 1: Solicitar códigos de dispositivo y usuario
  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;
    }
    
    // Paso 2: Manejar respuesta del servidor de autorización
    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) {
      // Paso 3: Mostrar código de usuario
      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);
      
      // Paso 5: Consultar servidor de autorización
      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;
            // Enviar mensaje de éxito a 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"));
      
      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_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)

Este archivo se sube al PHPoC Shield. Proporciona interfaz web de usuario para el proceso de login de Google.

<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; -webkit-backface-visibility: hidden; 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;}
/*loading icon*/
.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; }
.lds-roller div:nth-child(1) {animation-delay: -0.036s;}
.lds-roller div:nth-child(1):after {top: 50px;left: 50px;}
.lds-roller div:nth-child(2) {animation-delay: -0.072s;}
.lds-roller div:nth-child(2):after {top: 54px;left: 45px;}
.lds-roller div:nth-child(3) {animation-delay: -0.108s;}
.lds-roller div:nth-child(3):after {top: 57px;left: 39px;}
.lds-roller div:nth-child(4) {animation-delay: -0.144s;}
.lds-roller div:nth-child(4):after {top: 58px;left: 32px;}
.lds-roller div:nth-child(5) {animation-delay: -0.18s;}
.lds-roller div:nth-child(5):after {top: 57px;left: 25px;}
.lds-roller div:nth-child(6) {animation-delay: -0.216s;}
.lds-roller div:nth-child(6):after {top: 54px;left: 19px;}
.lds-roller div:nth-child(7) {animation-delay: -0.252s;}
.lds-roller div:nth-child(7):after {top: 50px;left: 14px;}
.lds-roller div:nth-child(8) {animation-delay: -0.288s;}
.lds-roller div:nth-child(8):after {top: 45px;left: 10px;}
@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.onopen = null; ws.onclose = null; ws.onmessage = null; 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++)

Librería para cámara Grove

#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;
    }
  }
  
  cmd[1] = 0x0e | camera_address;
  cmd[2] = 0x0d;
  cameraSendCmd(cmd, 6);
  
  char cmd2[] = {0xaa, 0x01 | camera_address, 0x00, color_type, preview_resolution, jpeg_resolution};
  Serial.setTimeout(100);
  
  while (1) {
    cameraClearRxBuf();
    cameraSendCmd(cmd2, 6);
    if(Serial.readBytes((char *)resp, 6) != 6) continue;
    if(resp[0] == 0xaa && resp[1] == (0x0e | camera_address) && resp[2] == 0x01 && resp[4] == 0 && resp[5] == 0) break;
  }
  
  char cmd3[] = {0xaa, 0x06 | camera_address, 0x08, PIC_PKT_LEN & 0xff, (PIC_PKT_LEN>>8) & 0xff, 0};
  
  while (1) {
    cameraClearRxBuf();
    cameraSendCmd(cmd3, 6);
    if(Serial.readBytes((char *)resp, 6) != 6) continue;
    if(resp[0] == 0xaa && resp[1] == (0x0e | camera_address) && resp[2] == 0x06 && resp[4] == 0 && resp[5] == 0) break;
  }
}

long cameraGetPicture() {
  char cmd[] = {0xaa, 0x04 | camera_address, 0x01, 0x00, 0x00, 0x00};
  unsigned char resp[6];
  unsigned long picTotalLen = 0;
  
  while (1) {
    cameraClearRxBuf();
    cameraSendCmd(cmd, 6);
    if(Serial.readBytes((char *)resp, 6) != 6) continue;
    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) continue;
      if(resp[0] == 0xaa && resp[1] == (0x0a | camera_address) && resp[2] == 0x01) {
        picTotalLen = (resp[3]) | (resp[4] << 8) | (resp[5] << 16);
        break;
      }
    }
  }
  
  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 | camera_address, 0x00, 0x00, 0x00, 0x00};
  Serial.setTimeout(100);
  
  if(i < camera_packet_num) {
    cmd[4] = i & 0xff;
    cmd[5] = (i >> 8) & 0xff;
    cameraClearRxBuf();
    cameraSendCmd(cmd, 6);
    
    int pkt_len;
    if(i < (camera_packet_num - 1)) pkt_len = PIC_PKT_LEN;
    else pkt_len = camera_last_packet_len;
    
    uint16_t cnt = Serial.readBytes((char *)buf, pkt_len);
    return cnt;
  } else {
    cmd[4] = 0xf0;
    cmd[5] = 0xf0;
    cameraSendCmd(cmd, 6);
  }
  return "";
}

Esquemáticos

Arduino Mega: Captura Fotos y Súbelas Automáticamente a Google Drive

Proceso de manufactura

  1. Construye un Spybot controlado por Bluetooth con Arduino
  2. FlickMote: Mando IR por gestos para Smart Home
  3. TV‑B‑Gone DIY con Arduino: Mando universal para apagar cualquier TV
  4. Construye un Reloj Maestro LED Personalizado con Alarma – Visible desde 12 Metros
  5. Find Me: Localizador Inteligente de Objetos con Arduino y Bluetooth
  6. Soluciones de Alimentación Optimizadas para Proyectos Arduino
  7. Tres en raya con Arduino, matriz LED MAX7219 y carcasa de cartón
  8. Construye un Robot Cuadrúpedo Inteligente con Arduino: Guía Paso a Paso
  9. Electrodomésticos activados por voz: Control Bluetooth con Google Assistant
  10. Construye un volante joystick personalizado con Arduino para gaming
  11. Inversión de $450M de Google en ADT Acelera la Adopción de Seguridad IoT