En esta entrada os presento mi idea para el concurso de Particle en Hackster.io. En mi caso voy a usar la placa de desarrollo de Particle, para desarrollar un prototipo de proyecto V2V. Básicamente lo que quiero hacer es un simulador de comunicaciones entre vehículos (de ahí el V2V), los cuales se transmiten estados de la carretera o eventos para mejorar la conducción. Este prototipo estaría más orientado a su uso en vehículos autónomos sabrían la situación de la carretera desde antes. Antes de seguir, podéis ver otro proyecto que presenté donde hacía una red neuronal en Ada y la ejecutaba en un STM32f4 para detectar patologías del corazón: “Low-cost ECG con Inteligencia Artificial”
La idea es usar las capacidades de las placas de desarrollo de Particle, de conectarse muy fácilmente a la red, para publicar eventos. En mi ejemplo, el vehículo se subscribiría a los eventos de la ciudad en la que esté. Estos eventos serían, por ejemplo, una calle cortada, mucho tráfico en una zona, mal estado de la calzada, etc. Además de recibir estos eventos, los propios vehículos o los usuarios, podrían publicarlos para que el resto los recibiesen.
La idea
Concretamente tenía pensado dividir el prototipo en dos partes: la primera, en la cual mi “coche” se encontrase un obstáculo, lo detectase y enviase una señal; y la segunda, que el coche fuese a una velocidad X y al recibir la señal de que ha habido un accidente, la disminuyese.
Para montar todo este prototipo vamos a necesitar, además del software, algo que se mueva. Con un par de sensores para detectar los obstáculos y poco más.

Diagrama de bloques
Hardware
Como hardware vamos a usar la mota de Particle de la cual hice una pequeña introducción: Introduccion a Particle y su IoT con el kit Argon. Además, vamos a poner un pequeño coche controlado por la mota, con algún sensor para detectar un obstáculo. Si queréis hacer el mismo proyecto, os dejo donde podéis comprar bastante barato lo básico que necesitáis:
En mi caso además he usado una batería LiPo de 2 celdas para alimentar al conjunto y no tener que tenerlo conectado físicamente a la corriente. A continuación os dejo un pequeño diagrama de las conexiones.
Software
Para controlar el coche vamos a programarlo con el IDE de Particle. La lógica que voy a implementar es muy sencilla. El coche se podrá manejar de manera manual mandándole comandos de dirección, en este caso el vehículo se moverá hasta encontrar un obstáculo. En el modo ruta, el coche se moverá siguiendo una especie de ruta muy sencilla y de la misma manera, cuando detecte un obstáculo, se detendrá y emitirá un evento para avisar al resto de vehículos conectados. Como en este caso el código es un poco extenso ( y bastante sencillo) voy a pasar por encima y hablaré principalmente de las partes más críticas. (Os dejo enlace al código completo en GitHub).
El software lo voy a dividir en 3 partes principales: la clase que coche que se encargará de recibir comandos de movimiento y generar las señales PWM adecuadas, la clase SensorUltradound para manejar la comunicación con el sensor hc-sr04, y por último el bucle principal donde se organizan las llamadas a ambas clases.
Clase “CarController”
La clase CarController
es la encargada de entender como se mueve el coche, y que señales hay que generar para que se mueva de una manera concreta. El interfaz básico sería algo como el que sigue a este parrafo. El código está reducido y he eliminado muchas partes (no olvideis mirar el github si os interesa).
Lo que queremos es que desde el bucle principal podamos llamar a la función Move()
para que el coche se mueva, y además poderle cambiar la dirección y el modo de circulación. Además, la variable MaxSpeed
nos servirá en nuestra demo para que cuando se reciba un evento de que hay que reducir la velocidad, simplemente cambiaremos esta variable y el coche se moverá más lento.
class CarController{ private: struct Waypoint wp_list[10]; // 10 waypoints int wp_list_num; // Numero de waypoints usados en realidad int maxSpeed; // Maxima velocidad del vehículo int pins[2][2]; // Pines donde está conectado el controlador del motor enum DIRECTION currDirection; // Marca la dirección en la que se tiene que mover el Coche; enum MODE modo; // Modo de funcionamiento ( Ruta o Simple) public: CarController(int right_pin1, int right_pin2, int left_pin1, int left_pin2); ~CarController(){}; // Inicializa los pines y las variables necesarias void Init(); // Mueve en coche en función del modo en el que está void Move(); // Se le puede cambiar la dirección de movimiento desde el programa principal int setDirection(String cmd); }
Para mover los motores a una velocidad variable hace falta generar una señal PWM. De esta manera, podemos controlar a través del “Duty Cycle” la velocidad de giro de cada motor. En Particle esto se consigue mediante la función analogWrite
sobre un puerto digital en modo OUTPUT. Esta función admite tres parámetros, el pin en el que se generará la señal PWM, el ciclo de trabajo siendo 0 todo el rato el pin a nivel bajo y 255 todo el tiempo a nivel alto, y por último la frecuencia de la señal PWM.
Por ejemplo, si queremos poner el PIN A1, con una señal PWM al 50% y a la máxima frecuencia posible, tendríamos que hacer:
// setup pinMode(A1, OUTPUT); //loop int maxFreq = analogWriteMaxFrequency(A1); analogWrite(A1, 125, maxFreq / 2);
Clase “SensorUltrasound”
Esta es la clase encargada de manejar la comunicación y los cálculos del Sensor de Ultrasonidos (hc-sr04). El interfaz de este sensor es muy sencillo, solo tenemos las funciones de inicilizar el sensor y la de medir. El funcionamiento del sensor y como medir lo podeis encontrar en zonamaker.
class UltraSoundSensor{ private: int triggerPin; int echoPin; // Last measured time unsigned long echotime; public: UltraSoundSensor(int trigger, int echo); ~UltraSoundSensor(){}; void Init(); float Measure(); };
En mi caso, dado que el sensor está pensado para trabajar con voltajes de 5V y el particle Argon es una tarjeta de 3.3V, tengo que hacer un apaño. En el camino de ida (cuando ponemos el pin de Trigger a ON) el Particle mandará un pulso de 3.3V, pero el sensor será capaz de leerlo sin problema (soporta hasta 5V). Sin embargo, en el camino de vuelta, el ultrasonidos nos responderá con una señal de 5V, lo cuál podría destrozar nuestra Particle. Por lo tanto, tendremos que hacer un divisor de tensión para bajar el voltaje de vuelta a un nivel que no nos queme nuestra placa.

Divisor de tensión para el hc-sr04
Programa principal
Por último, la parte más importante del programa, ya que conecta entre sí a todas las demás, es el programa principal. El Particle se programa como si fuera un arduino, por lo que al abrir el IDE ya tenemos las funciones de [loop
y setup
preparadas.
En nuestro Setup
tenemos que Inicializar el Controlador del coche y el Sensor, y además publicar todas las variables y funciones compartidas. Por otro lado el loop
se encargará de llamar a la función Move
del coche y de medir la distancia que detecte el sensor cada 200ms.
CarController Car( A4, A3, A0, A1); UltraSoundSensor Sensor(D3, D6); unsigned long int lastTime; int periodMillis = 200; void setup() { Car.Init(); Sensor.Init(); Particle.function("setDirection", pChangeDir); Particle.function("setMode", pSetMode); Particle.function("SensorTest", pSensorTest); Particle.function("Speed", pSetSpeed); } void loop() { unsigned long elapsed; Car.Move(); cDir = Car.getDirection(); if( Sensor.Measure() < 10.0f){ Particle.publish("motion-detected", PUBLIC); pSetMode("simple"); } elapsed = millis() - lastTime; if ( elapsed < periodMillis ) delay(periodMillis - elapsed); }
Además, que es la parte más interesante de Particle, tenemos que conectar las funciones que se ejecutarán cuando se le envíen comandos a través del interfaz web. Estas funciones son las que están definidas en el segunda parametro de Particle.function(nombre_web, nombre_funcion)
. En nuestro caso, son funciones que se encargan de abstraer las clases de CarController y SensorUltrasound, para que se puedan manejar desde la Web.
int pChangeDir(String cmd){ return Car.setDirection(cmd); } int pSetMode(String cmd){ if( cmd == "route"){ return Car.setModeRoute(); }else if(cmd=="simple"){ Car.setDirection("none"); return Car.setModeSimple(); }else{ Car.setDirection("none"); return Car.setModeSimple(); } } int pSensorTest(String cmd){ if( cmd == "dist" ){ return int(Sensor.Measure()); } return -1; } int pSetSpeed(String cmd){ int value; value = cmd.toInt(); //return value; if(value > 0 && value < 255){ return Car.setMaxSpeed(value); } return -1; }
Esto es un poco todo lo que he implementado hasta la fecha, el coche ya se puede manejar tanto de manera simple (enviando direcciones), como dejándolo en modo ruta. Además, se para al encontrar un obstáculo. Subiré un video en cuanto pueda!
1 comentario
Carlos Francisco Ortiz Caporal · 02/05/2022 a las 22:50
Muy buen trabajo, con tu post me has ayudado a establecer dirección en mi proyecto, el cual es similar al tuyo. GRACIAS