Transmisión de Vídeo WiFi con Arduino
Transmisión de Vídeo WiFi con Arduino
DE TRANSMISIÓN DE VÍDEO
CON MÓDULOS WIFI
SEPTIEMBRE 2016
TECNOLOGÍAS
TRABAJO FIN DEINDUSTRIALES
GRADO PARA Félix Moreno González
LA OBTENCIÓN DEL TÍTULO DE
GRADUADO EN INGENIERÍA EN
TECNOLOGÍAS INDUSTRIALES
Desarrollo de un sistema de transmisión de vídeo con módulos Wifi
Dedicatoria y agradecimientos
Me gustaría dedicar este Trabajo Fin de Grado a mis padres por su apoyo incondicional y
por haberme educado para llegar a ser la persona que soy ahora. Quiero agradecer a mi hermana
que siempre haya estado para apoyarme y hacerme reír en los momentos en los que más lo
necesitaba.
Deseo agradecer a mi tutor, Félix Moreno, haberme dado la oportunidad de realizar este
proyecto con el que he podido poner en práctica y ampliar mis conocimientos en el campo de
la electrónica. Me gustaría mencionar también a Yago Torroja y Jorge Portilla, que también
permitieron que ampliara los conocimientos que poseía sobre Arduino y los
microcontroladores.
No puedo acabar sin antes dar las gracias a mis amigos de la ETSII, con los que he vivido
tantas experiencias durante estos años y las que están por llegar. Y no me olvidaré de Pablo
Pérez, con el que formamos un grupo para participar en Cybertech; gracias a él comencé mis
andanzas en el mundo de la electrónica y la automática y descubrí mi verdadera pasión.
Resumen
desarrollar un sistema capaz de adquirir imágenes de una cámara de vídeo y enviarlas por
Wifi a un dispositivo receptor para posteriormente procesarlas y mostrarlas al usuario.
En esencia, se han utilizado dos módulos: la cámara OV7670, que extrae la información de
los píxeles mediante 8 bits, y el Nodemcu esp-12e, una placa de programación con Wifi
incorporado. Estos han sido elegidos por varios motivos: bajo coste, bajo consumo, tamaño
reducido y fácil programación.
Como objetivo secundario, se han analizado las capacidades y limitaciones de este sistema
de transmisión de imágenes:
el tiempo empleado desde que se empieza a recibir el primer píxel de la imagen hasta que
se termina de mostrar la imagen completa,
Las imágenes se envían sin comprimir a un servidor, del cual se podrían cogen mediante
cualquier dispositivo con Wifi. Específicamente, se ha conseguido recibir y mostrar las
imágenes en el ordenador con Matlab y en el móvil con una aplicación desarrollada en Android
Studio.
Además, el orden de las entradas físicas de la placa no coincidían con el orden de los bits de
estos registros, pero se investigó más y se hicieron pruebas para estar seguro de la
correspondencia entre los bits de los registros y las entradas, y comprobar también cuáles se
podían emplear mediante este método.
2) Haría falta una señal de reloj que introducir a la cámara para hacerla funcionar.
Se investigaron métodos para conseguir una señal de reloj, tanto por software (variar un pin
digital o utilizar PWM) como por hardware (construir un oscilador).
La opción con la que se obtuvieron mejores resultados fue el oscilador de Pierce, que es un
circuito compuesto básicamente de: un cristal de cuarzo, un inversor, un par de condensadores
y un par de resistencias, una de ellas funciona como filtro de paso bajo y evita sobreoscilaciones.
El cristal de cuarzo elegido fue uno de 10MHz, por lo que el circuito también oscila a la
misma frecuencia, habiendo ajustado la resistencia mencionada para evitar sobreoscilaciones.
Posteriormente, todos los componentes se soldaron en una placa perforada para tener un
circuito con todo fijo y que no hubiera problemas por falta de contacto entre componentes.
El resultado es una onda casi cuadrada de 10MHz y bastante estable. El pin del oscilador por
el que sale esta onda se conecta a la señal XCLK de la cámara, para proporcionarle la señal de
reloj que necesita.
3) Haría falta programar los registros de la cámara para configurar ésta, lo cual se debía
hacer mediante comunicación SCCB (semejante a la comunicación I2C) a través de dos
entradas de la cámara llamadas SIO_C (señal de reloj de la comunicación) y SIO_D (canal de
datos de la comunicación). Se estudió el funcionamiento de este tipo de comunicación y,
afortunadamente, se encontró un código que funcionó perfectamente. Por si acaso se decidió
analizar en el osciloscopio las señales que producía este código, para entender bien su
funcionamiento y poder hacer modificaciones si hacía falta.
4) La placa probablemente no tendría suficientes pines para leer todas las salidas de la
cámara. Se planteó cuántos pines harían falta y, efectivamente, se necesitaban 4 más de los
disponibles.
Se decidió entonces utilizar los pines disponibles de la placa para leer las entradas más
importantes de la cámara:
- VSYNC: señal de salida que indica cuándo acaba una imagen y empieza a salir
información de la siguiente.
- HREF: señal de salida que indica cuando empieza a salir información de cada línea.
- PCLK: señal de salida que indica cuándo sale información de cada píxel (esta iría
también a 10MHz si no se prescala).
5) También sería necesario poder enviar grandes paquetes de información por Wifi, para
después poder descargar dicha información en el móvil.
Alguno de los programas de Wifi probados en la placa consistía en hacer que funcionara
como un servidor web o enviar la información a una página de una determinada forma que
impedía hacer envíos grandes.
Tras realizar estos análisis y comprobaciones, se dispuso a conectar todos los componentes
y realizar un programa con el que leer la información que extrae la cámara.
Los cambios de las señales VSYNC, HREF y PCLK de la cámara se detectaban mediante
interrupciones.
Para obtener dicha información con Matlab, se utiliza la función urlread() y la librería
JSON.m (descargada de internet) para analizar la estructura de los mencionados archivos .json.
Una vez descodificada la información y obtenidos los colores en el formato YCbCr, estos se
convierten a RGB y se almacenan en una matriz de 100x100x3, correspondiendo las dos
primeras dimensiones al tamaño de la imagen y la tercera dimensión al color R, G o B. Dicha
matriz de colores se muestra por pantalla mediante la función imshow().
Para obtener la información con Android, se ha diseñado una aplicación en Android Studio,
en la que, mediante tareas asíncronas para no bloquear el hilo principal del programa, se realiza
la obtención de la información del mismo modo que antes, excepto que se usan funciones
propias de java y la librería JSON que ya incorpora Android. Además, los colores se guardan
en una variable de tipo Bitmap, a la cual se puede asignar un color a cada píxel. El bitmap se
muestra por pantalla mediante un imageView, componente visual que nos permite mostrar
imágenes en la aplicación.
Se han analizado también otros tiempos: la placa tarda en torno a 7s en enviar la información
de la imagen de 100x100 píxeles, Matlab tarda 3s en descargar y mostrar la imagen, mientras
que Android tarda el doble que Matlab. Así, se obtienen tiempos de entre 13 y 16s entre que se
empieza a coger la información del primer píxel de la imagen hasta que se consigue visualizar
dicha imagen completa. Estos tiempos impiden la visualización de imágenes en tiempo real.
- Pueden haberse configurado erróneamente los registros. La cámara tiene 200 registros y
se ha podido encontrar poca información en cuanto a cómo configurarlos para obtener la
resolución deseada, por lo que se ha podido pasar por alto algún registro que hubiera que
modificar.
Palabras clave: Arduino, Matlab, Android, Wifi, Nodemcu, ESP8266, cámara OV7670,
servidor, PHP, JSON, captura de imágenes
Índice general
3.1.4. Android........................................................................................................... 50
3.2. Software desarrollado .............................................................................................. 51
3.2.1. Comunicación SCCB para configurar la cámara ........................................... 51
3.2.1.1. ¿Qué es la comunicación SCCB? ..................................................... 51
3.2.1.2. Funcionamiento del SCCB............................................................... 51
3.2.1.3. Escribiendo en los registros de la cámara ........................................ 53
3.2.2. Proceso de obtención y visualización de las imágenes .................................. 55
3.2.2.1. Captura y almacenamiento de la imagen en Arduino ...................... 55
3.2.2.2. Envío de la imagen en Arduino ........................................................ 55
3.2.2.3. Procesamiento de la url en el servidor ............................................. 56
3.2.2.4. Unión de toda la información de la imagen en un solo archivo ....... 57
3.2.2.5. Descarga de la imagen con Matlab .................................................. 59
3.2.2.6. Descarga de la imagen con Android ................................................ 60
3.2.2.7. Extras añadidos ................................................................................ 64
Capítulo 4 - Pruebas y resultados ......................................................................................... 65
4.1. Conexión de la cámara con el oscilador .................................................................. 65
4.2. Conexión de la cámara con el Nodemcu ................................................................. 68
4.3. Imágenes obtenidas .................................................................................................. 70
Capítulo 5 - Conclusiones ...................................................................................................... 73
Capítulo 6 - Líneas futuras .................................................................................................... 75
Capítulo 7 - Planificación temporal y presupuesto ............................................................. 77
7.1. Estructura de Descomposición del Proyecto (EDP) ................................................ 77
7.2. Diagrama de Gantt ................................................................................................... 77
7.3. Presupuesto .............................................................................................................. 81
Índice de Figuras .................................................................................................................... 83
Índice de Tablas...................................................................................................................... 85
Índice de Códigos ................................................................................................................... 87
Bibliografía ............................................................................................................................. 89
Capítulo 1 - Introducción
1.1. Antecedentes
Hoy en día las cámaras tienen innumerables aplicaciones. Además de poder plasmar
momentos inolvidables, se usan también en vigilancia, como detectores de movimiento y para
alertar de intrusos; en investigación científica, para ver lo que el ojo humano no puede apreciar;
en reconocimiento facial, para identificar ladrones o dar acceso a instalaciones protegidas;
control a distancia, para pilotar drones, etc.
Este último uso es uno de los más interesantes pues, con la industria de drones en aumento,
supone poder controlar helicópteros teledirigidos y pequeñas naves no tripuladas, en espacios
reducidos y de difícil acceso, sin necesidad de tenerlos a la vista. Para ello es imprescindible un
dispositivo de captura de imágenes de buena calidad, conectado a un procesador que lea la
información y sea capaz de enviarla a otro dispositivo que se encuentre lejos, para después
procesar dicha información y actuar en consecuencia.
1.2. Objetivos
desarrollar un sistema capaz de adquirir imágenes de una cámara de vídeo y enviarlas por
Wifi a un dispositivo receptor para posteriormente procesarlas y mostrarlas al usuario.
Se utilizarán dos módulos: la cámara OV7670, que extrae la información de los píxeles
mediante 8 bits, y el Nodemcu esp-12e, una placa de programación con Wifi incorporado.
Las ventajas por las que se ha decidido lograr el objetivo principal anteriormente
mencionado mediante estos módulos son:
Bajo coste
Bajo consumo
Tamaño reducido
Fácil programación
Las imágenes se enviarán sin comprimir y serán recibidas por algún dispositivo con Wifi
como por ejemplo el móvil a través de una aplicación Android, para procesarlas y mostrarlas al
usuario.
el tiempo empleado desde que se empieza a recibir el primer píxel de la imagen hasta que
se termina de mostrar la imagen completa,
Durante el desarrollo del proyecto y una vez alcanzado el objetivo, todos estos factores serán
analizados para sugerir posibles líneas de mejora u otras vías por las que se pueda alcanzar el
objetivo con mejores resultados.
1.3. Metodología
Capítulo 2 - Hardware
1) El nodemcu esp-12e
2) La cámara OV7670
También se utilizaron otros dispositivos para alimentar estos módulos y para convertir la
tensión de alimentación a 3.3V, los cuales serán explicados en un apartado posterior.
Se trata de una placa de desarrollo de bajo coste creada por Espressif Systems y que
incorpora el chip ESP8266 (microprocesador Xtensa Tensilica LX106 con conexión Wi-Fi).
El ESP8266 es ampliamente usado para aplicaciones de internet de las cosas (IoT, Internet
of Things) que se refiere a la interconexión de objetos cotidianos mediante Internet.
Contiene protocolo TCP/IP para proporcionar conexión Wi-Fi. Puede albergar su propia
aplicación o servir como conexión entre cualquier microcontrolador e Internet.
Está diseñado de forma que tiene un consumo mínimo y opciones de suspensión (sleep mode
y deep sleep mode).
El nodemcu esp-12e incorpora el chip CH340 para permitir la conexión serie entre la placa
y el ordenador y así poderse programar, ya sea directamente mediante comandos AT, mediante
el lenguaje de programación Lua de la plataforma de desarrollo NodeMCU o incluso mediante
el entorno de desarrollo (IDE) de Arduino.
Los pines de los que dispone el Nodemcu se pueden apreciar en la Figura 1. Estos son:
- 11 pines digitales (todos permiten PWM e I2C excepto el D0; además el D4 y D7-10
pueden funcionar como TX o RX permitiendo comunicación UART).
Un aspecto clave ha sido la posibilidad de leer los pines digitales del Nodemcu de manera
inmediata mediante los registros, ya que funciones de lectura y escritura de pines digitales como
digitalRead() y digitalWrite() proporcionados por las librerías de Arduino utilizan más
instrucciones de las realmente necesarias y pueden ralentizar el proceso.
Los registros para acceder al estado de los pines se encuentran en posiciones de memoria a
partir de la dirección 0x60000300.
Para poderse utilizar, dichas posiciones de memoria se declaran como está especificado en
el Código 1 (los códigos completos se encuentran en la web del Departamento de Electrónica).
- PIN_OUT: posee el valor de todas las salidas a la vez y, por ello, escribir en él afecta a
todas las salidas a la vez. Escribir un ‘1’ en un bit pondrá el correspondiente pin a HIGH
y escribir un ‘0’ lo pondrá a LOW.
- PIN_DIR: funciona como PIN_OUT, es decir, afecta a todos los pines a la vez. Indica si
un pin funciona como salida o como entrada. Escribir un ‘1’ en un bit hará que el
correspondiente pin funcione como salida y escribir un ‘0’ hará que funcione como
entrada.
- PIN_DIR_INPUT: igual que PIN_DIR_OUTPUT pero hace que los pines funcionen
como entrada.
Siempre hay que tener en cuenta el diagrama de la Figura 2 para cada pin.
PIN_DIR
PIN_IN
- El estado del pin se podrá modificar mediante PIN_OUT siempre que dicho pin esté
configurado como salida.
- PIN_OUT no tiene por qué coincidir con PIN_IN en los pines configurados como entrada.
Como se puede ver, las posiciones de memoria son de 4 bytes, esto es, 32 bits. El orden de
estos coincide con la numeración de los GPIO (General Purpose IO), pero no coincide con la
numeración de los pines digitales D0 a D10 escritos en la placa. La correcta correspondencia
se puede ver en la Figura 3.
Por ello, al leer los registros mencionados, la correspondencia de los bits con los pines
digitales es de la siguiente forma:
- Bits 31 a 16: solo el bit 16 se corresponde con el pin digital D0. No proporcionan
información ya que no se pueden leer ni modificar.
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
D8 D5 D7 D6 D1 D2 D9 D4 D10 D3
(RX) (TX)
Tabla 1: Correspondencia de los bits 15 a 0 de los registros con las salidas digitales del Nodemcu
- Todos los pines digitales del Nodemcu se pueden leer y modificar mediante registros,
excepto el pin digital D0.
- No se ha encontrado ningún registro con el que poder leer el pin analógico A0.
- Por ello, se tendrán que seguir utilizando las funciones digitalRead() y digitalWrite() de
Arduino para el pin digital D0, y las funciones analogRead() y analogWrite() de Arduino
para el pin analógico A0.
Todas las funciones del procesador de imagen (control de exposición, gamma, balance de
blancos, saturación de color, control de tono y más) son programables a través de una
comunicación SCCB (compatible con comunicación I2C).
- PCLK: señal de reloj de salida (indica el comienzo de salida de información del siguiente
píxel)
- D[7-0]: 8 bits de datos (máxima cantidad de información que se puede extraer en cada
momento)
- Reset: señal de reset (restablece todos los registros a sus valores por defecto y reinicia el
módulo)
2.1.2.2. Funcionamiento
HREF se pone a HIGH cuando empieza a salir información de una fila y a LOW cuando ésta
acaba. Este proceso se repite hasta haber sacado la información de todas las filas.
Mientras HREF está a LOW, la información que sale por los pines D[7-0] no es válida.
Como se puede apreciar en la Figura 7, los cambios se producen por defecto por flanco de
bajada de la señal de reloj de salida (PCLK).
Como los pines D[7-0] son 8, por cada ciclo de dicho reloj se puede obtener 1 Byte de
información.
Cuando HREF se pone a HIGH, empieza a salir información de los píxeles de una fila (2
Bytes por pixel) hasta llegar al último pixel de la fila.
2.1.2.3. Configuración
Para configurar los distintos modos de operación de la cámara se utilizan los registros.
Los registros son dispositivos digitales donde se almacena información de manera temporal.
Están constituidos por flip-flops o biestables, cada uno de los cuales almacena un bit con el
valor ‘0’ o ‘1’. Lo más común es que los registros estén constituidos por 8 biestables, es decir,
almacenan 8 bits, este es el caso de los registros de la cámara OV7670.
- la resolución de la imagen,
Al igual que el resto de registros, el registro 0x12 posee 8 bits (numerados del 7 al 0).
- Seleccionar el formato de color (YUV o RGB) según lo que se escriba en los bits 2 y 0.
Como se puede ver en la Figura 8, el valor por defecto del registro 0x12 (es decir, cuando se
reinicia el módulo) es 0x00. Esto significa que por defecto no está configurada ninguna de las
resoluciones mencionadas y el color se extrae en el formato YUV.
Cabe mencionar que todos los registros tienen un valor por defecto, de forma que la cámara
ya viene configurada para funcionar de un determinado modo nada más encenderla.
Los pines de la cámara que hay que leer son: VSYNC, HREF, PCLK y D[7-0]. Y hay que escribir
en los pines SIO_C y SIO_D para configurar los registros. Así que en total son 11 para leer y 2
para escribir.
Se deduce por tanto que la placa de programación no tiene suficientes entradas para manejar
todos los pines de la cámara.
Aunque se consideró utilizar algún dispositivo para convertir varias señales digitales en una
sola analógica y poder leerla con el pin analógico del Nodemcu (comentado al final de este
apartado), esta opción fue descartada debido a la gran cantidad de tiempo que tarda en ejecutarse
la función analogRead() de Arduino.
- utilizar los pines necesarios del Nodemcu para escribir en SIO_C y SIO_D y leer VSYNC,
HREF y PCLK
- utilizar los pines digitales D9 y D10, que poseen las funciones RX y TX de comunicación
con el ordenador por USB, para depurar el programa
- y conectar los pines sobrantes a los pines más importantes D[7-…] de la cámara
Mediante un DAC (conversor digital a analógico) podemos convertir varias señales digitales
de la cámara en una sola señal analógica que poder leer con el Nodemcu.
El problema que podría presentar la conversión podría ser un retraso temporal entre las
señales digitales a convertir y la señal analógica a obtener. Este retraso temporal sería debido a
capacitancias de los componentes del circuito, sobre todo de los cables.
Existen diferentes arquitecturas para construir un DAC. Se probó la red R-2R [5]. El circuito
básico es el de la Figura 10.
Las señales digitales se conectan a las entradas a0 (Bit menos significativo) a an-1 (bit más
significativo).
En función del código digital, la señal analógica Vout tendrá un valor comprendido entre 0 y
Vref, ya que estos son los dos únicos valores que pueden presentar las entradas digitales (0
cuando estén a LOW y Vref cuando estén a HIGH).
Para un determinado código digital C y una red R-2R con N bits de valor 0V o Vref, el valor
de la señal analógica será:
𝑉𝑟𝑒𝑓
𝑉𝑜𝑢𝑡 = C · 2𝑁
Pruebas
Para hacer pruebas, hacemos un montaje para 2 bits cogiendo solo resistencias de 2R=5.6kΩ.
Como habíamos previsto, sí se producen retrasos. Vout necesita 2us para cambiar
completamente de estado.
Como ya se ha mencionado antes, esta opción fue descartada debido a la gran cantidad de
tiempo que tarda en ejecutarse la función analogRead() de Arduino.
2.1.3. Otros
Durante la realización de este TFG se han utilizado: una fuente de alimentación del
laboratorio y un convertidor a 3.3V encajable en protoboard.
La cámara empleada necesita una señal de reloj de entre 10 y 48MHz para poder funcionar,
por lo que se consideraron distintas soluciones para generar dicha señal.
Fundamentación teórica
Como se muestra en la Figura 14, un circuito oscilador está compuesto básicamente por un
amplificador, de ganancia A y fase α, y una red de realimentación, de ganancia F y fase β.
V1 Amplificador V2
A, α
Realimentación
F, β
Para que el circuito oscile deben cumplirse las dos siguientes condiciones [7]:
1) F x A ≥ 1 (la ganacia global de lazo cerrado debe ser mayor o igual que 1)
Dentro de los circuitos osciladores probados, el oscilador Pierce (Figura 15) es con el que se
han conseguido los resultados deseados.
RF
Inversor RS
Cristal
C1 C2
C L R
C0
Figura 16: Circuito equivalente de un cristal de cuarzo
Los valores de C y L vienen determinados por las características mecánicas del cristal; R es
la resistencia de resonancia del circuito oscilador, y C0 representa la capacitancia de los cables
y electrodos. C0 es mucho más grande que C y es afectada por el resto de capacitancias del
circuito final.
1 𝐶
𝑓𝑝𝑎𝑟 = 2𝜋√𝐿𝐶 √1 + 𝐶
𝑝 +𝐶0
𝐶 ·𝐶
En el oscilador Pierce, Cp está formado por C1 y C2. Así: 𝐶𝑝 = 𝐶 1+𝐶2 .
1 2
𝐶1
F=
𝐶2
La estabilidad del oscilador de cristal depende de Cp, el cual debe ser igual a CL, carga
capacitiva recomendada por el fabricante del cristal.
El oscilador Pierce también utiliza un inversor CMOS que trabaja en su zona lineal como
amplificador. El inversor proporciona un cambio de fase de 180°. Para que se cumpla la segunda
condición de oscilación, el oscilador de cristal también debe proporcionar un giro de 180°. Si
C1=C2, la corriente por ambos condensadores es idéntica y desfasada 180°, por lo que el cristal
también provee 180° de cambio de fase.
Para circuitos osciladores, se pueden usar inversores con Schmitt trigger (que mantienen el
valor de salida a ‘0’ o ‘1’ completamente) o inversores sin Schmitt trigger. Sin embargo, la
ganancia de los inversores con Schmitt trigger es mucho mayor y por ello son más sensibles a
cambios en el circuito oscilador y menos estables que los inversores sin Schmitt trigger.
El circuito oscilador incorpora además una resistencia RS conectada a la salida del inversor
y al punto de unión entre el cristal y C2. Junto con C2, RS forma un filtro de paso bajo para
evitar oscilaciones de muy alta frecuencia. Se pueden conseguir resultados aceptables eligiendo
1 1
un valor cercano a la reactancia capacitiva de C2: RS = XC = 𝑤𝐶 = 2𝜋𝑓𝐶 .
2
Realización práctica
SN74HC04 RS=98Ω
Cristal
C1=33pF 10MHz C2=33pF
1) Un inversor SN74HC04 (sin Schmitt trigger), de tipo “through hole” (con patillas) para
montar en protoboard.
- el 74ACT04, también más rápido pero solo alimentable entre 4.5 y 5.5V
- frecuencia de 10MHz
4) Dos condensadores de 33pF conectados a cada terminal del cristal y a tierra, obteniendo
𝐶 ·𝐶
una capacitancia de carga: CL = 𝐶 1+𝐶2 = 16.5pF (correctamente comprendido entre 8 y 32pF)
1 2
5) Una resistencia RS conectada a la salida del inversor y al punto de unión entre el cristal y
C2. RS junto con C2 forma un filtro de paso bajo para evitar oscilaciones de muy alta frecuencia.
Se pueden conseguir resultados aceptables eligiendo un valor cercano a la reactancia capacitiva
1 1 1
de C2: XC = 𝑤𝐶 = 2𝜋𝑓𝐶 = 2𝜋·10𝑀·33𝑝 = 482 Ω
2
Montaje y pruebas
Se probó primero sin RS y posteriormente valores mayores para obtener una señal lo más
cuadrada posible. En la figuras 23-28, se muestran ejemplos para distintos valores de RS:
Se eligió el valor de 470Ω para RS por obtenerse una señal lo más parecida posible a una onda
cuadrada y ser un valor cercano a la reactancia capacitiva de C2 (482Ω, calculado al final del apartado
anterior).
Finalmente se probó con un oscilador de 20MHz (Figura 29), pero como se puede apreciar en la
Figura 30 la señal obtenida no era buena.
Soldadura
Finalmente, se soldaron todos los componentes del oscilador probado en una placa perforada
para tener un circuito con todo fijo y que no hubiera problemas por falta de contacto entre
componentes.
El resultado se puede apreciar en la Figura 31. Los componentes finalmente utilizados fueron
los que dieron mejores resultados:
Un inversor SN74HC04
Una tira de pines hembra a la izquierda de la placa para la tensión de 3.3V y otra a la
derecha para la línea de GND (0V)
Un pin a la salida del inversor para insertar un cable con el que conectar el oscilador a la
cámara
Características:
- Ciclos de trabajo entre 1% y 99% Figura 32: Generador de ondas del laboratorio
Además de ser un aparato grande, solo puede generar ondas cuadradas de hasta 5MHz, por
lo que no sirve para este proyecto.
Mediante un sencillo programa como el del Código 2, en el que se varía continuamente una
salida digital del Nodemcu, y utilizando los registros de estado de los pines, se consigue una
frecuencia de 175Khz (tanto si funciona el nodemcu a 80MHz como si funciona a 160MHz).
void setup() {
pinMode(D1, OUTPUT);
digitalWrite(D1, LOW);
}
void loop() {
PIN_OUT_SET = data;
PIN_OUT_CLEAR = data;
}
Código 2: Variar un pin digital continuamente
Se observa que ésta está más tiempo a LOW que a HIGH, por lo que se deduce que el
programa se ralentiza en el momento de tener que repetir el loop. En efecto, se requieren varios
ciclos de reloj entre que termina el loop y éste vuelve a empezar, ya que el programa necesita
sacar la dirección de retorno de la pila, saltar a dicha dirección, devolver dicha dirección a la
pila y saltar a la localización en memoria de la función loop.
Por ello se decide introducir las dos instrucciones que cambian el estado del pin
(“PIN_OUT_SET = data” y “PIN_OUT_CLEAR = data”) dentro de un bucle infinito
“while(1)”. Así, se consiguen tiempos en HIGH y en LOW muy parecidos y una frecuencia de
6.6MHz.
Sin embargo, se observa también en el osciloscopio (Figura 33) que este programa deja de
funcionar durante 230ms cada 3.25s.
Se deduce que esto es debido a las interrupciones internas que el programa debe atender.
Para solucionarlo, se intentan usar las funciones “cli()” y “noInterrupts()”, pero estas no
eliminan todas las interrupciones (Figura 34), las cuales se siguen produciendo durante 220ms
cada 8.5s.
Figura 34: Imagen de osciloscopio: interrupciones tras usar la función noInterrupts() en el programa
La frecuencia del PWM en un Arduino es de 490 o 980Hz (dependiendo del pin digital
utilizado), muy lejos de los megahercios deseados.
Profundizando un poco más, averiguamos que para generar PWM, los pines digitales utilizan
los timers a los que están asociados, además de un prescalado para obtener la frecuencia deseada
[8].
- Timer2, con una frecuencia de 31250Hz, y prescalados de 1, 8, 32, 64, 128, 256, y 1024.
- Timer3, 4 y 5, con una frecuencia de 31250Hz, y preescalados de 1, 8, 64, 256, and 1024.
Por defecto, los timers utilizan el prescalado de 64, obteniendo así las frecuencias
mencionadas al principio (62500/64=980 y 31250/64=490, apreciables en la Figura 35 y la
Figura 36).
Figura 35: PWM en Arduino de 976MHz Figura 36: PWM en Arduino de 490MHz
Tomando como ejemplo el timer0 del ATmega328P (microcontrolador del Arduino UNO),
un timer posee un registro de cuenta (TCNT) al que suma 1 en cada ciclo de reloj, contando así
desde 0 hasta 255 (si fuera de 16 bits contaría hasta 65535). Cuando el registro llega al máximo
valor (llamado TOP), vuelve a 0 y sigue contando.
Mediante el generador de ondas se puede obtener una señal PWM en OCnA u OCnB, de
forma que se pone a HIGH cuando el contador es 0 y a LOW cuando el contador alcanza un
determinado valor indicado en OCRnA u OCRnB, variando así el ciclo de trabajo (Figura 38).
Figura 38: Ciclo de trabajo del PWM obtenido en OCnx según el valor de OCRnx
Ésta no es la única opción de PWM. Todas las opciones se muestran en la Figura 39.
Estábamos usando el modo 3, cuyo TOP es fijo (0xFF=255). Si usamos el modo 7 podremos
variar el TOP del timer. Así, indicaremos el TOP en el registro OCRnA y el ciclo de trabajo en
el registro OCRnB, obteniendo el PWM en OCnB.
La máxima frecuencia se obtiene con OCR0A=1 (TOP) y OCR0B=0, de forma que el timer
solo cuenta de 0 a 1 (Figura 40).
Se obtiene así una señal de 8MHz (casi los 10MHz deseados), aunque nada cuadrada (ya
que en cuanto la señal sube ya tiene que volver a bajar).
A los pines del nodemcu se les pueden asignar distintas funciones según la Figura 41.
Observamos que para el pin 0 (que corresponde al pin digital D3) se puede obtener la señal
CLK_OUT (señal de reloj del nodemcu) mediante la función 4. La manera de obtenerlo es
añadiendo el Código 3.
pinMode(0, FUNCTION4)
Código 3: Asignar función a un pin del nodemcu
Algo parecido se obtiene para la señal CLK_XTAL mediante la función 4 del pin 3 (D9).
Esta opción no se tuvo en cuenta por haberse conseguido al final cuando ya se había
implementado la señal de reloj del apartado 2.2.1.1. Solución adoptada: circuito oscilador
mediante cristal de cuarzo. De todas formas, mediante la solución adoptada se obtiene una señal
de mejor calidad y fiabilidad.
- XCLK de la cámara con la salida del circuito oscilador, para proporcionar a la cámara
la señal de reloj que necesita para funcionar.
- D1-4 del Nodemcu con D[7-4] de la cámara (en este orden), para leer los bits más
significativos de cada píxel, ya que, como se ha comentado, el Nodemcu no tiene suficientes
pines.
- D5 del Nodemcu con PCLK de la cámara, para saber cuándo leer cada píxel
- D6 del Nodemcu con VSYNC de la cámara, para saber cuándo empieza cada imagen
- D7 del Nodemcu con HREF de la cámara, para saber cuándo empieza y acaba cada fila
de la imagen
- Se han unido todos los GND para que todos los módulos tengan la misma referencia de
tensión
SIO_C
D7
D6
D5
D4
PCLK
VSYNC
HREF
SIO_D
GND
XCLK
GND GND
Capítulo 3 – Software
3.1.1. Arduino
Arduino es una platforma de código abierto basada en software y hardware de fácil uso.
Permite la programación de microcontroladores: leer y escribir en sus entradas para controlar
leds, motores, sensores y otros dispositivos; comunicarse por bluetooth, wifi, etc, consiguiendo
realizar grandes proyectos de forma sencilla y barata. Para dicha programación se utiliza el
Entorno de Desarrollo de Arduino (IDE, Integrated Development Environment), que utiliza un
lenguaje parecido a C [9].
Mediante este software se ha programado el Nodemcu de este proyecto, habiendo tenido que
descargar la librería thinger.io para poder manejar dispositivos con capacidad de comunicación
a distancia como el ESP8266 (chip incorporado en el Nodemcu) [10].
3.1.2. LiveWire
Se ha usado este programa para diseñar los circuitos del oscilador de cristal incluidos en este
TFG.
3.1.3. Matlab
En este proyecto se ha utilizado Matlab (además de Android) para visualizar las imágenes
obtenidas de la cámara.
3.1.4. Android
La comunicación SCCB (Serial Camera Control Bus) es un bus de comunicación entre varios
dispositivos, parecido al I2C. La transferencia de datos se realiza siempre entre un maestro (el
dispositivo que empieza la comunicación) y un esclavo (el que responde).
El bus suele estar formado por tres líneas: una que actúa como reloj (SIO_C), otra que aporta
los datos (SIO_D) y una tercera de habilitación (SCCB_E).
SCCB_E
SIO_C
Dispositivo maestro SIO_D Dispositivo esclavo
Dispositivo esclavo
Dispositivo esclavo
El bus trabaja con lógica positiva, es decir, una tensión alta es un ‘1’ y una tensión baja es
un ‘0’.
En nuestro caso, la comunicación SCCB posee solo dos cables (no hay señal de habilitación):
SIO_C
Dispositivo maestro SIO_D Dispositivo esclavo
Este tipo de comunicación se utiliza en la cámara OV7670 para modificar los registros y así
poder configurar opciones como la frecuencia de la señal de reloj de salida, el tamaño de la
imagen, etc.
Para poder comenzar la comunicación deben estar ambas señales a nivel alto. Entonces se
pone a ‘0’ SIO_D y después SIO_C tras un tiempo característico T (un cuarto del periodo del
La señal SIO_C cambia repetidamente de ‘0’ a ‘1’ y viceversa, simulando una señal de reloj.
Un ciclo de escritura se compone de 3 fases, cada una de ellas está formada por 9 bits: 8 bits
del dato a transmitir y un noveno bit de “acknowledge” (reconocimiento de que la información
ha sido recibida).
Fase 1) En la primera fase se transmite la dirección del esclavo con el que el maestro desea
comunicar.
Fase 2) En la segunda fase se transmite la dirección del registro que se quiere modificar.
Fase 3) En la tercera y última fase, se transmite el dato que se quiere escribir en dicho
registro.
Para terminar la comunicación, se pone SIO_D a ‘0’, después SIO_C a ‘1’ y finalmente
SIO_D a ‘1’, cada una de estas acciones separadas por un tiempo T, quedando ambas señales a
nivel alto y permitiendo que comience la comunicación otra vez en cualquier momento.
Se ha utilizado la dirección 0x42 para escribir, ya que esta es la dirección de la cámara para
comunicación SCCB (según el datasheet de la cámara OV7670; 0x43 sería para leer),
Según la resolución, tamaño de imagen y frecuencia de reloj que deseemos utilizar hay que
escribir en unos registros u otros. Para saber en cuáles escribir se mira el datasheet, en el cual
aparece una tabla con los 202 registros que posee la cámara y una explicación de para qué sirve
cada bit de cada registro (para algunos bits no aparece información y están reservados para la
configuración de fábrica).
Para conseguir imágenes en resolución QQVGA (120x160 píxeles) hay que modificar los
siguientes registros:
En cualquier momento podemos reiniciar todos los registros a su valor por defecto
escribiendo 0x80 en el registro COM7 (0x12).
- detectar flancos de HREF de la cámara mediante el pin D7 del Nodemcu, para saber
cuándo empieza y acaba cada línea de la imagen y poder saber el valor de HREF en cada
momento, ya que solo se debe coger información cuando HREF está a HIGH
- detectar flancos de subida de PCLK de la cámara mediante el pin D5 del Nodemcu, para
saber cuándo coger la información de los bits D[7-0], siempre que HREF esté a HIGH
Por defecto, la información de la cámara sale en formato YCbCr, también conocido como
YUV, en el cual la Y indica la luminancia (cantidad de blanco) y Cb y Cr son las crominancias
(diferencia de azul y diferencia de rojo). Es una forma de codificar RGB.
De esta forma, hay un valor de Y para cada píxel, pero los valores de Cb y Cr son
compartidos por cada dos píxeles consecutivos.
PHP es un lenguaje de programación ejecutado por servidores para generar páginas web.
Json es una forma de gestionar gran cantidad de información de forma sencilla y eficaz.
Sirve para el intercambio de datos, almacenándolos con una sintaxis estructurada que
puede ser leída por cualquier lenguaje de programación.
www.esp8266.netai.net/savepixelsYCr.php?line=0&number=2&data1=F0A2...&data2=C2
7B...
- Después se escribe el símbolo ‘?’, tras el cual vienen las variables que podrá utilizar el
archivo .php:
o ‘line’ es una variable tras la cual se indica el número de la primera fila que se envía.
o ‘number’ es una variable tras la cual se indica cuántas filas se envían en la url.
o ‘data1’ y ‘data2’ son las variables tras las que se indica la información de la primera
y segunda fila enviada. Dicha información se envía en hexadecimal (un byte de 0 a
255 se convierte en dos caracteres entre ‘00’ y ‘FF’).
Como una url solo puede tener un máximo de 2000 caracteres, solo se pueden enviar unas
pocas filas de la imagen en cada url. Por ello se ha decidido enviar la información de 2 en 2
filas (el arduino tarda unos 8-10 segundos en enviar toda esta información y se ha comprobado
que enviar más filas en una misma url apenas influye en este tiempo, pero es más cómodo enviar
estas filas de 2 en 2 por ser divisor de 100).
El archivo .php mencionado en la url anterior crea un archivo .json llamado dataYCr001.json
en el que guarda los valores de las variables recibidas de la url como se puede ver en el Código
4.
{
"info":{
"f":"0",
"n":"2",
"lines":[
"F0A2…",
"C27B…"
]
}
}
Código 5: Archivo dataYCr001.json estructurado
- La información se guarda en objetos englobados por llaves: el objeto ‘info’ contiene los
objetos ‘f’, ‘n’ y ‘lines’:
o ‘lines’ guarda los valores de las variables ‘data1’ y ‘data2’ de la url en un vector
englobado por corchetes y separados por comas.
- Los nombres de los objetos se escriben entre comillas por ser cadenas de caracteres.
- Los valores de los objetos se pueden escribir entre comillas si son cadenas de caracteres,
o sin comillas si se quiere que sean interpretados como números enteros.
Como hay que enviar la información correspondiente a 100 filas y estas se envían de 2 en 2,
se crean 50 archivos en el servidor:
…………………………………………………………
Para que la herramienta posteriormente utilizada (Matlab, Android o cualquier otra) pueda
descargar la información de manera más rápida, se decidió juntar la información de todas las
filas en un solo archivo.
Una vez enviada la información de todas las filas, el arduino hace un POST incluyendo el
nombre del archivo joinlinesYCr.php, el cual lee los archivos desde el dataYCr001.json hasta
el dataYCr050.json y almacena toda la información en otro archivo .json llamado
imageYCr001.json. El siguiente archivo se llamara automáticamente imageYCr002.json, ya
que el anterior ya existe, el siguiente imageYCr003, y así sucesivamente.
En este nuevo archivo .json, la información se guarda como se puede ver en el Código 6.
{"info":{"height":"100","width":"100","type":"hex","lines":["F0A2…","C27B…","…",…
,"…"]}}
Código 6: Archivo imageYCr001.json
{
"info":{
"height":"100",
"width":"100",
"type":"hex",
"lines":[
"F0A2…",
"C27B …",
"…",
… (información del resto de líneas)
"…"
]
}
}
Código 7: Archivo imageYCr001.json estructurado
url = 'http://www.esp8266.netai.net/imageYCr001.json'
data = urlread(url)
Código 8: Lectura de una url con Matlab
jsonobj = JSON.parse(data);
Código 9: Utilización de la librería JSON.m
De esta forma podemos acceder a la información de cada objeto del archivo .json mediante
punteros. Podemos ver un ejemplo de ello en el Código 10 (obtención de la altura de la imagen).
jsonobj.info.height
Código 10: Acceso a los objetos del archivo .json mediante punteros
height=str2num(jsonobj.info.height);
width=str2num(jsonobj.info.width);
Código 11: Transformar una String en un número entero
for j=1:height
s=char(jsonobj.info.lines(1,j));
Código 12: Obtención de una cadena de caracteres del archivo .json
La información de los píxeles de cada fila se guarda en la variable “s” en formato YCbCr y
en hexadecimal. Por ser valores de 0 a 255, en “s” están guardadas parejas de caracteres de “00”
a “FF” de los valores de Y, Cb y Cr en el orden ya mencionado: Cb12, Y1, Cr12, Y2, Cb34, Y3,
Cr34, Y4… Por lo tanto, debemos usar el Código 13 para transformar dichas parejas de caracteres
a decimal.
for k=1:2:width
ind=4*(k-1)+1;
cb=hex2dec(s(ind:ind+1));
y1=hex2dec(s(ind+2:ind+3));
cr=hex2dec(s(ind+4:ind+5));
y2=hex2dec(s(ind+6:ind+7));
Código 13: Obtención del valor YCbCr de cada píxel
Se obtienen así las luminancias y crominancias de cada píxel. Para cada par de píxeles: los
valores del primer píxel son y1-cb-cr, y los del segundo píxel son y2-cb-cr
R = Y + 1.14*Cr
G = Y - 0.396*Cb - 0.581*Cr
B = Y + 2.029*Cb
Los valores obtenidos se guardan en una matriz de 100x100x3, correspondiendo las dos
primeras dimensiones al tamaño de la imagen y la tercera dimensión al color R, G o B.
Una vez se ha conseguido la información de todos los píxeles de la imagen, esta se muestra
mediante la función del Código 14, siendo “píxeles” la matriz de 3 dimensiones mencionada.
imshow(pixels)
Código 14: Función para crear una imagen a partir de una matriz de 3 dimensiones
El tiempo transcurrido entre mostrar una imagen y otra son unos 4-5s
Se ha diseñado una aplicación en Android Studio para descargar las imágenes del servidor
en el móvil y poder visualizarlas.
String response="";
DefaultHttpClient client = new DefaultHttpClient();
HttpGet httpGet = new HttpGet(url);
try {
HttpResponse execute = client.execute(httpGet);
InputStream content = execute.getEntity().getContent();
} catch (Exception e) {
e.printStackTrace();
}
return response;
Para obtener los objetos que nos interesan, se utiliza el Código 17. Al igual que en el anterior
apartado, hay que convertir los valores de “height” y “width” a enteros. Esto se hace mediante
el método “Integer.parseInt()”.
imageComponents.height=Integer.valueOf(jsonObject.getJSONObject("info").getString("
height"));
imageComponents.width=Integer.valueOf(jsonObject.getJSONObject("info").getString("
width"));
JSONArray jsonArray = jsonObject.getJSONObject("info").getJSONArray("lines");
for(int i=0; i<imageComponents.height; i++){
imageComponents.lines[i]=jsonArray.getString(i);
}
Código 17: Acceso a los objetos del archivo .json mediante la librería JSON de Android
Del mismo modo que en el anterior apartado, la información de cada línea se guarda en una
variabe “s”, de la cual se obtienen los valores Y, Cb y Cr de cada par de píxeles (Código 18).
Para transformar cada par de caracteres de hexadecimal a decimal, se utiliza otra vez el método
“Integer.parseInt()”, pero ahora indicándole 16 como segundo parámetro para que sepa que la
cadena introducida como primer parámetro se encuentra en hexadecimal.
String s=imageComponents.lines[j];
for(int k=0; k<WIDTH; k+=2) {
int ind=4*k;
cb = Integer.parseInt(s.substring(ind,ind+2), 16);
y1 = Integer.parseInt(s.substring(ind+2,ind+4), 16);
cr = Integer.parseInt(s.substring(ind+4,ind+6), 16);
y2 = Integer.parseInt(s.substring(ind+6,ind+8), 16);
Código 18: Obtención del valor YCbCr de cada píxel
Se utilizan las mismas fórmulas que antes para transformar YCbCr en RGB:
R = Y + 1.14*Cr
G = Y - 0.396*Cb - 0.581*Cr
B = Y + 2.029*Cb
imageView.setImageBitmap(bitmap);
Código 20: Obtención del valor YCbCr de cada píxel
Todos estos pasos se realizan en diferentes tareas asíncronas (llamada asyncTask) para no
bloquear el hilo principal del programa.
http://www.esp8266.netai.net/sendYesOrNo_write.php?answer=YES
Si no se desea que el Nodemcu capture una imagen y la envíe se debe escribir “NO” en vez
de “YES” al final de la url (cualquier otra cosa también impide que el Nodemcu capture
imágenes y las envíe).
En la aplicación Android se puede dar la orden de escribir “Yes” o “No” en dicho archivo
.json tan solo haciendo click en el cuadrito pequeño para poner un click (Figura 50).
PCLK con respecto a XCLK (Figura 51): ambas presentan la misma frecuencia (10MHz)
ya que el prescalado es 1. PCLK aparece un poco retrasada con respecto a XCLK.
VSYNC con respecto a HREF (Figura 52): VSYNC es un pulso pequeño (ya que solo
indica cuándo empieza una imagen), comparado con HREF, que está compuesto por muchos
pulsos representantes de cada fila de la imagen (Figura 53). Como indicaba el datasheet, HREF
no da pulsos cerca del momento en que VSYNC da cada pulso. Contando el número de
cuadrículas y sabiendo el tiempo que representa cada una, sabemos que entre dos pulsos de
VSYNC hay 80ms, por lo que se obtiene una imagen cada 80 ms, lo que se corresponde con
1/0.08 = 12.5 fps (imágenes por segundo).
Con la misma frecuencia en XLCK (10MHz), se tuvo que prescalar la señal de salida PCLK,
ya que el nodemcu no era lo suficientemente rápido y se reiniciaba (dicho problema se explicará
en el siguiente apartado). Se eligió el mayor prescalado posible (32), ya que otros daban también
problemas, obteniendo una frecuencia de 10MHz/32 = 312.5kHz. Se observó:
PCLK con respecto a XCLK (Figura 54Figura 51): XCLK es 32 veces más rápido. La
frecuencia resultante exacta es 312.488kHz. La señal se ve más cuadrada ya que ahora los
tiempos de subida y bajada de las señales son ínfimos en comparación con el tiempo en HIGH
o LOW
VSYNC con respecto a HREF (Figura 55Figura 52): VSYNC se sigue viendo como un
pulso pequeño comparado con HREF, que está compuesto por muchos pulsos representantes
de cada fila de la imagen (Figura 56Figura 53). Ahora el tiempo entre dos pulsos de VSYNC es
2.5s, por lo que se obtiene una imagen cada 2.5s, lo que se corresponde con 1/2.5 = 0.4 fps.
Obtenemos el mismo resultado si dividimos el resultado de antes (12.5fps) entre 32.
Figura 51: Señales de la cámara mediadas en el osciloscopio (XCLK en amarillo, PCLK en azul)
Figura 52: Señales de la cámara mediadas en el osciloscopio (HREF en amarillo, VSYNC en azul)
Figura 54: Señales de la cámara mediadas en el osciloscopio, prescalado de 32 (XCLK en amarillo, PCLK en azul)
Figura 55: Señales de la cámara mediadas en el osciloscopio, prescalado de 32 (HREF en amarillo, VSYNC en azul)
- D1-4 del Nodemcu con D[7-4] de la cámara (en este orden), para leer los bits más
significativos de cada píxel, ya que, como se ha comentado, el Nodemcu no tiene suficientes
pines.
- D5 del Nodemcu con PCLK de la cámara, para saber cuándo leer cada píxel
- D6 del Nodemcu con VSYNC de la cámara, para saber cuándo empieza cada imagen
- D7 del Nodemcu con HREF de la cámara, para saber cuándo empieza y acaba cada fila
de la imagen
Debido a este error se decidió prescalar la señal de reloj de PCLK, para que el Nodemcu
tuviera tiempo de atender todas las peticiones de interrupción de PCLK.
Se probó con pequeños prescalados pero seguía produciéndose el mismo error, solo que más
tarde, por lo que se decidió utilizar el mayor prescalado posible (32) para no tener problemas.
Las señales obtenidas con este prescalado ya se han comentado en el apartado anterior
(Figura 54, Figura 55 y Figura 56).
En las funciones que atienden las interrupciones de HREF y PCLK, existen contadores para
contar el número de filas y bytes, respectivamente, que extrae la cámara. Dividiendo el número
de bytes entre el número de filas y entre 2 (2B/píxel), se puede obtener el número de columnas.
Estas cantidades se muestran por pantalla (Figura 58) y así se puede comprobar que la cámara
está bien configurada y que se cogen los 100x100 píxeles de la parte superior izquierda de la
imagen.
Figura 59: Imagen obtenida con Matlab para la resolución VGA 480x640 con la cámara tapada
Figura 60: Imagen obtenida con Matlab para la resolución VGA 480x640 con una hoja blanca delante
Figura 61: Imagen obtenida con Matlab para la resolución QQVGA 120x160 con la cámara tapada
Figura 62: Imagen obtenida con Matlab para la resolución QQVGA 120x160 con una hoja blanca delante
Capítulo 5 - Conclusiones
Es importante explorar los posibles motivos por los que no se ha conseguido obtener
imágenes reales:
- Sin embargo, también cabe la posibilidad de que la cámara se haya podido estropear en
algún momento durante todas las pruebas realizadas y, cuando finalmente se ha empezado a
capturar la información de los píxeles, estos no proporcionan los colores reales.
Hay que destacar que, después de todas las pruebas realizadas y la cantidad de problemas
encontrados, existe la posibilidad de que la placa de programación utilizada (el Nodemcu esp-
12e) no fuera la mejor elección.
De todas formas, se puede decir que el proceso de aprendizaje ha sido amplio, pues se han
desarrollado conocimientos en diferentes disciplinas:
- Electrónica Analógica: al tener que construir un circuito oscilador para obtener una señal
de reloj.
Mientras que la cámara es de muy buena calidad, la placa de programación utilizada ha sido
el factor limitante. Por ello, es importante investigar otros métodos de obtención de la
información de la cámara. Utilizar otra placa por ejemplo:
y buscar un método para conseguir conexión a internet y poder recibir las imágenes en
dispositivos lejanos. Ejemplos de cómo lograr esto podrían ser:
Una vez conseguidas imágenes, si el tiempo entre que se empieza a recoger información de
la cámara hasta que se recibe en el dispositivo destinado a procesarla es muy pequeño, se podrán
conseguir imágenes a intervalos de tiempo muy reducidos, permitiendo visualizar imágenes en
tiempo real, para por ejemplo pilotar un dron e incluso aplicar algoritmos de detección de
movimiento y previsión de trayectorias.
Es importante mencionar que este diagrama se empezó a pensar en febrero pero, al haberse
investigado poco aún sobre los módulos que había que utilizar, se decidió aplazar hasta tener
una idea más amplia de lo que iba a necesitar el proyecto. Así, la planificación temporal se
realizó a principios de marzo, cuando ya se sabía que habría que construir un circuito oscilador.
De todas formas las fechas son orientativas y, aunque al principio se acercaron a cómo se
fue desarrollando el proyecto, después no fue posible debido a trabajos, exámenes finales y
problemas que fueron surgiendo, por lo que se tuvo que retrasar la fecha de finalización y
entrega hasta julio. Fue la documentación de este trabajo lo que más tiempo llevó y lo que más
se retrasó, realizando una extensa mayoría en julio, y compaginándolo con más pruebas para
intentar conseguir imágenes reales, lo que finalmente no fue posible.
Cabe destacar el acierto con el tiempo necesario a investigar sobre los módulos pues, aunque
ya se sabía lo que hacía falta, fue sumamente necesario seguir investigando para no cometer
errores.
7.3. Presupuesto
El tiempo dedicado a este proyecto ronda las 420h, por lo que el salario establecido ha sido:
3150€.
A este salario hay que añadirle los gastos de material. En la Tabla 4 se indican los costes de
módulos, cables, el convertidor y la protoboard utilizada. Se compraron un par de cada módulo
para tener de reserva por si alguno se estropeaba, lo cual ocurrió con una de las cámaras. En la
Tabla 5 se muestran los costes de fabricación del circuito oscilador, a los cuales hay que
sumarles los costes de los componentes que también se compraron para realizar pruebas pero
que al final sobraron (Tabla 6). Hay que destacar que el precio de condensadores y resistencias
son muy pocos céntimos y por ello no se han incluido.
Componente Precio por unidad (€/ud) Unidades (ud) Precio total (€)
Nodemcu esp12-e 3.88 2 7.76
Cable usb 0.72 2 1.44
Cámara OV7670 3.58 2 7.16
Pack de cables 2 1 2
Protoboard pequeña 0.4 1 0.4
Convertidor a 3.3V 0.75 1 0.75
Total 19.51
Tabla 4: Presupuesto de los componentes básicos
Componente Precio por unidad (€/ud) Unidades (ud) Precio total (€)
Cristal de cuarzo 10MHz 0.19 1 0.19
Inversor SN74HC04 0.25 1 0.25
Placa perforada 0.3 1 0.3
Total 0.74
Tabla 5: Presupuesto de los componentes del circuito oscilador
Componente Precio por unidad (€/ud) Unidades (ud) Precio total (€)
Cristal de cuarzo 20MHz 0.16 1 0.16
Cristal de cuarzo 16MHz 0.21 1 0.21
Cristal de cuarzo 2MHz 1.16 1 1.16
Inversor SN74AC04 0.23 1 0.23
Total 1.76
Tabla 6: Presupuesto de los componentes sobrantes utilizados en pruebas
Índice de Figuras
Índice de Tablas
Tabla 1: Correspondencia de los bits 15 a 0 de los registros con las salidas digitales del Nodemcu .... 17
Tabla 2: Modificación de registros de la cámara OV7670 para la resolución QQVGA ....................... 54
Tabla 3: Tareas del diagrama de Gantt (representado en la Figura 64) ................................................. 79
Tabla 4: Presupuesto de los componentes básicos ................................................................................ 81
Tabla 5: Presupuesto de los componentes del circuito oscilador .......................................................... 81
Tabla 6: Presupuesto de los componentes sobrantes utilizados en pruebas .......................................... 81
Índice de Códigos
Código 1: declaración de posiciones de memoria para la lectura de los registros asociados a los pines
del nodemcu .......................................................................................................................................... 15
Código 2: Variar un pin digital continuamente ..................................................................................... 40
Código 3: Asignar función a un pin del nodemcu ................................................................................. 45
Código 4: Archivo dataYCr001.json ..................................................................................................... 56
Código 5: Archivo dataYCr001.json estructurado ................................................................................ 57
Código 6: Archivo imageYCr001.json .................................................................................................. 58
Código 7: Archivo imageYCr001.json estructurado ............................................................................. 58
Código 8: Lectura de una url con Matlab .............................................................................................. 59
Código 9: Utilización de la librería JSON.m......................................................................................... 59
Código 10: Acceso a los objetos del archivo .json mediante punteros.................................................. 59
Código 11: Transformar una String en un número entero ..................................................................... 59
Código 12: Obtención de una cadena de caracteres del archivo .json ................................................... 59
Código 13: Obtención del valor YCbCr de cada píxel .......................................................................... 60
Código 14: Función para crear una imagen a partir de una matriz de 3 dimensiones ........................... 60
Código 15: Lectura de una url con Android .......................................................................................... 61
Código 16: Utilización de la librería JSON de Android........................................................................ 61
Código 17: Acceso a los objetos del archivo .json mediante la librería JSON de Android .................. 61
Código 18: Obtención del valor YCbCr de cada píxel .......................................................................... 62
Código 19: Guardado de los valores de RGB en una variable Bitmap ................................................. 62
Código 20: Obtención del valor YCbCr de cada píxel .......................................................................... 62
Bibliografía
[8] L. Llamas, «Luis Llamas: Ingenieía, informática y diseño,» [En línea]. Available:
http://www.luisllamas.es/2015/08/salidas-analogicas-pwm-en-arduino/. [Último acceso: Junio
2016].