Full text
COCHE DE JUGUETE TELEDIRIGIDO REMOTE CONTROL TOY CAR TRABAJO DE FIN DE GRADO CURSO 2023-2024 AUTOR Marcos Docampo Prieto-Puga DIRECTOR Juan Carlos Fabero Jiménez GRADO EN INGENIERÍA INFORMÁTICA FACULTAD DE INFORMÁTICA UNIVERSIDAD COMPLUTENSE DE MADRID
COCHE DE JUGUETE TELEDIRIGIDO REMOTE CONTROL TOY CAR TRABAJO DE FIN DE GRADO EN INGENIERÍA INFORMÁTICA AUTOR Marcos Docampo Prieto-Puga DIRECTOR Juan Carlos Fabero Jiménez CONVOCATORIA: SEPTIEMBRE 2024 GRADO EN INGENIERÍA INFORMÁTICA FACULTAD DE INFORMÁTICA UNIVERSIDAD COMPLUTENSE DE MADRID 1 DE SEPTIEMBRE DE 2024 2
Dedicatoria A mis padres, por apoyar siempre mis decisiones, queriendo que encuentre no otra cosa que la felicidad. 3
Agradecimientos A Juan Carlos, mi tutor, por soportar la incertidumbre de la falta de feedback. A mis padres, Alfonso y Beatriz, por hacer posible que esté haciendo este TFG. A mis amigos por sacarme de casa cuando llevaba días encerrado trabajando. A mi hermana, Ariadna, y a mis gatos por hacerme compañía en esos días. 4
Resumen Coche de juguete teledirigido Este trabajo se centra en el desarrollo de un coche teledirigido basando su creación en las opciones elegidas que se expondrán durante la memoria. El coche teledirigido tiene como imposición propia el uso de una cámara con la que se pueda ver desde dentro del habitáculo como si de un piloto se tratase. El manejo se hará desde una página web que tendrá los controles y la visión de la cámara. En la introducción, se presentarán las razones que motivan la realización del proyecto, los objetivos que se pretenden alcanzar y un plan de trabajo que guiará su desarrollo. A partir de ahí, se exploran distintas opciones para determinar la base sobre la que se creará el coche teledirigido, considerando las siguientes alternativas: la Raspberry Pi, Arduino y ESP-32 CAM. Una vez definido sobre qué se programará la lógica del coche, se lleva a cabo un proceso de aprendizaje centrado en Arduino. Esto se debe a que la base sobre la que se parte es muy baja, y no existen conocimientos previos con ninguna de las tres opciones. En esta etapa, se describen los materiales empleados y se realizan diversos proyectos guiados iniciales para familiarizarse con la plataforma, tales como la programación de LED, el uso de resistencias LDR, sensores de temperatura, servomotores, sensores de inclinación y el control de motores mediante un puente-H. Estos ejercicios prácticos permiten adquirir conocimientos fundamentales necesarios para avanzar en el desarrollo del coche. Como se mencionará más adelante, solo se han incluido en la memoria los que aportan ideas para el futuro coche a pesar de que en realidad han sido muchos más los proyectos de iniciación a Arduino. El aprendizaje continúa con la exploración de los SoC (system-on-chip) ESP32 y ESP32-CAM, donde se detalla la configuración del entorno de desarrollo y las pruebas realizadas con diferentes componentes: el flash, los servomotores, los motores y la creación de un servidor web. Esta parte del proyecto es crucial para comprender cómo integrar distintos módulos y tecnologías, asegurando un desarrollo correcto de cara a la implementación conjunta para el coche teledirigido. La construcción final del coche se produce gracias a la aplicación de los conocimientos adquiridos y la selección y descarte de materiales electrónicos y estructurales. A partir de una estructura de coche montada con bloques que simulan el estilo LEGO® Technic™, se verá cómo integrar los componentes electrónicos para intentar construir un modelo viable. Aquí se describirá el proceso de montaje y del desarrollo del código necesario para el funcionamiento del coche, basado en el paradigma de la POO (programación orientada a objetos) para el control del servidor web, la cámara, el motor y el servo. Finalmente, el trabajo incluye una bibliografía que reúne todas las fuentes consultadas, aportando el fundamento teórico y técnico necesario para la realización del proyecto. Palabras clave Arduino, cámara, coche, ESP32-Cam, POO, protoboard, SoC (system-on-circuit), motor DC, servomotor, web server. 5
Abstract Remote control toy car This work focuses on the development of a remote-controlled car, basing its creation on the chosen options that will be presented on this document. The remote-controlled car has my own imposition on the use of a camera with which it will be possible to see from inside the cockpit as if it were a pilot. The management system will be controlled from a web page that will have the simulated controller and a window to display the video. In the introduction you will see the reasons that motivates this project, the objectives to be achieved and a work plan to guide the development. From there, different options will be considered in order to determine the basis on which the remote controlled car will be created, following these alternatives: the Raspberry Pi, Arduino and ESP-32 CAM. Once it is defined what the car's logic will be programmed on, a learning process focused on Arduino is carried out. This is because the basis on which I start is very low, and there is no prior knowledge with any of the three options. In this stage, I describe the materials that are used in and various initial guided projects are carried out to become familiar with the platform, such as the programming of LEDs, the use of phototransistors, temperature sensors, servo motors, tilt sensors and the control of motors using a H-bridge. These exercises will allow me to acquire the fundamental knowledge necessary to advance in the development of the car. It will be mentioned later, but only those that provide ideas for the future car have been included in the report, although in reality there have been many more. The learning continues with the exploration of the SoC (system-on-chip) ESP32 y ESP32-CAM, where the report details the configuration of the environment and the tests done with different components such as the flash, servo motors, motors and the creation of a web page. This chapter of the project is capital to understand how to integrate different parts, modules and technologies, ensuring the correct development towards the future remote-controlled car. The final chapter contains the assembly of the car, which is produced thanks to the acknowledgement acquired and the selection or discard of materials, both electronic and structurals. Starting with a car structure assembled with blocks that simulate LEGO® Technic™, I will try to join all the components to create a viable car. In this memo you can see the progress of building and developing the necessary code for the correct operation of the car. This code is based on the OOP (object oriented programming) paradigm which allows the encapsulation and abstraction of the handling of the camera, motor and servo motor. Finally, the memo also includes a bibliography that brings together all the sources of information consulted, which provided the foundation of the theoretical and technical knowledge necessary to carry out this project. Keywords Arduino, camera, car, ESP32-Cam, POO, protoboard, SoC (system-on-circuit), motor DC, servomotor, web server. 6
Índice de contenidos Dedicatoria..............................................................................................................................................3 Agradecimientos.....................................................................................................................................3 Resumen..................................................................................................................................................5 Coche de juguete teledirigido............................................................................................................5 Palabras clave.................................................................................................................................... 5 Abstract...................................................................................................................................................5 Remote control toy car...................................................................................................................... 6 Keywords...........................................................................................................................................6 Introducción..........................................................................................................................................11 Motivación.......................................................................................................................................11 Motivation....................................................................................................................................... 12 Objetivos..........................................................................................................................................13 Objectives.........................................................................................................................14 Plan de trabajo................................................................................................................................. 15 Work plan........................................................................................................................................ 16 El coche teledirigido.............................................................................................................................17 1. Capítulo I: Elección del formato................................................................................................. 17 1.1. Opciones.............................................................................................................................17 1.1.1. Raspberry..................................................................................................................17 1.1.2. Arduino.....................................................................................................................17 1.1.3. ESP-32 CAM............................................................................................................17 1.2. Decisión final.....................................................................................................................17 2. Capítulo II: Aprendiendo Arduino.............................................................................................. 18 2.1. Materiales...........................................................................................................................18 2.2. Proyectos de aprendizaje....................................................................................................20 2.2.1. LED.......................................................................................................................... 20 2.2.2. Resistencias LDR..................................................................................................... 23 2.2.3. Sensor de temperatura.............................................................................................. 26 2.2.4. Servomotor............................................................................................................... 29 2.2.5. Piezoeléctrico........................................................................................................... 32 2.2.6. Sensor de inclinación................................................................................................34 2.2.7. Puente-H...................................................................................................................37 3. Capítulo III: Aprendiendo sobre ESP32 y ESP32-CAM.............................................................41 3.1. Disposición para desarrollo................................................................................................41 3.1.1. Nuevo material......................................................................................................... 41 3.1.2. Construcción del entorno..........................................................................................43 3.2. Pruebas...............................................................................................................................44 3.2.1. Flash......................................................................................................................... 44 3.2.2. Servomotor............................................................................................................... 45 3.2.3. Motor........................................................................................................................47 7
3.2.4. Web server................................................................................................................50 4. Capítulo IV: Construyendo el coche............................................................................................53 4.1. Materiales...........................................................................................................................53 4.1.1. Componentes electrónicos........................................................................................53 4.1.2. Componentes estructurales.......................................................................................54 4.2. Código................................................................................................................................55 4.2.1. main.ino....................................................................................................................56 4.2.2. MyWebServer.h........................................................................................................57 4.2.3. MyCamera.h.............................................................................................................61 4.2.4. MyMotor.h................................................................................................................62 4.2.5. MyServo.h................................................................................................................ 64 4.2.6. webcode.h.................................................................................................................66 4.3. Montaje.............................................................................................................................. 69 Conclusiones y trabajo futuro.............................................................................................................77 Conclusions and future work..............................................................................................................78 Bibliografía........................................................................................................................................... 79 8
Índice de figuras Figura 1-1. El origen del proyecto......................................................................................................... 11 Figura 1-2. The inspiration of the project.............................................................................................. 12 Figura 2-1. Circuito con LED................................................................................................................ 21 Figura 2-2. Circuito con resistencia LDR.............................................................................................. 24 Figura 2-3. Circuito con sensor de temperatura.....................................................................................27 Figura 2-4. Circuito con servomotor......................................................................................................30 Figura 2-5. Circuito con zumbador piezoeléctrico.................................................................................33 Figura 2-6. Circuito con sensor de inclinación...................................................................................... 35 Figura 2-7. Circuito con puente-H......................................................................................................... 38 Figura 4-1. Manual de construcción del coche...................................................................................... 54 Figura 4-2. Esquema de funcionamiento en el archivo principal...........................................................55 Figura 4-3. Diagrama de la clase MyWebServer................................................................................... 57 Figura 4-4. Diagrama de la clase MyCamera.........................................................................................61 Figura 4-5. Diagrama de la clase MyMotor...........................................................................................62 Figura 4-6. Diagrama de la clase MyServo............................................................................................64 Figura 4-7. Base del coche. Transmisión y dirección............................................................................ 69 Figura 4-8. Sistema montado en la protoboard......................................................................................70 Figura 4-9. Sistema montado en la protoboard......................................................................................71 Figura 4-10. Sistema con el ESP32-CAM montado.............................................................................. 72 Figura 4-11. Servo conectado a la dirección..........................................................................................73 Figura 4-12. Motor con engranaje de juguete conectado a la transmisión.............................................74 Figura 4-13. Alimentación del coche: 2 pilas de 9V..............................................................................75 Figura 4-14. Coche terminado con la capota abierta..............................................................................76 Figura 4-15. Coche de juguete teledirigido............................................................................................76 9
Work plan The goal is to complete the entire project within a maximum of 5 months of effective working time. That is, not from the start to the end date, but rather defined in weeks of actual work. I will break this down in a table with four columns: phase (P) of simultaneously compatible work, task, description, and duration (T) in weeks. P Trabajo Descripción T 1* Introduction Define the motivation, objectives, and project plan. 1 Choice of Format Explore and decide on the most suitable hardware platform to build the car. Research options, weigh pros and cons, and make a final decision. 2 2 Learning Arduino Complete the 15 projects from the Arduino starter pack, learning about components and their functionality. 4 - 6 3 Learning ESP32 Learn to use the ESP32 and ESP32-CAM platforms to integrate them into the project by replicating the Arduino projects in the new environment. 3 - 5 4 Code Development Design, develop, and test the code needed for the car's operation, based on the object-oriented programming paradigm (OOP). 3 - 4 Circuit Assembly Build a portable circuit, based on the created diagrams, that can be integrated into the car. 2 Car Assembly Assemble and modify the car to integrate the circuit within the car’s structure. 1 - 2 5 Testing Conduct functional tests to check and correct the behavior of the applied circuit. 2 6 Report Final preparation of the report and presentation. 1 Tabla I-2. Work plan [*] - Phase 1 is a blocking phase for the rest of the planning. Therefore, it is not possible to proceed with further planning until it is completed. 16
El coche teledirigido 1. Capítulo I: Elección del formato 1.1. Opciones 1.1.1. Raspberry Fue mi idea inicial puesto que he visto que es a efectos prácticos un ordenador mini, con una gran capacidad de cómputo y procesamiento. Se le puede meter un sistema operativo completo y hacer uso de componentes externos conectados a la placa. Tiene interfaz gráfica, soporte para múltiples lenguajes de programación y, muy importante, una tarjeta de red para poder manejar el coche de forma inalámbrica. 1.1.2. Arduino Mi plan B era usar una placa de Arduino, puesto que durante la exploración es algo que había visto que era posible. Tienen un bajo consumo de energía, son muy programables para distintas tareas y está diseñado para control de hardware en tiempo real, o sea, muy útil para motores y cámaras. Posee además un entorno de programación dedicado para subir el material y pueden añadirse módulos para que tengan conexión Wi-Fi. 1.1.3. ESP-32 CAM Esta fue la recomendación de mi tutor. Yo no sabía que existía así que exploré sobre ello. Este SoC es capaz de transmitir vídeo gracias a su cámara integrada y capacidades Wi-Fi. Es bastante barata (menos de 5 €), es bastante pequeña y tiene un bajo consumo de energía. Y se puede programar en el Arduino IDE, PlatformIO, o directamente en C++ o Python. 1.2. Decisión final El primer descarte sería la Raspberry. Todo lo bueno que tiene esta placa es lo que la hace innecesaria para este proyecto. Usar una Raspberry para controlar un coche teledirigido sería un malgasto de potencia, o sea, “matar moscas a cañonazos”. Entre las dos opciones que restaban, decidí apoyarme en la experiencia de mi tutor, Juan Carlos debido a mi bajo conocimiento en la materia y su más que sobrada y contrastada experiencia. En cualquier caso, ESP32-CAM es un aparato dedicado con compatibilidad con los programas de desarrollo de Arduino. Bajo consumo y mucha mayor potencia que una placa de Arduino, además de conectividad. Un factor también diferencial es el precio, puesto que sé que habrá que comprar un montón de material y debo optimizar al máximo el presupuesto que tengo. 17
2. Capítulo II: Aprendiendo Arduino Para el aprendizaje de circuitos opté por comprar el Starter Kit de ArduinoⓇporque era la manera más directa para aprender desde cero. El libro que trae es de un nivel básico y está explicado para principiantes. Enseña de una manera muy práctica y dinámica e incluye una placa Arduino, un montón de componentes electrónicos (sensores, LED, resistencias, motores…). Además como lo venden en tiendas no tengo que esperar a que venga por correo, ahorrando tiempo y dinero. La idea de estos proyectos es replicarlos más tardes con el ESP32 y así comprender su funcionamiento más profundamente. 2.1. Materiales A continuación se muestra una tabla con los componentes que incluye el pack: Nombre Cantidad Especificaciones Base de plástico 1 - Cable USB 1 - Cables de prototipado 2 - Cables rígidos de prototipado 70 5 mm ~ 50 mm Condensadores 3 100 µF Condensadores 5 100 nF Condensadores 5 100 pF Conector de pila 9V 1 - Diodos 7 - Driver de motores 1 Puente en H LED 1 Blanco brillante LED 1 RGB LED 8 Rojo LED 8 Verde LED 8 Amarillo LED 3 Azul Motor DC pequeño 1 6 V ~ 9 V Optoacopladores 2 - Pantalla LCD 1 16x2 caracteres 18
Nombre Cantidad Especificaciones Piezoeléctrico 1 - Placa Arduino UNO 1 R3 Placa de prototipado 1 29x10 pines Plásticos transparentes 3 RGB de 2x10 mm Potenciómetros 3 10 kΩ Pulsadores 10 - Resistencias 22 220 Ω Resistencias 7 560 Ω Resistencias 7 1 kΩ Resistencias 7 4,7 kΩ Resistencias 22 10 kΩ Resistencias 7 1 MΩ Resistencias 7 10 MΩ Sensor de inclinación 1 - Sensor de luz 6 Sensor de temperatura 1 - Servomotor pequeño 1 - Tira de pines macho 1 40x1 pines Transistores 2 MOSFET Transistores 5 NPN Tabla 2-1. Materiales de aprendizaje 19
2.2. Proyectos de aprendizaje Teniendo en cuenta que se empezaba de cero, leí e hice todos los proyectos del libro. Sin embargo, para simplificar incluiré en la memoria sólo los estrictamente necesarios y que me dieron ideas para su implementación en el coche. En cada apartado se podrá leer cuál es la idea que saqué para el vehículo, además de una breve explicación del proyecto, la lista de materiales necesarios y el código con la explicación de funciones. Los proyectos, que ya se pudieron leer en el índice, son: - LED. Primer contacto - Resistencia LDR.. Faros fotosensibles - Sensor de temperatura. Temperatura interior. - Piezoeléctrico. Cláxon. - Sensor de inclinación. Suspensión - Puente-H. Motor DC. 2.2.1. LED El objetivo es aprender sobre la lógica de control con botones, el uso de entradas digitales y la interacción básica con LED. El proyecto trata de simular el funcionamiento de un semáforo de botón utilizando tres LED de los colores característicos y un botón. El pulsador que activa la secuencia de LED, que pasará de rojo a amarillo y luego a verde con un lapso de 0,25 s. Este proyecto lo incluyo para que se pueda ser consciente de la base de la que se parte, puesto que esta es la primera vez que programaría sobre un microcontrolador. Para este circuito fueron necesarios los siguientes materiales: Nombre Cantidad Especificaciones Básicas Arduino UNO Ⓡ 1 R3 Cable USB 1 - Cables de prototipado 2 - Cables rígidos de prototipado - Usar a voluntad Protoboard 1 - Específicas LED 3 Rojo, amarillo y verde Pulsadores 1 - Resistencias 4 220 Ω (3 uds.), 10 kΩ (1 ud.) Tabla 2-2. Materiales del circuito con LED 20
Figura 2-1. Circuito con LED. Diagrama sacado de [1]. El botón pulsador se conecta al pin digital de Arduino ‘2’ y a una resistencia pull-down para asegurar que el estado por defecto sea bajo (LOW). Los LED se conectan a los pines ‘3’, ‘4’ y ‘5’ y se usan resistencias para limitar la corriente. El código de este circuito, sacado de [2], es el siguiente: int switchState =0; void setup() { pinMode(3, OUTPUT); pinMode(4, OUTPUT); pinMode(5, OUTPUT); pinMode(2, INPUT); } void loop() { switchState =digitalRead(2); if (switchState == LOW) { digitalWrite(3, HIGH); digitalWrite(4, LOW); digitalWrite(5, LOW); } else { digitalWrite(3, LOW); digitalWrite(4, LOW); digitalWrite(5, HIGH); delay(250); digitalWrite(4, HIGH); digitalWrite(5, LOW); 21
delay(250); } } Guía de funciones nuevas usadas: ●void setup(): se llama una sola vez para inicializar las variables. ●void pinMode(int pin, int mode): configura el pin como entrada o salida. ○pin: número del pin cuyo modo se ha de configurar. ○mode: INPUT, OUTPUT, o INPUT_PULLUP. ●void loop(): itera sobre sí mismo una y otra vez. ●int digitalRead(int pin): lee el valor de un pin especificado. ○pin: número del pin de donde se quiere leer la entrada. ○return: 0 (LOW) o 1 (HIGH). ●void digitalWrite(int pin, int value): escribe en el pin el valor querido. ○pin: número del pin cuyo valor se ha de configurar. ○value: 0 (LOW) o 1 (HIGH). ●void delay(int ms): pausa el programa un tiempo en milisegundos. ○ms: número de milisegundos a esperar. 22
2.2.2. Resistencias LDR El objetivo es aprender sobre la interacción analógica, el control de LED RGB y cómo combinar colores de manera dinámica con Arduino. El proyecto es crea una luz que puede cambiar de color ajustando la intensidad de los componentes rojo, verde y azul de un LED RGB mediante la absorcion de la luz de tres resistencias LDR, cada uno asignado a un color (R-G-B). Cada resistencia LDR controla la intensidad de un color del LED RGB: uno para el rojo, otro para el verde y otro para el azul. Tapando las resistencias LDR se puede ajustar el brillo de cada color, lo que permite mezclar los colores y crear diferentes tonos. El LED RGB combina estos colores para mostrar una variedad de colores. Lo interesante de este proyecto y aplicable para el vehículo final es poder usar las luces de cruce cuando esté oscuro. Para este circuito fueron necesarios los siguientes materiales: Nombre Cantidad Especificaciones Básicas Arduino UNO Ⓡ 1 R3 Cable USB 1 - Cables de prototipado 2 - Cables rígidos de prototipado - Usar a voluntad Protoboard 1 - Específicas LED 3 RGB Resistencias 6 220 Ω (3 uds.), 10 kΩ (3 uds.) Resistencias LDR 3 - Tabla 2-3. Materiales del circuito con resistencia LDR. 23
Figura 2-2. Circuito con resistencia LDR.. Diagrama sacado de [3]. El LED RGB se conecta a tres pines PWM de Arduino: ‘9’, ‘10’ y ‘11’. Se utilizan resistencias para limitar la corriente de cada componente de color. Las resistencias LDR se conectan a tres pines analógicos de Arduino: ‘A0’, ‘A1’ y ‘A2’. Los extremos de las resistencias LDR van a GND y 5V, y el pin central se conecta a las entradas analógicas. El código de este circuito, basado en [4], es el siguiente: const int gLEDp =9; const int rLEDp =10; const int bLEDp =11; const int rSensor =A0; const int gSensor =A1; const int bSensor =A2; int rValue =0; int gValue =0; int bValue =0; int rSensVal =0; int gSensVal =0; int bSensVal =0; void setup() { Serial.begin(9600); pinMode(gLEDp, OUTPUT); pinMode(rLEDp, OUTPUT); pinMode(bLEDp, OUTPUT); } void loop() { 24
rSensVal =analogRead(rSensor); delay(5); gSensVal =analogRead(gSensor); delay(5); bSensVal =analogRead(bSensor); Serial.print("Raw Sensor Values \t red: "); Serial.print(rSensVal); Serial.print("\t green: "); Serial.print(gSensVal); Serial.print("\t blue: "); Serial.println(bSensVal); rValue =rSensVal /4; gValue =gSensVal /4; bValue =bSensVal /4; Serial.print("Mapped Sensor Values \t red: "); Serial.print(rValue); Serial.print("\t green: "); Serial.print(gValue); Serial.print("\t blue: "); Serial.println(bValue); analogWrite(rLEDp, rValue); analogWrite(gLEDp, gValue); analogWrite(bLEDp, bValue); } Guía de funciones nuevas usadas: ●Serial.begin(int baud): establece los baudios del monitor en serie. ○baud: velocidad. Usualmente 9600 o 115200 baudios. ●int analogRead(int pin): lee el valor analógico de un pin específico. ○pin: número del pin de donde se quiere leer la entrada. ○return: valor de 10 bits (0-1024) ●Serial.print(string text): plasma en el monitor en serie un texto. ○text: el mensaje que se quiere enviar. ●void analogWrite(int pin, int value): plasma un valor analógico en un pin. ○pin: número del pin cuyo valor se ha de configurar. ○value: señal PWM (pulse width modulation) de 0 a 255. 25
2.2.5. Piezoeléctrico El objetivo de este proyecto es usar sensores analógicos para capturar datos del entorno y convertirlos en señales sonoras mediante un zumbador piezoeléctrico. Al cubrir o destapar un sensor fotosensible, la frecuencia del sonido cambia y genera distintos sonidos. Este proyecto podría servir para implementar un claxon en el vehículo. Para este circuito fueron necesarios los siguientes materiales: Nombre Cantidad Especificaciones Básicas Arduino UNO Ⓡ 1 R3 Cable USB 1 - Cables de prototipado 2 - Cables rígidos de prototipado - Usar a voluntad Protoboard 1 - Específicas Piezoeléctrico 1 100 μF Resistencia 1 10 kΩ Resistencia LDR 1 - Tabla 2-6. Materiales del circuito con zumbador piezoeléctrico 32
Figura 2-5. Circuito con zumbador piezoeléctrico. Diagrama sacado de [9]. La resistencia LDR va conectada a 5V y al pin analógico de ‘A0’, y el piezoeléctrico va conectado al pin digital ‘8’ y el pin negativo a GND. El código del circuito, sacado de [10], es el siguiente: int sensVal; int sensLow =1023; int sensHigh =0; const int ledPin =13; void setup() { pinMode(ledPin, OUTPUT); digitalWrite(ledPin, HIGH); while (millis() <5000) { sensVal =analogRead(A0); if (sensVal >sensHigh) { sensHigh =sensVal; } if (sensVal <sensLow) { sensLow =sensVal; } } digitalWrite(ledPin, LOW); Serial.println("Setup finished"); } void loop() { sensVal =analogRead(A0); int pitch =map(sensVal, sensLow, sensHigh, 50,4000); tone(8, pitch, 20); delay(10); } Guía de funciones nuevas usadas: ●unsigned long millis(): devuelve el valor en milisegundos desde que empezó el programa. ○return: un valor entero tipo long que representa los milisegundos desde el inicio. ●void tone(int pin, unsigned int frequency, unsigned long duration): genera una onda. ○pin: el número de pin del piezoeléctrico. ○frequency: la frecuencia a la que va a vibrar el piezoeléctrico. ○duration: el tiempo que va a sonar en milisegundos. 33
2.2.6. Sensor de inclinación El objetivo de este proyecto es controlar múltiples salidas digitales (LED) mediante la detección de un estado utilizando un sensor de inclinación. El sensor de inclinación detecta si el dispositivo está inclinado y basado en la orientación detectada, los LED se encienden o apagan de manera secuencial. Este proyecto es interesante porque podría servir para manejar digitalmente la suspensión del vehículo. Para este circuito fueron necesarios los siguientes materiales: Nombre Cantidad Especificaciones Básicas Arduino UNO Ⓡ 1 R3 Cable USB 1 - Cables de prototipado 2 - Cables rígidos de prototipado - Usar a voluntad Protoboard 1 - Específicas LED 6 Rojos Resistencia 7 220 Ω (6 uds.) y 10 kΩ (1 ud.) Sensor de inclinación 1 - Tabla 2-7. Materiales del circuito con sensor de inclinación 34
Figura 2-6. Circuito con sensor de inclinación. Fotografía de [11] Para el funcionamiento se conectan los LED a pines digitales ‘2’ a ‘7’, cada uno con una resistencia de 220 Ω en serie para limitar la corriente y el sensor se conecta al pin digital ‘8’ y a GND. El código del circuito, basado en [12], es el siguiente: const int switchPin =8; unsigned long prevTime =0; int switchState =0; int prevSwitchState; int led =2; long interval =1000; void setup() { for(int x=2; x <8; x++) { pinMode(x, OUTPUT); } pinMode(switchPin, INPUT); } void loop() { unsigned long currentTime =millis(); if (currentTime -prevTime >interval) { prevTime =currentTime; digitalWrite(led, HIGH); 35
led++; } if (led == 7) { // Apagar } // Lectura del sensor (pin 8) switchState =digitalRead(switchPin); if (switchState != prevSwitchState) { for(int x=2; x <8; x++) { digitalWrite(led, LOW); } led =2; prevTime =currentTime; } prevSwitchState =switchState; } 36
2.2.7. Puente-H El objetivo de este proyecto es controlar un motor DC utilizando señales PWM gestionadas mediante un puente-H. El potenciómetro transmite un valor analógico a la placa y este traduce la señal para controlar la velocidad de rotación del motor. El circuito tiene dos botones, uno que enciende y apaga el motor, y otro que invierte la señal del motor para que vaya hacia delante yhacia detrás. Sendas funcionalidades serán útiles en el planteamiento del coche. Para este circuito fueron necesarios los siguientes materiales: Nombre Cantidad Especificaciones Básicas Arduino UNO Ⓡ 1 R3 Cable USB 1 - Cables de prototipado 2 - Cables rígidos de prototipado - Usar a voluntad Protoboard 1 - Específicas Batería 1 9V Conector de batería 1 - Motor 1 DC Potenciómetro 1 - Puente-H 1 L293D Pulsador 2 - Resistencia 2 10 kΩ Tabla 2-8. Materiales del circuito con puente-H 37
Figura 2-7. Circuito con puente-H. Fotografía de [13] Para el funcionamiento hay que tener en cuenta como opera el puente-H. Este circuito integrado se usa para controlar motores DC o paso a paso. En este caso se usa un motor DC, a traves del manejo de señales digitales convertidas en el Arduino UNO. Los pines en su numeración van primero la columna izquierda de arriba a abajo y luego la columna derecha de abajo a arriba, y los necesarios para su uso son los siguientes: Pin Nombre Descripción 1 Enable 1,2 Activación de la señal (ON-OFF) 2 Input 1 Entrada del motor 1 3 Output 1 Salida del motor (5V) 4 GND - 5 GND - 6 Output 2 Salida del motor (GND) 7 Input 2 Entrada del motor 2 8 Vcc2 Alimentación del motor a 9V 16 Vcc1 Alimentación estándar a 5V Tabla 2-9. Pines del puente-H El código del circuito, basado en [14], es el siguiente: const int controlPin1 =2; 38
const int controlPin2 =3; const int enablePin =9; const int directionSwitchPin =4; const int onOffSwitchStateSwitchPin =5; const int potPin =A0; int onOffSwitchState =0; int previousOnOffSwitchState =0; int directionSwitchState =0; int previousDirectionSwitchState =0; int motorEnabled =0; int motorSpeed =0; int motorDirection =1; void setup() { pinMode(directionSwitchPin, INPUT); pinMode(onOffSwitchStateSwitchPin, INPUT); pinMode(controlPin1, OUTPUT); pinMode(controlPin2, OUTPUT); pinMode(enablePin, OUTPUT); digitalWrite(enablePin, LOW); } void loop() { onOffSwitchState =digitalRead(onOffSwitchStateSwitchPin); delay(1); directionSwitchState =digitalRead(directionSwitchPin); motorSpeed =analogRead(potPin)/4; if (onOffSwitchState != previousOnOffSwitchState) { if (onOffSwitchState == HIGH) { motorEnabled = !motorEnabled; } } if (directionSwitchState != previousDirectionSwitchState) { if (directionSwitchState == HIGH) { motorDirection = !motorDirection; } } if (motorDirection == 1) { digitalWrite(controlPin1, HIGH); digitalWrite(controlPin2, LOW); } 39
else { digitalWrite(controlPin1, LOW); digitalWrite(controlPin2, HIGH); } if (motorEnabled == 1) { analogWrite(enablePin, motorSpeed); } else { analogWrite(enablePin, 0); } previousOnOffSwitchState =onOffSwitchState; previousDirectionSwitchState =directionSwitchState; } 40
3. Capítulo III: Aprendiendo sobre ESP32 y ESP32-CAM Como se mencionó al principio, para este proyecto se decidió hacer uso del SoC (system-on-chip) ESP32 para practicar debido a su capacidad de procesamiento, pequeño tamaño y conectividad Wi-Fi. Además, su versión ESP32-CAM, a pesar de tener menos pines incluye un módulo para el uso de una cámara, imprescindible para el vehículo. 3.1. Disposición para desarrollo 3.1.1. Nuevo material Esta es una lista del material que fue comprado. No hay materiales usados de existencias propias pues yo nunca había hecho uso de este tipo de material. La mayoría fue comprado a lo largo de todo el tiempo de proyecto, incluyendo los que se tuvieron que comprar de forma imprevista. Nombre Cantidad Especificaciones Adaptador de fuente de alimentación 3,3V/5V 5 MB102 Breadboard Adaptador FTDI 3 Cabezal de 6 pines macho Alfombrilla de corte verde A3 1 44×30 cm, 5 capas Alicates cortacables 1 - Antena externa 1 - Bobina de estaño 2 - Cables de prototipado hembra 120 Colores variados Cables de prototipado macho 240 Colores variados Cable micro-USB 2 - Cable mini-USB 1 - Cables rígidos de prototipado 540 14 longitudes distintas Cámara compatible con ESP32-CAM 3 OV2640 Coche montable con bloques de construcción 1 Similar a los LEGO Conector de pila 9V 12 Marca YIXISI ESP32-CAM 1 - ESP32-CAM con ESP32-CAM-MB 2 Con módulo de programación ESP32-WROOM-32 1 - Módulo de bajada de DC a 3,3V 5 TECNOIOT 5pcs LM1117 Multímetro 1 - 41
bool driveST =false; bool prevDriveST =false; Las últimas variables controlan el estado del motor: encendido, velocidad y dirección. int motorEnabled =0; int motorSpeed =0; int motorDirection =1; Al inicio del programa se establecen si los pines serán de entrada o salida, así como el pin que se encargará de mandar la señal al puente-H. Así pues, los pines del puente-H son de escritura, y los de los botones son de lectura. void setup() { pinMode(buttonGears, INPUT); pinMode(buttonStart, INPUT); pinMode(bridgeDrive, OUTPUT); pinMode(bridgeRevrs, OUTPUT); pinMode(bridgeStart, OUTPUT); // Configuración de canal PWM usando la nueva API ledcAttach ledcAttach(bridgeStart, 5000,8); // Pin, Frecuencia, Resolución // Inicia con el motor desactivado (0% de PWM) ledcWrite(0,0); } En la primera parte del bucle de uso se maneja el encendido y apagado del motor. Se lee el botón que lo acciona, se establece la velocidad a 128 (mitad de potencia) y se permuta su estado en función de la lectura del motor y previo estado. Es el método void ledcWrite(int pin, int velocity) el que se encarga de asignar la velocidad al motor. void loop() { // MOTOR START startST =digitalRead(buttonStart); motorSpeed =128; if (startST != prevStartST) { if (startST == HIGH) { Serial.println(" => Start: " +motorEnabled); motorEnabled = !motorEnabled; } } prevStartST =startST; if (motorEnabled) { ledcWrite(bridgeStart, motorSpeed); }else { ledcWrite(bridgeStart, 0); } Similarmente, en la segunda parte se maneja la dirección. Sin embargo, esta tiene una peculiaridad que ha de ser explicada teniendo en cuenta el uso de los pines del puente-H ya explicado en el ejemplo de Arduino. Para que el motor rote ha de tener el pin GPIO 12 y GPIO 13 en estados 48
HIGH y LOW opuestos. La permutación de estos dos estados en los pines es lo que hará que la dirección de rotación varíe. // MOTOR DIRECTION driveST =digitalRead(buttonGears); if (driveST != prevDriveST) { if (driveST == HIGH) { Serial.println(" => Direction: " +motorDirection); motorDirection = !motorDirection; } } prevDriveST =driveST; if (motorDirection == 1) { digitalWrite(bridgeDrive, HIGH); digitalWrite(bridgeRevrs, LOW); } else { digitalWrite(bridgeDrive, LOW); digitalWrite(bridgeRevrs, HIGH); } } 49
3.2.4. Web server El servidor web es el más difícil de entender de todos. En este caso se trata de la creación de un servidor en el puerto 80 de una IP de la red local. No se necesita nada específico para esta prueba, debido a que lo más importante es la comprensión de la creación de un servidor web. Por ello, paso directamente al desglose del código: Lo primero es usar los includes necesarios. #include <WiFi.h> sirve para acceder a la red local y #include <WebServer> para crear el servidor. #include <WiFi.h> #include <WebServer.h> Los credenciales de la red están ocultos en otro archivo (obviamente incluido en el .gitignore). Para no mezclarlo se crea una variable global a la que se le asigna la palabra definida en este archivo. Además, también hay que crear el servidor en el puerto 80. Debe ser una variable global porque debe ser accedido desde los dos métodos principales: void setup() yvoid loop(). const char*ssid =NETWORK; const char*password =PASSWORD; WebServer server(80); Para compartimentar el código y abstraerse lo máximo posible, y esto es algo que se usará mucho en el desarrollo final, se ponen fuera de los dos métodos que manejan el servidor. En este caso, el método WebServer.send(int code, char* type, char* text) se encarga de enviar un mensaje al servidor con lo que debe procesar. El parámetro int code es el tipo de respuesta que se quiere enviar. Son los códigos de estado HTTP: ●100+ (Informativo): ○100 - Continue ○101 - Switching Protocols ○102 - Processing (WebDAV) ●200+ (Éxito): ○200 - OK (Respuesta estándar para solicitudes HTTP exitosas) ○201 - Created ○202 - Accepted ○203 - Non-Authoritative Information ○204 - No Content (Respuesta exitosa sin contenido) ○205 - Reset Content ○206 - Partial Content (Utilizado para transferencias de archivos) ●300+ (Redirección): ○300 - Multiple Choices ○301 - Moved Permanently ○302 - Found (o 302 Temporarily Moved) ○303 - See Other ○304 - Not Modified ○307 - Temporary Redirect ○308 - Permanent Redirect ●400+ (Errores del Cliente): ○400 - Bad Request ○401 - Unauthorized 50
○403 - Forbidden ○404 - Not Found (Recurso no encontrado) ○405 - Method Not Allowed ○406 - Not Acceptable ○408 - Request Timeout ○409 - Conflict ○410 - Gone ○411 - Length Required ○413 - Payload Too Large ○414 - URI Too Long ○415 - Unsupported Media Type ●500+ (Errores del Servidor): ○500 - Internal Server Error ○501 - Not Implemented ○502 - Bad Gateway ○503 - Service Unavailable ○504 - Gateway Timeout ○505 - HTTP Version Not Supported El segundo parámetro char* type hace referencia al tipo de contenido que se envía al cliente: - text/html: Para enviar contenido HTML. - text/plain: Para enviar texto plano sin formato. - text/css: Para enviar hojas de estilo CSS. - application/json: Para enviar datos en formato JSON. - application/xml: Para enviar datos en formato XML. - text/javascript: Para enviar scripts JavaScript. - image/png: Para enviar imágenes en formato PNG. - image/jpeg: Para enviar imágenes en formato JPEG. - image/gif: Para enviar imágenes en formato GIF. - image/svg+xml: Para enviar imágenes en formato SVG. - application/pdf: Para enviar archivos PDF. - application/octet-stream: Para enviar datos binarios o archivos descargables. - audio/mpeg: Para enviar archivos de audio en formato MP3. - video/mp4: Para enviar archivos de video en formato MP4. - font/woff2: Para enviar fuentes web en formato WOFF2. - text/csv: Para enviar archivos de datos en formato CSV. Por último está el char* text con la información en cuestión. En este caso es un archivo HTML. void handleRoot() { server.send(200,"text/html", html_index); } Lo primero que se hace al iniciar el programa es intentar establecer conexión con la red local con el método void WiFi.begin(char* ssid, char* password). Tras esto se espera de forma bloqueante a que la conexión esté establecida con el método int WiFi.status(). void setup() { Serial.begin(115200); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(1000); Serial.print("."); 51
} Serial.println(" Done!\n"); Por último se establecen los manejadores con con el método void Server.on(char* where, int type, void handler). El primer parámetro marca dónde se aloja esa petición en el servidor. El segundo parámetro es el método HTTP que manejará. Puede ser: - HTTP_GET: Solicitudes GET, las más comunes para obtener recursos del servidor. - HTTP_POST: Solicitudes POST, para enviar datos al servidor. - HTTP_PUT: Solicitudes PUT, que se utilizan para actualizar o crear recursos. - HTTP_DELETE: Solicitudes DELETE, utilizadas para eliminar recursos del servidor. - HTTP_OPTIONS: Solicitudes OPTIONS, para obtener las opciones de comunicación.. - HTTP_PATCH: Solicitudes PATCH, que se utilizan para realizar actualizaciones. Por último el void handler, que puede ser un método que hay en otra parte del código (como en este caso) o una función lambda (como será en el código definitivo). server.on("/", HTTP_GET, handleRoot); server.begin(); Serial.println(" => Server HTTP started"); } En el bucle principal simplemente se usa el método void Server.handleClient() que se encarga de ver si hay nuevas conexiones o desconexiones, si hay alguna solicitud HTTP entrante al servidor y también se asegura de que se envíe una respuesta. void loop() { server.handleClient(); } 52
4. Capítulo IV: Construyendo el coche 4.1. Materiales 4.1.1. Componentes electrónicos La lista cerrada de materiales electrónicos que han sido usados para montar el circuito es el siguiente: Nombre Cantidad Especificaciones Adaptador de fuente de alimentación 3,3V/5V 1 MB102 Breadboard Adaptador del engranaje del motor 1 - Batería 2 9V Brazo del servomotor 1 Cuña Cables de prototipado hembra 20 Flexibles Cables de prototipado macho 1 Flexibles Condensador 2 100 µF Conector de batería 2 - ESP32-CAM 1 - Motor 1 DC Pines macho 21 - Protoboard 1 Pequeña Puente-H 1 L293D Servomotor 1 - Tabla 4-1. Componentes electrónicos del coche La estructura se divide en 4 partes, que serán explicadas con más detalle en el apartado de montaje con diagramas y fotografías. En la explicación se subrayan los elementos que pertenecen a estas secciones. Las cuatro zonas son: -Alimentación, en el asiento del copiloto. Esta incluye 2 baterías 9V: una alimentando el adaptador de fuente de alimentación, configurado para proporcionar únicamente 5V, y la pila del motor, que se conecta directa a la protoboard. -Cableado, en la zona del motor. Con la protoboard como base, se han usado dos tiras de 5 pines macho para conectar todos los componentes que necesiten 5V y GND, respectivamente. Otra tira de 8 pines macho se ha usado para conectar los cables de prototipado hembra del puente-H, y otros 2 pines macho sueltos se han usado para elementos que había que conectar desde un terminal hembra a la placa. Sobre esta placa se coloca el ya mencionado puente-H y las conexiones del servo y del motor. 53
-Motor, conectado a la transmisión. Este motor DC tiene encajado una pieza circular de plástico original de Arduino que a su vez se ha pegado a un engranaje que ajusta la medida del motor a la de la transmisión. Con esto nos aseguramos que el motor consiga encajarse dentro de la estructura. -Servo, conectado a la dirección. Debajo del salpicadero se ha hecho un hueco para que quepa el servomotor. Este está pegado a un engranaje que se encarga de transmitir los giros del volante a las ruedas. 4.1.2. Componentes estructurales Para construir la carcasa del coche opté por construir un coche basado en la estructura de bloques que simulan el estilo LEGO® Technic™. Esto facilita la mayor parte de la construcción porque te dan una base sólida sobre la que trabajar y no hay que preocuparse exageradamente por la integridad estructural del cuerpo. Es cierto que hay que salirse del manual para integrar correctamente los componentes, pero haciendo la valoración, es mejor que fabricar de manera íntegra y autónoma un chasis. Asimismo, es bastante asequible económicamente: menos de 16 €. Figura 4-1. Manual de construcción del coche 54
4.2. Código La estructura general del código está basado en el paradigma de la programación orientada a objetos (POO). Usando esto, he dividido las diferentes funcionalidades del circuito en código encapsulado e incomunicado entre ellos. Para no extender demasiado la memoria con código que existe en GitHubⓇ, tan solo voy a explicar cómo funcionan los métodos más importantes de cada archivo. Como idea general, se han usado 4 clases de “cosecha propia” para el funcionamiento de los componentes. Estos son class MyCamera,class MyMotor,class MyServo yclass MyWebServer; cada una manejando lo que su nombre indica. Todas estos objetos se crean en el archivo principal, el esp32cam_toycar.ino, que crea una instancia de cada clase. La clase MyWebServer será la encargada de manejar el resto de clases, que se le pasarán por parámetro. También, y como se ha visto en las pruebas, a través del objeto con la palabra reservada WiFi se conecta a la red local. Figura 4-2. Esquema de funcionamiento en el archivo principal Durante la explicación de los métodos y clases, para facilitar la lectura se eliminarán todos los controles de errores, así como código auxiliar que pueda distraer del contexto real que se quiere exponer. 55
4.2.1. main.ino El archivo .ino es el principal del código. En él se crean y se manejan los objetos. En la zona de declaraciones se asignan la red y la contraseña, así como los pines que se usarán en la ESP32-CAM. En este caso son: -GPIO 12 para ir hacia delante. -GPIO 13 para ir hacia atrás. -GPIO 15 para activar el motor. -GPIO 14 para manejar el servomotor. Asimismo, se declaran y se pasan por parámetro los objetos a MyWebServer, que se encargará de manejarlos según lo que reciba de la página web. const char*ssid =NETWORK; const char*password =PASSWORD; const int pinForward =12; const int pinBackward =13; const int pinEnaleMotor =15; const int pinServo =14; const int port =80; MyCamera cam; MyServo srv(pinServo); MyMotor mot(pinForward,pinBackward,pinEnaleMotor); MyWebServer mws(cam,mot,srv,port); En el void setup() se inicializan con los métodos públicos de los objetos. void setup() { [...] WiFi.begin(ssid, password); cam.initCamera(); mot.setupMotor(); srv.setupServo(); [...] } Por último, en el void loop() se maneja el método que se encarga de revisar los cambios en cada componente. void loop() { mws.handleLoop(); } 56
4.2.2. MyWebServer.h Figura 4-3. Diagrama de la clase MyWebServer 57
4.2.5. MyServo.h Figura 4-6. Diagrama de la clase MyServo La clase MyServo tiene nueve variables privadas: -Servo _servo: Objeto que representa el servo motor. -int _pin: Pin al que está conectado el servo. -int _angle: Ángulo actual del servo. -const int _maxAngle: Ángulo máximo permitido para el servo (180 grados). -const int _midAngle: Ángulo medio del servo (90 grados). -const int _minAngle: Ángulo mínimo permitido para el servo (0 grados). Asimismo, también tiene cuatro métodos públicos y dos privados. El primero de ellos es void MyServo::setupServo(). Primero asigna el pin GPIO 14 al servomotor, y luego se llama al método void MyServo::sweep() para comprobar que funciona correctamente, hace un movimiento de arco de ida y vuelta y después se coloca en el centro. void MyServo::setupServo() { _servo.attach(_pin); sweep(); } Los métodos void MyServo::right(),void MyServo::center() yvoid MyServo::left() son idénticos, salvo que su valor por defecto es el ángulo del lado al que se quiere girar. Esto son 0º para la izquierda, 90º para el centro y 180º para la derecha. void MyServo::right(int newAngle =180) { angle(newAngle); } 64
El método divertido de esta clase es void MyServo::sweep(), que como ya se ha explicado hace un movimiento de izquierda a derecha y vuelta, para al final quedarse con el brazo en el centro. Este movimiento lo hace con un retraso estándar de 10 ms por grado. void MyServo::sweep(int delayTime =10) { for (int pos =_minAngle; pos <= _maxAngle; pos++) { angle(pos); delay(delayTime); } for (int pos =_maxAngle; pos >= _minAngle; pos--) { angle(pos); delay(delayTime); } [...] center(); } El último método es void MyServo::angle(), que simplemente se encarga de mandar la orden al servo de la nueva posición que ha de tener. Primero se asegura de que el valor está en rango usando el método void constraint(), que limita el ángulo máximo a los valores marcados. Después usa la función void Servo.write() para mover el brazo. void MyServo::angle(int newAngle) { _angle =constrain(newAngle, _minAngle, _maxAngle); _servo.write(_angle); } 65
4.2.6. webcode.h El archivo webcode.h contiene el código .html,.ico y.css en formato necesario para el funcionamiento correcto de la página web. Lo más importante de todos estos archivos es el <script> integrado en el .html, que contiene la lógica de la comunicación entre el servidor y cliente. Primero revisaré el contenido del .html puro, antes de entrar en el <script>. En el código siguiente se puede ver donde se aloja el recuadro de vídeo: el <div id=”videoContainer”></div>, cuya forma se editará con estilos en el .css. La siguiente parte interesante es el ejemplo de botón que hay. En este caso es el botón que mueve hacia delante el vehículo. Este botón tiene dos métodos: - onmousedown: el usuario está pulsando el botón. Esto causa la llamada de void sendCMD(‘D’) que, como hemos visto antes en el void MyWebServer::onEventMotor(), al recibir el comando “D” (Drive), llamará a la función void MyMotor::forward(). - onmouseup: el usuario ha dejado de pulsar el botón. El proceso es el mismo pero en este caso el comando es “N” (Neutral), que llama a la función void MyMotor::break(). Es importante recalcar que se llama el método void MyMotor::break() y no al void MyMotor::lock() porque se quiere una aceleración y deceleración progresiva. De otra forma, se podría quemar el motor. Sería como meter en un coche real la marcha atrás cuando se va por la autopista. <!DOCTYPE html> <html lang="en"> <head> [...] </head> <body> [...] <div id="videoContainer"> <img id="videoStream" [...]> </div> <div id="controls"> <button id ="arrow-button" style ="grid-area: 1 / 2;" onmousedown ="sendCMD('D')" onmouseup ="sendCMD('N')"> ▲</button> [...] </div> [...] <script> [...] </script> </body> </html> Dentro del .html se encuentra el código del script de comunicación servidor-cliente, pero para simplificarlo lo expondré como si estuviera en su propio archivo, un script.js. Para cada socket hay que crear una variable que lo soporte, su URL y una función que encapsule su comportamiento. En este caso voy a usar como ejemplo el de la cámara ya que es el más complicado de todos. Los pasos para crear una comunicación exitosa con cada socket son los siguientes: 66
Primero hay que declarar la variable que creará el socket y su URL: var wsCam; var wsCamURL ="ws://" +window.location.hostname +"/wsCam"; Luego hay que declarar la función con los callbacks que definen su comportamiento: 1) Se crea el WebSocket a partir de la URL. 2) Se establece el tipo de binarios (mensaje) a blob (un objeto de tipo binario en JavaScript, útil para manejar imágenes y vídeos). 3) Se reescriben las funciones de comportamiento del socket. El método más importante aquí es onmessage, debido a que son los mensajes los que controlan toda la lógica. a) Primero lo que se hace es eliminar el elemento asociado al objeto creado con el blob. b) Despues se vuelve a crear el elemento con la nueva información c) Por último se asocia al elemento ya visto antes “videoStream”, de tipo <img=[...]>. function initWebSockCam() { wsCam =new WebSocket(wsCamURL); wsCam.binaryType ='blob'; wsCam.onopen =function(event) { console.log(" => wsCam connection opened"); }; wsCam.onclose =function(event) { console.log(" => wsCam connection closed"); setTimeout(initWebSockCam,2000); }; wsCam.onmessage =function(event) { var blob =event.data; if (url) { URL.revokeObjectURL(url); } var imageID =document.getElementById("videoStream"); url =URL.createObjectURL(blob); imageID.src =url; }; wsCam.onerror =function(error) { console.log(" => wsCam error: " +error.message); }; } Todas estas funciones con la lógica de los sockets han de ser llamadas en la inicialización del servidor, por lo que, por último, se llaman cuando se carga la página. function initWebSock() { initWebSockCam(); initWebSockMotor(); initWebSockServo(); initWebSockLog(); } window.onload =initWebSock; 67
Por último, a la hora de pulsar el botón, se llama a la función sendCMD() que a su vez llama a WebSocket.send() para transmitir el comando a void MyWebServer::onEventMotor() ovoid MyWebServer::onEventServo(). function sendCMD(command) { [...] switch (command) { case "D":case "R":case "N": if (wsMotor.readyState === WebSocket.OPEN) wsMotor.send(command); break; case "L":case "S":case "M": if (wsServo.readyState === WebSocket.OPEN wsServo.send(command); break; [...] } } 68
4.3. Montaje Para el montaje primero hubo que montar la base del coche. En ese momento no quedaba claro cómo se iba a integrar el circuito en la estructura, pero como se puede apreciar en la Figura 4-7, ya se pueden ver huecos en las partes bajas cerca de la dirección y la transmisión. Una opción plausible también sería poner el servo para controlar la dirección en el motor. Pero esto se vino abajo cuando, al girar el volante,no transmitía el movimiento lateral con el torque suficiente. Se había valorado dejar así el coche, usando solo su esqueleto inferior para poder meter todos los componentes a voluntad con tarjetas de prototipos soldadas, pero también se descartó por el mero hecho de que no era estético. Ya en esta etapa surgía una duda: cómo acoplar el motor con el engranaje del coche para que el movimiento se transmitiese correctamente. Esto es algo que dejaría para más adelante. Figura 4-7. Base del coche. Transmisión y dirección. 69
Antes de seguir el montaje, se probó el sistema y se hicieron múltiples comprobaciones ya con el servidor y la página web montada. Tanto el motor como el servo tenían el comportamiento esperado: - El motor acelera y frena lentamente. Si, por ejemplo, se pulsa el botón de freno, el motor no ejecuta la nueva orden hasta que termina su ejercicio actual. - El servo hace el void MyServo::sweep() para luego quedarse en el centro y moverse con la orden que le llega. Sin embargo, hubo que hacer bastantes correcciones en la cámara. Descubrí que el formato con el servidor de tipo WebServer no era nada óptimo, ya que debía atender demasiadas peticiones HTTP. Hice la prueba y según se iban añadiendo componentes, los FPS bajaban a un tercio. Pasaban de 10 FPS a 3 y luego a 1. Esto hizo que tuviera que cambiar toda la lógica y usar el sistema de WebSocket. Figura 4-8. Sistema montado en la protoboard. 70
Con la construcción avanzada se plantearon dos opciones: usar tarjetas de prototipos o simplemente conectar todos los componentes con cables de prototipado. Las dos opciones tuvieron que ser descartadas después de hacer varias pruebas por lo siguiente: - Tarjetas de prototipos: era necesario soldar los componentes, lo que requiere una práctica que faltaba. Además, la cantidad de cables y pines macho necesarios (insertados en los conectores hembra soldados) aumentaba y no se disponía de tantos pines macho de calidad. Se probaron varias formas pero ninguna era óptima respecto al tiempo y sobre todo mantenimiento. - Cables de prototipado: esta opción daba un manejo más libre de cada componente, pudiendo colocarlos como quisiera y haciendo pruebas más fácilmente. Sin embargo, cada vez que necesitaba hacer una nueva conexión había que hacer demasiados cambios. Es decir, no escalaba. Se decidió por esto usar una protoboard pequeña, colocándola en la zona del motor después de quitar el V6 de juguete y usar sus piezas para fijar las zonas endebles. Esto daba escalabilidad, estabilidad ymantenimiento a partes iguales. Figura 4-9. Sistema montado en la protoboard. 71
Luego se procedió a montar el motor y el SoC ESP32-CAM. El motor es el que tuvo más problemas, ya que primero tuve que colocarlo y luego comprobar la dirección. Efectivamente, como dice la ley de Murphy, después de colocarlo comprobé que el comportamiento era el inverso, así que hubo que hacer malabares para cambiar los pines GPIO 12 yGPIO 13 mediante el uso de dos pinzas, una de cada lado. Sin embargo, esto se expone para reflejar cómo la decisión de usar la protoboard fue la correcta. Podría haber cambiado la lógica del programa, pero llegados a este punto prefería modificar el código lo menos posible. Respecto a la ESP32-CAM, se fijó mediante piezas sacadas del asiento del copiloto al asiento principal, creando una nueva pieza que se podía apretar y desapretar para meterla o sacarla fácilmente de su posición. Con esto se quería también sacar las posibles piezas que pudiera tener cerca del reverso, donde se encuentra el chip y se sobrecalienta más. Como se puede apreciar, hay distintos cables de colores. Para poder trabajar bien, se usaron códigos de colores: - Rojo: Cables de corriente. - Negro: Cables a tierra. - Amarillos: cables de dirección del motor. - Verde: cable de activación del motor. - Blanco: cable de movimiento del servomotor. Figura 4-10. Sistema con el ESP32-CAM montado. 72
Para la dirección del coche se probaron dos opciones: conexión al volante yconexión directa a la dirección. La más divertida (en mi opinión) era la conexión al volante. Sin embargo, como se ha mencionado ya, esta conexión no transmitía el movimiento de giro correctamente, puesto que las piezas que había entre el volante y la dirección bailaban un poco. Lamentablemente hubo que desechar esta idea y pasar al plan B: conexión directa a la dirección. Para ello hubo que hacer muchos cambios de piezas y desmontaje, pues había que sacar todo el salpicadero y parte del capó, y por si no fuera poco, con las piezas sobrantes montar un soporte que fuera capaz de aguantar la fuerza de giro sin que el servomotor cambiara de posición. Entre las piezas del motor, del salpicadero y del asiento del copiloto, y con ayuda de una pieza de la protoboard se pudo encajar a la perfección. Por último, se pegó el engranaje al brazo del servo con pegamento de contacto. El resultado fue mucho mejor de lo esperado, y la transmisión se convirtió en la parte con el mejor resultado después de esto. Figura 4-11. Servo conectado a la dirección. 73
[20] Espressif Systems (Shanghai) PTE LTD, Igoe, T., & Hendrik, J. “WiFi Web Server LED Blink.” https://github.com/espressif/arduino-esp32/blob/master/libraries/WiFi/examples/SimpleWiFiS erver/SimpleWiFiServer.ino. 2012. Accedido en 08/24. 80