Estación Sensorial Móvil

Autores

  • Ángel Baeza Sánchez
  • Javier Raúl Alonso Tejera
  • Vicente Manuel Aguilera Gallardo

Introducción

Para el proyecto de la asignatura de Diseño de Sistemas Empotrados se ha decidido proceder a la construcción y programación de un vehículo de tres ruedas con diversos sensores los cuales aportan diversas funcionalidades; se ha bautizado el vehículo como Estación Sensorial Móvil. La Estación Sensorial Móvil (ESM para abreviar), cuenta con una multitud de sensores. Entre ellos se encuentran: Un sensor de distancia, un sensor de luminosidad y un sensor de infrarrojos.

El propósito final de la ESM es su uso bajo dos modos de funcionamiento, un modo automático, en el que la estructura se mueve automáticamente en línea recta utilizando sensores de distancia girando para evitar chocarse, y un modo “control”, en el que la ESM hace uso de su sensor de infrarrojos para poder ser controlada a distancia. Además, en ambos modos, la ESM utiliza el sensor de luminosidad para encender unos leds a modo de faros para poder situarla en la oscuridad.

Materiales y costes

MaterialCantidadPrecio Total
Motor 1.5V-2V23€
Cableado variado
Puente H (Controlador para los motores)15€
Ruedas motoras22.5€
Rueda “loca” adicional10.95€
Mando infrarrojo1
Alimentación (Pilas y baterías)
Materiales para la estructura5€
PCB1
Buzzer pasivo11€
Receptor infrarrojo10,3€
Sensor de distancia11€
Resistencias y transistores varios
Leds de posición20,20€
LDR10,20€
TOTAL19,15€ Aprox.
Hay electrónica varia que ya teníamos previamente y obviamente no ha habido que pagar.
Estos son los precios aproximados de la mayoría de piezas (Obviamos la placa Arduino UNO).

Circuito, Diseño e Implementación – Hardware

La ESM está formada por cinco componentes principales:

  1. Se ha construido una PCB personalizada, la cual integra un sensor LDR, un sensor de distancia, un sensor de infrarrojos, un buzzer y un par de leds.
  2. Una estructura motora compuesta por la propia estructura de madera ligera , dos ruedas traseras con dos motores, y una rueda móvil central.
  3. La placa de Arduino.
  4. Un Puente H para coordinar los motores.
  5. Un sistema de alimentación propio.

A la hora de la construcción de la ESM, en primer lugar, se ha medido el tamaño de la placa de Arduino y de la PCB que se iba a utilizar, y se ha elegido una plancha de madera para sostener la circuitería de un tamaño acorde, dejando espacio para la batería y el resto de componentes.

Una vez lista la plancha de madera, se le han adherido las ruedas traseras, con sus respectivos motores y se ha atornillado una rueda móvil a la parte frontal de la estructura.

Una vez listo el esqueleto del proyecto, se ha procedido a pegar la placa de Arduino a este, y a soldar los diferentes componentes a la PCB la cual se ha adherido también al cuerpo principal.

Finalmente, se ha construido un sistema de alimentación utilizando celdas de baterías de un portátil en desuso, creando una batería de unos 12 voltios. Esta batería está separada en dos nodos, uno para suministrar un voltaje de 3V a los motores, y otro para suministrar menos de 12V a la placa. Una vez todos los componentes han sido construidos, se han conectado todos los componentes del sistema mediante cables.

Una vez el proyecto ha alcanzado este punto, se ha comenzado a desarrollar el código para su correcto funcionamiento, a la vez que se han pulido diferentes apartados de la estructura que no funcionaban del todo bien.

Funcionamiento – Software

La implementación software se basa en la utilización de una máquina de estados para poder controlar los diferentes modos de funcionamiento y entradas recibidas de forma más correcta.

En principio denominamos 5 estados posibles:

  1. Idle

Es el centro de todos los estados, permite pasar al resto de estados. Es el equivalente a un punto muerto. Almacena el código para activar la luz en función del foto-resistor.

2. Navegación

En este modo, la ESM avanza indefinidamente, cuando el sensor de ultrasonidos detecta una superficie, frena y gira hacia la derecha hasta que deja de detectar un obstáculo. Sigue girando durante una fracción de segundo más para asegurar que no choca con nada, y posteriormente retoma su avance.

a. Adelante -> Estado sobre el que avanza hasta detectar obstáculo.

b. Girar -> Estado en el que gira hasta dejar de detectar obstáculo.

3. Control

En este estado se permite controlar la ESM con el mando a distancia.

Cuando se presiona un botón, la ESM realiza esa acción en bucle hasta que se cancele esa opción presionando otro botón.También se incluye un botón de frenado para pausar el movimiento del coche.

4. Alarma

Si el sensor de ultrasonidos encuentra un obstáculo a una distancia d, suena una alarma a un volumen v. Tanto V como D son configurables usando el mando.

5. Piano

Se le asocia un tono del buzzer a cada uno de los números del mando. Esto permite utilizar la ESM como un piano móvil.

La frecuencia del sonido del buzzer se puede configurar también utilizando el mando.

Problemas y Soluciones

El primer problema encontrado fue al terminar el esqueleto del proyecto, y es que se había pensado que la ESM fuese de tracción a cuatro ruedas, sin embargo, debido a la calidad de las ruedas empleadas, esto impedía que girase de forma correcta. La solución fue sustituir las dos ruedas delanteras por una rueda móvil singular, facilitando así el giro del prototipo.

Otro de los problemas principales que se han encontrado a lo largo del desarrollo es la dificultad que conlleva soldar, ya que, al comenzar el proyecto, se decidió utilizar componentes propios de los alumnos, en lugar de los recibidos por el profesor con el objetivo de no tener que desmontar el proyecto. Por otra parte, aunque quede un buen resultado, hay que tener cuidado al soldar y tener algo de habilidad para no ensuciar en demasía y ocasionar problemas de funcionalidad.

Un problema menor que hemos encontrado ha sido la dificultad a la hora de entenderse con el puente H, puesto que no respondía correctamente a las órdenes de código; Afortunadamente, al cambiarlo por otro, el prototipo volvió a funcionar correctamente.

El problema final que se ha encontrado ha sido que, una vez terminado el prototipo, faltaba potencia a la hora de mover la ESM. En un primer momento se pensó que era cosa del peso, por lo que se reconstruyó toda la estructura para reducirlo. Esto no fue suficiente, por lo que se valoró cambiar los motores del proyecto, sin embargo, al final, bastó por cambiar la fuente de alimentación de los motores por la batería fabricada.

Con el fin de mejorar el rendimiento y funcionalidad general de la estación, sería recomendable optar por motores con mayor potencia (Unas ruedas de mejor calidad también ayudarían).

Código

#include <IRremote.h>  //including infrared remote header file     

const int pResistorPin = A0;

const int IRPin = 2; // the pin where you connect the output pin of IR sensor7

const int LEDPin = 3;
const int buzzerPin = 4;

const int A1A = 5;
const int A1B = 6;

const int pingPin = 7; // Trigger Pin of Ultrasonic Sensor
const int echoPin = 8; // Echo Pin of Ultrasonic Sensor

const int B1A = 9;
const int B1B = 10;

//IR VALUES
/*
  RED: 0x33B8807F
  GREEN: 0x33B8A05F
  YELLOW: 0x33B8906F
  BLUE: 0x33B8B04F

  VOLUP: 0x33B8708F
  VOLDOWN: 0x33B840BF
  HOME : 0x33B8C03F

  UP: 0x33B8E01F
  DOWN: 0x33B822DD
  RIGHT: 0x33B848B7
  LEFT: 0x33B808F7

  1: 0x33B8827D
  2: 0x33B8A25D
  3: 0x33B8B24D
  4: 0x33B842BD
  5: 0x33B8629D
  6: 0x33B8728D
  7: 0x33B8C23D
  8: 0x33B8E21D
  9: 0x33B8F20D
*/

IRrecv irrecv(IRPin);
decode_results IRresults;
bool newIRvalue = false;

//IDLE VARIABLES
const float MAXLIGHT =  1400;
const float MINLIGHT = 200;
const float CHANGELIGHT = 50;
float lightFactor = 600;

//PIANO VARIABLES
float pianoIntensity = 1;
const float MINPIANO = 0.1;
const float MAXPIANO = 2;

const float CHANGEPIANO = 0.1;

//ALARM VARIABLES
const float MAXALARMDISTANCE = 510;
const float MINALARMDISTANCE = 10;
const float CHANGEALARMDISTANCE = 20;
float alarmDistance = MINALARMDISTANCE;

const float MAXALARMINTENSITY = 2;
const float MINALARMINTENSITY = 0.2;
const float CHANGEALARMINTENSITY = 0.1;

float alarmIntensity = MINALARMINTENSITY;

//NAVIGATION MODE
const int MINDISTANCE = 50;
const int DELAYTIME = 750;

//MODES
int currentMode;
const int IDLEMODE = 1;
const int NAVMODE = 2;
const int ALARMMODE = 3;
const int PIANOMODE = 4;
const int CONTROLMODE = 5;

int currentState;

bool homeIsActive = true;

//NAVIGATION STATE
const int FORWARDSTATE = 1;
const int TURNINGSTATE = 2;

void setup() {
  
  Serial.begin(9600); // Starting Serial Terminal
  Serial.println("STARTING");

  //Distance Sensor
  pinMode(pingPin, OUTPUT);
  pinMode(echoPin, INPUT);

  //IR Sensor
  irrecv.enableIRIn();

  //Light Sensor
  pinMode(pResistorPin, INPUT);

  pinMode(LEDPin, OUTPUT);
  digitalWrite(LEDPin, HIGH);

  pinMode(buzzerPin, OUTPUT);

  pinMode(B1A, OUTPUT);
  pinMode(B1B, OUTPUT);
  pinMode(A1A, OUTPUT);
  pinMode(A1B, OUTPUT);

  currentMode = IDLEMODE;
}

void loop() {
  
  newIRvalue = getIR();
  
  if (currentMode != IDLEMODE) {
    if (newIRvalue) {
      //If HOME has been pressed
      if (IRresults.value == 0x33B8C03F) {
        Serial.println("STARTING IDLE MODE");
        currentMode = IDLEMODE;

        stopMovement();

        tone(buzzerPin, 300, 200);
        blinkLEDs(50, 2);
        tone(buzzerPin, 500, 200);
        blinkLEDs(50, 2);
        tone(buzzerPin, 200, 200);
      }
    }
  }
  
  switch (currentMode) {

    //IDLE
    case IDLEMODE:
      //Serial.print("IR: ");
      //Serial.println(IRresults.value, HEX);
      idleMode();
      break;

    case NAVMODE:
      navMode();
      break;

    case ALARMMODE:
      alarmMode();
      break;

    case PIANOMODE:
      pianoMode();
      break;

    case CONTROLMODE:
      controlMode();

    default:
      break;
      
  }
}

///

void idleMode() {
  if (newIRvalue) {
    switch (IRresults.value) {

      //1 -> NAVIGATION
      case 0x33B8827D:
        digitalWrite(LEDPin, HIGH);

        Serial.println("STARTING NAVIGATION MODE");
        currentMode = NAVMODE;
        currentState = FORWARDSTATE;

        tone(buzzerPin, 100, 200);
        blinkLEDs(50, 2);
        tone(buzzerPin, 100, 200);
        break;

      //2 -> CONTROL
      case 0x33B8A25D:
        digitalWrite(LEDPin, HIGH);

        Serial.println("STARTING CONTROL MODE");
        currentMode = CONTROLMODE;
        currentState = -1;


        tone(buzzerPin, 150, 200);
        blinkLEDs(50, 2);
        tone(buzzerPin, 150, 200);
        break;

      //3 -> ALARM
      case 0x33B8B24D:
        digitalWrite(LEDPin, HIGH);

        Serial.println("STARTING ALARM MODE");
        currentMode = ALARMMODE;
        currentState = -1;


        tone(buzzerPin, 200, 200);
        blinkLEDs(50, 2);
        tone(buzzerPin, 200, 200);
        break;

      //4 -> PIANO
      case 0x33B842BD:
        digitalWrite(LEDPin, HIGH);

        Serial.println("STARTING PIANO MODE");
        currentMode = PIANOMODE;
        currentState = -1;


        tone(buzzerPin, 250, 200);
        blinkLEDs(50, 2);
        tone(buzzerPin, 250, 200);
        break;

      //Up -> Light Up
      case 0x33B8E01F:
        if (lightFactor < MAXLIGHT) {
          lightFactor += CHANGELIGHT;
          Serial.print("Light Factor up: ");
          Serial.println(lightFactor);

          tone(buzzerPin, 400, 150);


        } else {
          tone(buzzerPin, 100, 250);
          delay(270);

        }

        break;

      //Down -> Light Down
      case 0x33B822DD:
        if (lightFactor > MINLIGHT && lightFactor > 0) {
          lightFactor -= CHANGELIGHT;
          Serial.print("Light Factor Down: ");
          Serial.println(lightFactor);

          tone(buzzerPin, 240, 150);

        } else {
          tone(buzzerPin, 100, 250);
          delay(270);

        }
        break;

      default:
        break;

    }


  } else {
    if (getLight() < lightFactor) {
      digitalWrite(LEDPin, LOW);

    } else {
      digitalWrite(LEDPin, HIGH);
    }
  }

}

void navMode() {
  switch (currentState) {
    case FORWARDSTATE:
      if (getDistance() > MINDISTANCE) {
        moveForward(255);
      } else {
        currentState = TURNINGSTATE;
        stopMovement();
        Serial.println("NAVIGATION: Turning");
        delay(DELAYTIME);
      }

      break;

    case TURNINGSTATE:
      if (getDistance() < MINDISTANCE) {
        moveRight(255);

      } else {
        delay(DELAYTIME);
        currentState = FORWARDSTATE;
        Serial.println("NAVIGATION: Going forward");
        stopMovement();
        delay(DELAYTIME);
      }

      break;
  }
}


void controlMode() {
  const int intensity = 255;

  if (!newIRvalue) {
    return;
  }

  switch (IRresults.value) {
    
    //FORWARD
    case 0x33B8E01F:
      Serial.println("Moving Forward in Control");
      moveForward(intensity);
      break;

    //BACK
    case 0x33B822DD:
      Serial.println("Moving BackWard in Control");
      moveBackward(intensity);
      break;

    //RIGHT
    case 0x33B848B7:
      Serial.println("Moving Right in Control");
      moveRight(intensity);
      break;

    //LEFT
    case 0x33B808F7:
      Serial.println("Moving Left in Control");
      moveLeft(intensity);
      break;

    //STOP
    case 0x33B88877:
      Serial.println("Stoping Movement in Control");
      stopMovement();
      break;

    default:
      break;

  }
}


void alarmMode() {

  if (newIRvalue) {
    switch (IRresults.value) {
      //UP
      case 0x33B8E01F:
        if (alarmDistance < MAXALARMDISTANCE) {
          alarmDistance += CHANGEALARMDISTANCE;

          tone(buzzerPin, 400, 100);
          delay(120);
          tone(buzzerPin, 400, 100);

          Serial.print("Alarm Distance up: ");
          Serial.println(alarmDistance);

          delay(200);

        } else {
          Serial.println("Alarm Distace at maximum!");
          blinkLEDs(50, 4);
        }

        break;

      //DOWN
      case 0x33B822DD:
        if (alarmDistance > MINALARMDISTANCE && alarmDistance > 0) {
          alarmDistance -= CHANGEALARMDISTANCE;

          tone(buzzerPin, 200, 100);
          delay(120);
          tone(buzzerPin, 200, 100);

          Serial.print("Alarm Distance Down: ");
          Serial.println(alarmDistance);

          delay(200);

        } else {
          Serial.println("Alarm Distace at minimum!");
          blinkLEDs(50, 4);
        }

        break;

      //Volume Up
      case 0x33B8708F:

        if (alarmIntensity < MAXALARMINTENSITY) {
          alarmIntensity += CHANGEALARMINTENSITY;
          Serial.print("Alarm Volume up: ");
          Serial.println(alarmIntensity);

          tone(buzzerPin, 1000 * alarmIntensity, 100);
          delay(200);

        } else {
          blinkLEDs(100, 3);
        }

        break;

      //Volume Down:
      case 0x33B840BF:
        Serial.print("Alarm Volume Down: ");
        Serial.println(alarmIntensity);
        if (alarmIntensity > MINALARMINTENSITY) {
          alarmIntensity -= CHANGEALARMINTENSITY;

          tone(buzzerPin, 1000 * alarmIntensity, 100);
          delay(200);

        } else {
          blinkLEDs(200, 3);
        }

        break;

      default:
        break;

    }
  }

  if (getDistance() < alarmDistance) {

    tone(buzzerPin, 1000 * alarmIntensity, 30);
  }

}

void pianoMode() {
  const unsigned long DURATION = 100;

  if (!newIRvalue) {
    return;
  }

  switch (IRresults.value) {
    //1
    case 0x33B8827D:
      Serial.println("TONE 1");
      tone(buzzerPin, 100 * pianoIntensity, DURATION);
      blinkLEDs(DURATION * 0.2, 1);
      break;

    //2
    case 0x33B8A25D:
      Serial.println("TONE 2");
      tone(buzzerPin, 200 * pianoIntensity, DURATION);
      blinkLEDs(DURATION * 0.2, 1);
      break;

    //3
    case 0x33B8B24D:
      Serial.println("TONE 3");
      tone(buzzerPin, 300 * pianoIntensity, DURATION);
      blinkLEDs(DURATION * 0.2, 1);
      break;
    //4
    case 0x33B842BD:
      Serial.println("TONE 4");
      tone(buzzerPin, 400 * pianoIntensity, DURATION);
      blinkLEDs(DURATION * 0.2, 1);
      break;
    //5
    case 0x33B8629D:
      Serial.println("TONE 5");
      tone(buzzerPin, 500 * pianoIntensity, DURATION);
      blinkLEDs(DURATION * 0.2, 1);
      break;
    //6
    case 0x33B8728D:
      Serial.println("TONE 6");
      tone(buzzerPin, 600 * pianoIntensity, DURATION);
      blinkLEDs(DURATION * 0.2, 1);
      break;
    //7
    case 0x33B8C23D:
      Serial.println("TONE 7");
      tone(buzzerPin, 700 * pianoIntensity, DURATION);
      blinkLEDs(DURATION * 0.2, 1);
      break;
    //8
    case 0x33B8E21D:
      Serial.println("TONE 8");
      tone(buzzerPin, 800 * pianoIntensity, DURATION);
      blinkLEDs(DURATION * 0.2, 1);
      break;
    //9
    case 0x33B8F20D:
      Serial.println("TONE 9");
      tone(buzzerPin, 900 * pianoIntensity, DURATION);
      blinkLEDs(DURATION * 0.2, 1);
      break;

    //Volume Up
    case 0x33B8708F:
      Serial.print("Piano Volume Up: ");
      Serial.println(pianoIntensity);
      if (pianoIntensity < MAXPIANO) {
        pianoIntensity += CHANGEPIANO;

        tone(buzzerPin, 1000 * pianoIntensity, DURATION * 0.5);
        delay(DURATION * 0.5);
        tone(buzzerPin, 1000 * pianoIntensity, DURATION * 0.5);
        delay(DURATION * 0.5);

      } else {
        Serial.print("Volume at Maximum!");

        blinkLEDs(DURATION * 0.4, 3);
      }


      break;

    //Volume Down:
    case 0x33B840BF:

      if (pianoIntensity > MINPIANO && pianoIntensity > 0) {
        pianoIntensity -= CHANGEPIANO;
        Serial.print("Piano Volume Down: ");
        Serial.println(pianoIntensity);

        tone(buzzerPin, 1000 * pianoIntensity, DURATION * 0.5);
        delay(DURATION * 0.5);
        tone(buzzerPin, 1000 * pianoIntensity, DURATION * 0.5);
        delay(DURATION * 0.5);

      } else {
        Serial.print("Volume at Minimum!");

        blinkLEDs(DURATION * 0.4, 3);
      }
      break;

    default:
      break;
  }

}

///

void blinkLEDs(int t, int n) {

  for (int i = 0; i < n; i++) {
    digitalWrite(LEDPin, LOW);
    delay(t);
    digitalWrite(LEDPin, HIGH);
    delay(t);
  }

  digitalWrite(LEDPin, HIGH);
}

//MOVEMENT
void moveForward(int intensity) {
  digitalWrite(A1A, intensity);
  digitalWrite(A1B, 0);

  digitalWrite(B1A, 0);
  digitalWrite(B1B, intensity);
}

void moveRight(int intensity) {
  digitalWrite(A1A, intensity);
  digitalWrite(A1B, 0);

  digitalWrite(B1A, intensity);
  digitalWrite(B1B, 0);
}

void moveLeft(int intensity) {
  digitalWrite(A1A, 0);
  digitalWrite(A1B, intensity);

  digitalWrite(B1A, 0);
  digitalWrite(B1B, intensity);
}

void moveBackward(int intensity) {
  digitalWrite(A1A, 0);
  digitalWrite(A1B, intensity);

  digitalWrite(B1A, intensity);
  digitalWrite(B1B, 0);
}

void stopMovement() {
  digitalWrite(A1A, 0);
  digitalWrite(A1B, 0);

  digitalWrite(B1A, 0);
  digitalWrite(B1B, 0);
}

//SENSORS
long getDistance() {
  
  long duration;

  digitalWrite(pingPin, HIGH);
  delay(50);
  digitalWrite(pingPin, LOW);

  duration = pulseIn(echoPin, HIGH) * 0.01724;

  //Serial.print("DISTANCE: ");
  //Serial.println(duration);

  return duration ;

}

bool getIR() {
  if (irrecv.decode(&IRresults)) {

    if (IRresults.value != 0xFFFFFFFF) {
      irrecv.resume();
      return true;
    }
    irrecv.resume();

  }

  return false;
}

float getLight() {
  return analogRead(pResistorPin);
}

Vídeo Demostración

También te podría gustar...

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *