Sommario:

DuvelBot - Robot per servire la birra ESP32-CAM: 4 passaggi (con immagini)
DuvelBot - Robot per servire la birra ESP32-CAM: 4 passaggi (con immagini)

Video: DuvelBot - Robot per servire la birra ESP32-CAM: 4 passaggi (con immagini)

Video: DuvelBot - Robot per servire la birra ESP32-CAM: 4 passaggi (con immagini)
Video: Ai robots taking over ping pong 👀 #shorts 2024, Dicembre
Anonim
DuvelBot - Robot per servire la birra ESP32-CAM
DuvelBot - Robot per servire la birra ESP32-CAM

Dopo una dura giornata di lavoro, niente si avvicina al sorseggiare la tua birra preferita sul divano. Nel mio caso, questa è la birra bionda belga "Duvel". Tuttavia, dopo tutto, ma al collasso, ci troviamo di fronte a un problema molto serio: il frigorifero contenente il mio Duvel è un incolmabile 20 piedi rimosso da detto divano.

Mentre una leggera coercizione da parte mia potrebbe spingere un occasionale spazzino di frigoriferi adolescente a versare la mia indennità settimanale di Duvel, il compito di consegnarlo effettivamente al suo progenitore quasi esausto è ovviamente un passo troppo lontano.

È ora di rompere il saldatore e la tastiera…

DuvelBot è una webcam di guida basata su AI-Thinker ESP32-CAM senza fronzoli, che puoi controllare dal tuo smartphone, browser o tablet.

È facile adattare o espandere questa piattaforma per usi meno alcolici (pensa a SpouseSpy, NeighbourWatch, KittyCam…).

Ho costruito questo robot principalmente per imparare un po' dell'intera programmazione web e dell'IoT, di cui non sapevo nulla. Quindi alla fine di questo Instructable c'è una spiegazione elaborata di come funziona.

Molte parti di questo Instructable si basano sulle eccellenti spiegazioni trovate su Random Nerd Tutorials, quindi per favore vai a visitarli!

Forniture

Quello di cui hai bisogno:

L'elenco delle parti non è scolpito nella pietra e molte parti possono essere ottenute in un sacco di versioni diverse e da molti luoghi diversi. Mi sono procurato la maggior parte da Ali-Express. Come diceva Machete: improvvisare.

Hardware:

  • Modulo ESP32-CAM del pensatore AI. Probabilmente potrebbe funzionare con altri moduli ESP32-CAM ma è quello che ho usato
  • Scheda driver motore L298N,
  • Una piattaforma robotica a 4 ruote economica,
  • Un alloggiamento con una grande superficie piana come l'Hammond Electronics 1599KGY,
  • Convertitore da USB a 3.3V-TTL per la programmazione.
  • Per l'illuminazione: 3 LED bianchi, BC327 o altro transistor NPN generico (Ic=500mA), resistore 4k7k, 3 resistori 82Ohm, perfboard, cavi (vedi schema e immagini).
  • Un interruttore a levetta on/off e un pulsante normalmente aperto per la programmazione.

Opzionale:

  • Una telecamera fisheye con flex più lungo rispetto alla telecamera standard OV2460 fornita con il modulo ESP32-CAM,
  • Antenna WiFi con cavo opportunamente lungo e connettore coassiale ultra miniaturizzato, come questo. L'ESP32-CAM ha un'antenna integrata e l'alloggiamento è in plastica, quindi non è davvero necessaria un'antenna, tuttavia ho pensato che fosse bello, quindi …
  • Carta adesiva stampabile a getto d'inchiostro per il design della copertina superiore.

I soliti strumenti di ferramenta: saldatore, trapani, cacciaviti, pinze…

Passaggio 1: costruire la piattaforma del robot

Costruire la piattaforma robotica
Costruire la piattaforma robotica
Costruire la piattaforma robotica
Costruire la piattaforma robotica
Costruire la piattaforma robotica
Costruire la piattaforma robotica

Lo schema:

Lo schema non è niente di speciale. La cam ESP32 controlla i motori tramite la scheda driver del motore L298N, che ha due canali. I motori del lato destro e sinistro sono posti in parallelo e ogni lato occupa un canale. Quattro piccoli condensatori ceramici da 10..100nF vicino ai pin del motore sono come sempre consigliabili per contrastare le interferenze RF. Inoltre, un grande cappuccio elettrolitico (2200…4700uF) sull'alimentazione della scheda motore come mostrato nello schema, sebbene non strettamente necessario, può limitare un po' l'ondulazione della tensione di alimentazione (se vuoi vedere un film horror, allora sonda Vbat con un oscilloscopio mentre i motori sono attivi).

Si noti che entrambi i pin ENABLE dei canali del motore sono pilotati dallo stesso pin a modulazione di larghezza di impulso (PWM) dell'ESP32 (IO12). Questo perché il modulo ESP32-CAM non ha un sacco di GPIO (schema del modulo incluso per riferimento). I LED del robot sono pilotati da IO4, che aziona anche il LED flash integrato, quindi rimuovere Q1 per evitare che il LED flash si accenda in un alloggiamento chiuso.

Pulsante di programmazione, interruttore di accensione/spegnimento, connettore di ricarica e connettore di programmazione sono accessibili sotto il robot. Avrei potuto fare un lavoro molto migliore per il connettore di programmazione (jack da 3,5 mm?), ma la birra non poteva più aspettare. Anche gli aggiornamenti via etere (OTA) sarebbero piacevoli da configurare.

Per mettere il robot in modalità di programmazione, premere il pulsante di programmazione (questo porta IO0 basso) e poi accenderlo.

Importante: per caricare le batterie NiMH del robot, utilizzare un alimentatore da laboratorio impostato (scaricato) a circa 14V e corrente limitata a 250mA. La tensione si adatterà alla tensione delle batterie. Scollegare se il robot è caldo o se la tensione della batteria raggiunge circa 12,5 V. Un ovvio miglioramento qui sarebbe quello di integrare un caricabatterie adeguato, ma questo esula dallo scopo di questo Instructable.

L'hardware:

Si prega di vedere anche le note nelle immagini. L'alloggiamento è montato sulla base del robot mediante 4 bulloni M4 e dadi autobloccanti. Notare i tubi di gomma usati come distanziatori. Si spera che questo dia anche un po 'di sospensione al Duvel, se la corsa si rivelasse accidentata. Il modulo ESP32-CAM e la scheda motore L298N sono montati nell'alloggiamento utilizzando piedini adesivi in plastica (non sono sicuro del nome corretto in inglese), per evitare di dover praticare fori aggiuntivi. Anche l'ESP32 è montato su una propria perfboard e pinheader collegabili. Ciò semplifica la sostituzione di ESP32.

Non dimenticare: se stai utilizzando un'antenna WiFi esterna invece di quella integrata, salda anche il ponticello di selezione dell'antenna sul lato inferiore della scheda ESP32-CAM.

Stampa il logo in alto nel file DuvelBot.svg su carta adesiva a getto d'inchiostro (o creane uno tuo) e sei pronto per partire!

Passaggio 2: programma il robot

Programma il Robot
Programma il Robot

Si consiglia di programmare il robot prima di chiuderlo, per assicurarsi che tutto funzioni e non appaia fumo magico.

Sono necessari i seguenti strumenti software:

  • L'IDE Arduino,
  • Le librerie ESP32, SPIFFS (file system flash periferico seriale), libreria ESPAsync Webserver.

Quest'ultimo può essere installato seguendo questo randomnerdtutorial fino alla sezione "organizzare i file" inclusa. Davvero non potrei spiegarlo meglio.

Il codice:

Il mio codice può essere trovato su:

  • Uno schizzo Arduino DuvelBot.ino,
  • Una sottocartella di dati che contiene i file che verranno caricati sulla flash ESP utilizzando SPIFFS. Questa cartella contiene la pagina web che l'ESP servirà (index.html), un'immagine del logo che fa parte della pagina web (duvel.png) e un foglio di stile a cascata o un file CSS (style.css).

Per programmare il robot:

  • Collegare il convertitore USB-TTL come mostrato nello schema,
  • File -> Apri -> vai alla cartella in cui si trova DuvelBot.ino.
  • Cambia le tue credenziali di rete nello schizzo:

const char* ssid = "yourNetworkSSIDHere";const char* password = "yourPasswordHere";

  • Strumenti -> Scheda -> "AI-Thinker ESP-32 CAM" e seleziona la porta seriale appropriata per il tuo PC (Strumenti -> Porta -> qualcosa come /dev/ttyUSB0 o COM4),
  • Apri il monitor seriale nell'IDE Arduino, mentre premi il pulsante PROG (che abbassa IO0), accendi il robot,
  • Verificare sul monitor seriale che ESP32 sia pronto per il download,
  • Chiudere il monitor seriale (altrimenti il caricamento di SPIFFS fallisce),
  • Strumenti -> "Caricamento dati schizzo ESP32" e attendi che finisca,
  • Spegnere e riaccendere tenendo premuto il tasto PROG per tornare in modalità programmazione,
  • Premi la freccia "Carica" per programmare lo schizzo e attendi che finisca,
  • Aprire il monitor seriale e ripristinare ESP32 spegnendo/accendendo,
  • Una volta avviato, annota l'indirizzo IP (qualcosa come 192.168.0.121) e scollega il robot dal convertitore USB-TTL,
  • Apri un browser a questo indirizzo IP. Dovresti vedere l'interfaccia come nell'immagine.
  • Opzionale: imposta l'indirizzo mac di ESP32 su un indirizzo IP fisso nel router (dipende dal router come fare).

Questo è tutto! Continua a leggere se vuoi sapere come funziona…

Passaggio 3: come funziona

Ora veniamo alla parte interessante: come funziona tutto insieme?

Cercherò di spiegarlo passo dopo passo, ma tieni presente che Kajnjaps non è uno specialista di programmazione web. In effetti, imparare un po' di programmazione web era l'intera premessa per costruire DuvelBot. Se commetto errori evidenti, per favore lascia un commento!

Ok, dopo l'accensione di ESP32, come al solito in configurazione inizializza i GPIO, li associa ai timer PWM per il controllo del motore e dei LED. Vedi qui per ulteriori informazioni sul controllo del motore, è piuttosto standard.

Quindi la telecamera è configurata. Ho deliberatamente mantenuto la risoluzione piuttosto bassa (VGA o 640x480) per evitare una risposta lenta. Nota che la scheda AI-Thinker ESP32-CAM ha un chip ram seriale (PSRAM) che utilizza per memorizzare i fotogrammi della fotocamera con una risoluzione maggiore:

if(psramFound()) { Serial.println("PSRAM trovata."); config.frame_size = FRAMESIZE_VGA; config.jpg_quality = 12; config.fb_count = 2; //numero di framebuffer vedere: https://github.com/espressif/esp32-camera } else { Serial.println("nessuna PSRAM trovata."); config.frame_size = FRAMESIZE_QVGA; config.jpg_quality = 12; config.fb_count = 1; }

Quindi viene inizializzato il file system flash periferico seriale (SPIFFS):

//inizializza SPIFFS if(!SPIFFS.begin(true)) { Serial.println("Si è verificato un errore durante il montaggio di SPIFFS!"); Restituzione; }

SPIFFS si comporta come un piccolo filesystem su ESP32. Qui viene utilizzato per memorizzare tre file: la pagina web stessa index.html, un file stylesheet style.css in cascata e un logo immagine-p.webp

Successivamente ESP32 si connette al router (non dimenticare di impostare le credenziali prima del caricamento):

//cambia le credenziali del tuo router quiconst char* ssid = "yourNetworkSSIDHere";const char* password = "yourPasswordHere"; … //connettiti al WiFi Serial.print("Connessione al WiFi"); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { Serial.print('.'); ritardo (500); } // ora connesso al router: ESP32 ora ha l'indirizzo IP

Per fare effettivamente qualcosa di utile, avviamo un server web asincrono:

//crea un oggetto AsyncWebServer sulla porta 80AsyncWebServer server(80); … server.begin(); //inizia ad ascoltare le connessioni

Ora, se digiti l'indirizzo IP che è stato assegnato all'ESP32 dal router nella barra degli indirizzi del browser, l'ESP32 riceve una richiesta. Ciò significa che dovrebbe rispondere al client (tu o il tuo browser) servendogli qualcosa, ad esempio una pagina web.

L'ESP32 sa come rispondere, perché nella configurazione le risposte a tutte le possibili richieste consentite sono state registrate utilizzando server.on(). Ad esempio, la pagina Web principale o l'indice (/) vengono gestiti in questo modo:

server.on("/", HTTP_GET, (AsyncWebServerRequest *request){ Serial.println(" / richiesta ricevuta!"); request->send(SPIFFS, "/index.html", String(), false, processore); });

Quindi, se il client si connette, ESP32 risponde inviando il file index.html dal filesystem SPIFFS. Il processore dei parametri è il nome di una funzione che preelabora l'html e sostituisce eventuali tag speciali:

// Sostituisce i segnaposto nell'html come %DATA%// con le variabili che vuoi mostrare//

Dati: %DATI%

String processor(const String& var){ if(var == "DATI"){ //Serial.println("in processor!"); return String(dutyCycleNow); } return String();}

Ora, seziona la pagina web index.html stessa. In generale ci sono sempre tre parti:

  1. codice html: quali elementi devono essere visualizzati (pulsanti/testo/slider/immagini ecc.),
  2. codice di stile, in un file.css separato o in una sezione …: come dovrebbero apparire gli elementi,
  3. javascript a … sezione: come dovrebbe agire la pagina web.

Una volta che index.html viene caricato nel browser (che sa che è html a causa della riga DOCTYPE), viene eseguito in questa riga:

Questa è una richiesta per un foglio di stile CSS. La posizione di questo foglio è indicata in href="…". Quindi cosa fa il tuo browser? Bene, lancia un'altra richiesta al server, questa volta per style.css. Il server acquisisce questa richiesta, perché è stata registrata:

server.on("/style.css", HTTP_GET, (AsyncWebServerRequest *request){ Serial.println(" richiesta css ricevuta"); request->send(SPIFFS, "/style.css", "text/css "); });

Pulito eh? Per inciso, avrebbe potuto essere href="/some/file/on/the/other/side/of/the/moon", per tutto il tuo browser. Andrebbe a prendere quel file altrettanto felicemente. Non spiegherò il foglio di stile poiché controlla solo le apparenze, quindi non è molto interessante qui, ma se vuoi saperne di più, dai un'occhiata a questo tutorial.

Come appare il logo DuvelBot? In index.html abbiamo:

a cui l'ESP32 risponde con:

server.on("/duvel", HTTP_GET, (AsyncWebServerRequest *request){ Serial.println("ricevuta richiesta logo duvel!"); request->send(SPIFFS, "/duvel.png", "image-p.webp

..un altro file SPIFFS, questa volta un'immagine completa, come indicato da "image/png" nella risposta.

Ora veniamo alla parte davvero interessante: il codice per i pulsanti. Concentriamoci sul pulsante AVANTI:

INOLTRARE

Il nome è solo un nome per collegarlo al foglio di stile per personalizzare le dimensioni, il colore, ecc. Le parti importanti sono onmousedown="toggleCheckbox('forward')" e onmouseup="toggleCheckbox('stop') ". Questi costituiscono le azioni del pulsante (lo stesso per ontouchstart/ontouchend ma per i touchscreen/telefoni). Qui, l'azione del pulsante chiama una funzione toggleCheckbox(x) nella sezione javascript:

funzione toggleCheckbox(x){ var xhr = new XMLHttpRequest(); xhr.open("GET", "/" + x, true); xhr.send(); //potrebbe fare qualcosa anche con la risposta quando è pronta, ma non lo facciamo }

Quindi, premendo il pulsante avanti, si ottiene immediatamente la chiamata di toggleCheckbox ("avanti"). Questa funzione lancia quindi un XMLHttpRequest "GET", della posizione "/forward" che si comporta proprio come se avessi digitato 192.168.0.121/forward nella barra degli indirizzi del browser. Una volta che questa richiesta arriva all'ESP32, viene gestita da:

server.on("/forward", HTTP_GET, (AsyncWebServerRequest *request){ Serial.println("received /forward"); actionNow = FORWARD; request->send(200, "text/plain", "OK forward."); });

Ora ESP32 risponde semplicemente con un testo "OK avanti". Nota toggleCheckBox() non fa nulla con (o aspetta) questa risposta, tuttavia potrebbe come mostrato in seguito nel codice della fotocamera.

Di per sé durante questa risposta, il programma imposta solo una variabile actionNow = FORWARD, come risposta alla pressione del pulsante. Ora nel mainloop del programma, questa variabile viene monitorata con l'obiettivo di aumentare/diminuire il PWM dei motori. La logica è: finché abbiamo un'azione che non è STOP, far salire i motori in quella direzione fino a raggiungere un certo numero (dutyCycleMax). Quindi mantieni quella velocità, purché actionNow non sia cambiato:

void loop(){ currentMillis = millis(); if (currentMillis - previousMillis >= dutyCycleStepDelay) { // salva l'ultima volta che hai eseguito il ciclo previousMillis = currentMillis; //mainloop è responsabile dell'accelerazione/decelerazione dei motori if(actionNow != previousAction) { //ramp down, then stop, then change action and ramp up dutyCycleNow = dutyCycleNow-dutyCycleStep; if (dutyCycleNow <= 0) { //se dopo la decelerazione dc è 0, imposta la nuova direzione, inizia al minimo dutycycle setDir(actionNow); precedenteAzione = azioneOra; dutyCycleNow = dutyCycleMin; } } else //actionNow == previousAction ramp up, tranne quando la direzione è STOP { if (actionNow != STOP) { dutyCycleNow = dutyCycleNow+dutyCycleStep; if (dutyCycleNow > dutyCycleMax) dutyCycleNow = dutyCycleMax; } else dutyCycleNow = 0; } ledcWrite(pwmChannel, dutyCycleNow); //regola il dutycycle del motore }}

Questo aumenta lentamente la velocità dei motori, invece di lanciarsi a tutta velocità e versare il prezioso prezioso Duvel. Un ovvio miglioramento sarebbe quello di spostare questo codice in una routine di interruzione del timer, ma funziona così com'è.

Ora se rilasciamo il pulsante avanti, il tuo browser chiama toggleCheckbox('stop'), risultando in una richiesta di GET /stop. L'ESP32 imposta actionNow su STOP (e risponde con "OK stop."), che introduce il mainloop per far girare i motori.

E i LED? Stesso meccanismo, ma ora abbiamo uno slider:

Nel javascript viene monitorata l'impostazione dello slider, in modo tale che ad ogni modifica avvenga una chiamata per ottenere "/LED/xxx", dove xxx è il valore di luminosità a cui devono essere impostati i LED:

var slide = document.getElementById('slide'), sliderDiv = document.getElementById("sliderAmount"); slide.onchange = function() { var xhr = new XMLHttpRequest(); xhr.open("GET", "/LED/" + this.value, true); xhr.send(); sliderDiv.innerHTML = this.value; }

Nota che abbiamo usato document.getElementByID('slide') per ottenere l'oggetto slider stesso, che è stato dichiarato con e che il valore viene restituito a un elemento di testo con ad ogni modifica.

Il gestore nello schizzo cattura tutte le richieste di luminosità utilizzando "/LED/*" nella registrazione del gestore. Quindi l'ultima parte (un numero) viene divisa e convertita in un int:

server.on("/LED/*", HTTP_GET, (AsyncWebServerRequest *request){ Serial.println("ricevuta richiesta led!"); setLedBrightness((request->url()).substring(5).toInt ()); request->send(200, "text/plain", "OK Leds."); });

Simile a quanto descritto sopra, i pulsanti radio controllano le variabili che impostano i valori predefiniti PWM, in modo tale che DuvelBot possa guidare lentamente verso di te con la birra, attento a non versare quell'oro liquido, e tornare velocemente in cucina a prenderne dell'altro.

…Quindi, come viene aggiornata l'immagine della telecamera senza che tu debba aggiornare la pagina? Per questo usiamo una tecnica chiamata AJAX (Asynchronous JavaScript and XML). Il problema è che normalmente una connessione client-server segue una procedura fissa: client (browser) fa richiesta, server (ESP32) risponde, caso chiuso. Fatto. Non succede più niente. Se solo in qualche modo potessimo indurre il browser a richiedere regolarmente aggiornamenti da ESP32… ed è esattamente quello che faremo con questo pezzo di javascript:

setInterval(function(){ var xhttp = new XMLHttpRequest(); xhttp.open("GET", "/CAMERA", true); xhttp.responseType = "blob"; xhttp.timeout = 500; xhttp.ontimeout = function(){}; xhttp.onload = function(e){ if (this.readyState == 4 && this.status == 200) { //vedi: https://stackoverflow.com/questions/7650587/using… // https://www.html5rocks.com/en/tutorials/file/xhr2/ var urlCreator = window. URL || window.webkitURL; var imageUrl = urlCreator.createObjectURL(this.response); //crea un oggetto dal blob document.querySelector("#camimage").src = imageUrl; urlCreator.revokeObjectURL(imageurl) } }; xhttp.send(); }, 250);

setInterval prende come parametro una funzione e la esegue ogni tanto (qui una volta ogni 250ms risulta in 4 frame/secondo). La funzione che viene eseguita richiede un "blob" binario all'indirizzo /CAMERA. Questo è gestito da ESP32-CAM nello schizzo come (da Randomnerdtutorials):

server.on("/CAMERA", HTTP_GET, (AsyncWebServerRequest *request){ Serial.println("richiesta telecamera ricevuta!"); camera_fb_t * fb = NULL; //esp_err_t res = ESP_OK; size_t _jpg_buf_len = 0; uint8_t * _jpg_buf = NULL; //cattura un frame fb = esp_camera_fb_get(); if (!fb) {Serial.println("Impossibile acquisire il frame buffer");return;} if(fb->format != PIXFORMAT_JPEG)/ /già in questo formato da config{ bool jpeg_converted = frame2jpg(fb, 80, &_jpg_buf, &_jpg_buf_len); esp_camera_fb_return(fb); fb = NULL; if(!jpeg_converted){Serial.println("Compressione JPEG fallita");return; } } else{ _jpg_buf_len = fb->len; _jpg_buf = fb->buf; } //Serial.println(_jpg_buf_len); //invia la richiesta dell'immagine formattata->send_P(200, "image/jpg", _jpg_buf, _jpg_buf_len); //pulisci if(fb){ esp_camera_fb_return(fb); fb = NULL; _jpg_buf = NULL; } else if(_jpg_buf){ free(_jpg_buf); _jpg_buf = NULL; } });

Le parti importanti sono ottenere il frame fb = esp_camera_fb_get() convertendolo in un-j.webp

La funzione javascript attende quindi l'arrivo di questa immagine. Quindi ci vuole solo un po' di lavoro per convertire il "blob" ricevuto in un URL che può essere utilizzato come fonte per aggiornare l'immagine con nella pagina html.

uff, abbiamo finito!

Passaggio 4: idee e avanzi

Idee e avanzi
Idee e avanzi

L'obiettivo di questo progetto per me era imparare la programmazione web quanto basta per interfacciare l'hardware al web. Sono possibili diverse estensioni a questo progetto. Ecco alcune idee:

  • Implementa lo streaming "reale" della videocamera come spiegato qui e qui e spostalo su un secondo server come spiegato qui sullo stesso ESP32, ma sull'altro core della CPU, quindi importa il camerastream nell'html servito dal primo server utilizzando un …. Ciò dovrebbe comportare aggiornamenti della fotocamera più rapidi.
  • Usa la modalità punto di accesso (AP) in modo che il robot sia più autonomo come spiegato qui.
  • Espandi con la misurazione della tensione della batteria, le capacità di deep-sleep ecc. Questo è un po' difficile al momento perché l'AI-Thinker ESP32-CAM non ha molti GPIO; necessita di espansione tramite uart e ad esempio uno slave arduino.
  • Trasformati in un robot in cerca di gatti che espelle di tanto in tanto dei bocconcini per gatti premendo un grosso pulsante con la zampa, trasmetti in streaming tonnellate di belle foto di gatti durante il giorno…

Si prega di commentare se ti è piaciuto o hai domande e grazie per la lettura!

Consigliato: