Morse-Talkie (Grupo 11)

Descripción del proyecto

El proyecto realizado es un dispositivo que actúa a modo de walkie-talkie, pudiendo comunicarse con otros dispositivos a través de radiofrecuencias usando la modulación ASK (u On-Off Keying). La peculiaridad de este dispositivo es que sus mensajes son transmitidos codificados en morse. Es decir, para transmitir un mensaje, el usuario tendrá que realizar pulsaciones de ciertas duraciones para codificar su mensaje. 

Este dispositivo cuenta con varios modos de funcionamiento, que serán explicados posteriormente, para que el usuario pueda enviar y recibir mensajes, tanto directamente como mensajes previamente guardados.

Características

  • Envío de mensajes codificados en morse utilizando radiofrecuencias. 
  • Almacenamiento de mensajes para su posterior envío. 
  • Recibo de mensajes codificados en morse utilizando radiofrecuencias. 
  • Lectura de pulsaciones para la codificación de los mensajes en morse. 
  • Reproducción de mensajes por sonido. 
  • Visualización de los mensajes en una pantalla LCD. 
  • Mensaje SOS en morse guardado en memoria por defecto.

Casos de uso

Caso de uso 1: Modo envío directo

En el modo de envío directo (modo 0), el usuario introducirá pulsaciones que serán recogidas por el sistema, procesadas y enviadas directamente de una en una. Las pulsaciones de tiempo inferior a 500 ms son consideradas un carácter morse “.” mientras que las superiores a 500 ms son consideradas un carácter morse “-”.

Durante la pulsación se emite un sonido a modo de información de que se está detectando la pulsación correctamente. 

Después de enviar una pulsación, si han pasado más de 2s sin recibir pulsación se considerará que el usuario ha finalizado el carácter real, y en el siguiente envío se incluirá un espacio que indica la separación entre caracteres.  

En este modo se muestra por pantalla cada carácter en morse que se está enviando.

Caso de uso 2: Modo guardar mensaje

En el modo de guardar mensaje (modo 1), el usuario introducirá pulsaciones que serán recogidas por el sistema, procesadas y almacenadas en la memoria del dispositivo. Las pulsaciones de tiempo inferior a 500 ms son consideradas un carácter morse “.” mientras que las superiores a 500 ms son consideradas un carácter morse “-“.  

En este modo, toda pulsación superior a los 5s borrará el mensaje almacenado en memoria, emitiéndose posteriormente un triple pitido indicando que se ha borrado el mensaje de memoria. 

Durante la pulsación se emite un sonido a modo de información de que se está detectando la pulsación correctamente. 

Después de guardar cada pulsación, si han pasado más de 2s sin recibir pulsación se considerará que el usuario ha finalizado el carácter y cuando se guarde la siguiente pulsación se incluirá un espacio que indica el fin de carácter.  

En este modo, en todo momento se muestra por pantalla el mensaje almacenado en memoria. 

Caso de uso 3: Modo envío memoria

En el modo envío memoria (modo 2), se mostrará el mensaje almacenado en memoria y cuando el usuario pulse el botón, el mensaje será enviado. 

Caso de uso 4: Modo receptor

En el modo receptor (modo 3), el sistema esperará a recibir mensajes. Una vez recibe un mensaje, muestra el mensaje carácter morse a carácter morse y emite el sonido correspondiente a cada carácter morse, haciendo una pausa cuando haya un espacio.

Caso de uso 5: Cambio de modo

Para cambiar de modo, el usuario deberá pulsar el botón del encoder para entrar a la selección de modo. Tras ello, deberá rotar el encoder hasta alcanzar el modo deseado. Mientras el usuario rota el encoder, la pantalla mostrará el modo para informar al usuario del modo en el que se encuentra. Una vez el usuario haya seleccionado el modo deseado deberá volver a pulsar el botón del encoder para entrar al modo.

Componentes y precios

Componente Unidades Precio Unitario (€) 
Kit Arduino Uno Elegoo 39,09 
Encoder con pulsador 1,188 
Modulo RF Emisor y receptor 433MHz 6,3525 
Cable de cobre 5m 1,75 
Madera de contrachapado 600x300x5mm 3,96 
Cinta americana 1,5 
Cola para madera 1,5 
Pilas 1,5 
Bisagras 0,80 
Pack tuercas y tornillos 2,39 
Total: 110,56 

Cabe destacar que fueron necesarios 2 kits de Arduino por la falta de muchos componentes (botones) y cables en el kit prestado por el profesor. En una situación idónea, solo se hubiera necesitado un kit (y una placa para el segundo dispositivo).

Código

#include <Encoder.h>
#include <string.h>
#include <LiquidCrystal.h>
#include <RH_ASK.h>  // incluye libreria RadioHead.h
#include <SPI.h>     // incluye libreria SPI necesaria por RadioHead.h

// Pines
#define BUZZER A0
#define encoderBtn A1
#define encoderPinA 2
#define encoderPinB 3
// Pines 11 y 12 ocupados por Receptor y Emisor (respectivamente) 
#define BTN 13

#define LIMITE_PULSACION 500
#define TIEMPO_ESPACIO 2000

long posicionActual = 0;
int modoActual = 0;
char mensajeGuardado[64] = "...---..."; //S.O.S
bool yaEnvioMensaje = false;
unsigned long ultimoTiempoPulsado = 0;

LiquidCrystal lcd(9, 8, 7, 6, 5, 4);	// pines RS, E, D4, D5, D6, D7 de modulo 1602A

Encoder myEncoder(encoderPinA, encoderPinB);

RH_ASK rf_driver;

void setup() {
  rf_driver.init();
  lcd.begin(16, 2); // inicializa a display de 16 columnas y 2 lineas
  lcd.clear();
  pinMode(encoderBtn, INPUT_PULLUP);
  pinMode(BTN, INPUT);
  pinMode(BUZZER, OUTPUT);

  lcd.setCursor(0, 0);
  lcd.print("Modo 0");
}

void loop() {
  if (digitalRead(encoderBtn) == LOW) {
    leerModo();
    ultimoTiempoPulsado = 0;
    yaEnvioMensaje = false;
  }
  switch (modoActual) {
    case 0: modo0();break;
    case 1: modo1();break;
    case 2: modo2();break;
    default: modo3();break;
  }
}

void leerModo(){
  while(digitalRead(encoderBtn) != HIGH){}
  lcd.setCursor(0, 0);
  lcd.print("Leyendo Modo");
  vaciarFila(1);
  leerPosicion();
  vaciarFila(0);
  lcd.print("Modo ");
  lcd.print(modoActual);
}

void leerPosicion() {
  int modo = -1;
  while (digitalRead(encoderBtn) == HIGH) { // Mientras no esté pulsado
    long posicion = myEncoder.read();

    if (posicion != posicionActual) {
      int posNormalizada = ((posicion % 80) + 80) % 80;

      modoActual = posNormalizada / 20;

      posicionActual = posicion;
      if (modo != modoActual) {
        lcd.setCursor(0, 1);
        lcd.print(modoActual);
        modo = modoActual;
      }
    }
  }
  while(digitalRead(encoderBtn) != HIGH){}
  vaciarFila(1);
}

void modo0() {
  char mensaje[2] = "";
  if (digitalRead(BTN) == HIGH) {
    if (yaEnvioMensaje && millis() - ultimoTiempoPulsado >= TIEMPO_ESPACIO) {
      strcat(mensaje, " ");
    }
    tone(BUZZER, 1000);
    unsigned long init = millis();
    while(digitalRead(BTN) == HIGH) {}
    unsigned long end = millis();
    noTone(BUZZER);
    if (end - init <= LIMITE_PULSACION) {
      strcat(mensaje, ".");
    } else {
      strcat(mensaje, "-");
    }

    enviarMensaje(mensaje);
    ultimoTiempoPulsado = end;
    yaEnvioMensaje = true;
  }
}

void modo1() {
  imprimirMensaje(mensajeGuardado);
  if (digitalRead(BTN) == HIGH) {
    if (strlen(mensajeGuardado) != 0 && millis() - ultimoTiempoPulsado >= TIEMPO_ESPACIO) {
      strcat(mensajeGuardado, " ");
    }
    tone(BUZZER, 1000);
    unsigned long init = millis();
    while(digitalRead(BTN) == HIGH) {}
    unsigned long end = millis();
    noTone(BUZZER);
    if (end - init <= LIMITE_PULSACION) {
      strcat(mensajeGuardado, ".");
    } else if (end - init <= 5000) {
      strcat(mensajeGuardado, "-");
    } else {
      mensajeGuardado[0] = '\0';
      triplePitido();
      vaciarFila(1);
    }
    ultimoTiempoPulsado = end;
  }
}

void modo2() {
  imprimirMensaje(mensajeGuardado);
  if (digitalRead(BTN) == HIGH) {
    while(digitalRead(BTN) == HIGH) {}
    enviarMensaje(mensajeGuardado);
  }
}

void modo3() {
  uint8_t buf[65];
  uint8_t buflen = sizeof(buf) - 1;

  memset(buf, 0, sizeof(buf));   // Limpiar el buffer antes de recibir datos
  
  if (rf_driver.recv(buf, &buflen)) {  // Si hay datos correctos recibidos
    // Eliminar el último carácter si no es ASCII
    if (buflen > 0 && (buf[buflen - 1] < 32 || buf[buflen - 1] > 126)) {
      buf[buflen - 1] = '\0';
    }

    vaciarFila(1);
    int i = 0;
    for (i; buf[i] != '\0'; i++) {
      if (i >= 16) {
        lcd.scrollDisplayLeft();
      }
      char c = buf[i];
      lcd.print(c);

      int duration = 500;
      if (c == '.') {
        duration = 250;
      }
      
      if (c != ' ') {
        tone(BUZZER, 1000, duration);
      }
      delay(duration * 1.5);
    }

    if (i >= 16) {
      for (i; i > 16; i--) {
        lcd.scrollDisplayRight();
      }
    }
  }
}

void enviarMensaje(char * msg) {
  rf_driver.send((uint8_t *)msg, strlen(msg));
  rf_driver.waitPacketSent();
}

void imprimirMensaje(const char* mensaje) {
  lcd.setCursor(0, 1);
  int len = strlen(mensaje);
  if (len > 16) {
    lcd.print(mensaje + (len - 16));
  } else {
    lcd.print(mensaje);
  }
}

void vaciarFila(int fila) {
  lcd.setCursor(0, fila);
  lcd.print("                ");
  lcd.setCursor(0, fila);
}

void triplePitido() {
  tone(BUZZER, 1000, 100);
  delay(150);
  tone(BUZZER, 1000, 100);
  delay(150);
  tone(BUZZER, 1000, 100);
  delay(150);
}

Diseño del circuito

Se ha separado el diseño en varias capturas para favorecer la claridad de los diseños, pero en el dispositivo final están todos los circuitos que se muestran a continuación en la misma placa:

Circuito de emisor y receptor RF
Circuito del encoder con pulsador
Circuito del módulo LCD
Circuito del buzzer y el pulsador

Vídeo explicatorio

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 *