// Traductor Morse RS232 -> Lights & Sound

#include <18f4550.h>
#fuses HS,MCLR,NOWDT,NOPROTECT,NOPUT,NOBROWNOUT,NOPBADEN,NOLVP,NOCPD,NODEBUG,NOWRT,NOVREGEN // Fuses
#use delay(clock=20000000)                       // Clock a 20 Mhz
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7)   // Canal serie con el PC

const int  maxlendefinition=12;                  // Maxima longitud de la definicion Morse en las tablas
const long lapsus=1000;                          // Tiempo en milisegundos para Leds al reset
const long periodo=75;                           // Tiempo en milisegundos para unidad Morse = . (un punto)
const int  lenrecbuffer=64;                      // Tamaño en bytes del buffer de recepcion
const int  COMMAND_NULL=0;                       // COMANDO NULO -> No hay comando
const int  COMMAND_PROCESS=1;                    // COMANDO PROCESA TEXTO -> Convertir buffer a Morse

char Version[]="1.5.0\0";                        // Version del programa
char CharRcv=0;                                  // Caracter recibido por la USART
char Command=0;                                  // Caractar a enviar al main para su procesado
char recbuffer[lenrecbuffer];                    // Buffer de recepción
int  nextc=0;                                    // Indice de siguiente caracter en recbuffer

char Numeros[10][maxlendefinition]={             // Tabla de definicion de los Numeros

                    "\x010\x02-----\0  ",
                    "\x011\x02.----\0  ",
                    "\x012\x02..---\0  ",
                    "\x013\x02...--\0  ",
                    "\x014\x02....-\0  ",
                    "\x015\x02.....\0  ",
                    "\x016\x02-....\0  ",
                    "\x017\x02--...\0  ",
                    "\x018\x02---..\0  ",
                    "\x019\x02----.\0  "};


char Letras[27][maxlendefinition]={              // Tabla de definicion de las Letras

                    "\x01A\x02.-\0     ",
                    "\x01B\x02-...\0   ",
                    "\x01C\x02-.-.\0   ",
                    "\x01D\x02-..\0    ",
                    "\x01E\x02.\0      ",
                    "\x01F\x02..-.\0   ",
                    "\x01G\x02--.\0    ",
                    "\x01H\x02....\0   ",
                    "\x01I\x02..\0     ",
                    "\x01J\x02.---\0   ",
                    "\x01K\x02-.-\0    ",
                    "\x01L\x02.-..\0   ",
                    "\x01M\x02--\0     ",
                    "\x01N\x02-.\0     ",
                    "\x01O\x02---\0    ",
                    "\x01P\x02-..-\0   ",
                    "\x01Q\x02--.-\0   ",
                    "\x01R\x02-.-\0    ",
                    "\x01S\x02...\0    ",
                    "\x01T\x02-\0      ",
                    "\x01U\x02..-\0    ",
                    "\x01V\x02...-\0   ",
                    "\x01W\x02.--\0    ",
                    "\x01X\x02-..-\0   ",
                    "\x01Y\x02-.--\0   ",
                    "\x01Z\x02--..\0   ",
                    "\x01Ñ\x02--.--\0  "};

char Signos[7][maxlendefinition]={               // Tabla de definicion de las Letras

                    "\x01 \x02 \0      ",        // Espacio
                    "\x01,\x02.-.-.-\0 ",        // Coma
                    "\x01?\x02--..--\0 ",        // Interrogación
                    "\x01=\x02-...-\0  ",        // Igual
                    "\x01-\x02-....-\0 ",        // Guión
                    "\x01/\x02-..-.\0  ",        // Barra
                    "\x01@\x02.--.-.\0 "};       // Arroba


#int_rda                                         // Declaración de Interrupción ...
void rda_handler() {                             // Manejador de la interrupción recepcion USART

   CharRcv=0;                                    // Inicializo caracter a recibir
   Command=COMMAND_NULL;                         // Inicializo comando para main
   if(kbhit()){                                  // Si hay algo pendiente de recibir ...
      CharRcv=getc();                            // lo recibo sobre RecRcv.
      if(CharRcv!=0){                            // Si no es un \0 ...
         recbuffer[nextc]=CharRcv;               // lo copio en el buffer, ...
         if(CharRcv==0x0D){                      // Si he recibido un [Intro] 0x0D 13 ...
            recbuffer[nextc]='\0';               // Sustituyo el 0x0D en recbuffer por el terminador \0            
            Command=COMMAND_PROCESS;             // lo copio sobre command para procesarlo en main
         } else
         {
            putc(CharRcv);                       // lo envío de vuelta como eco y continuo ...
         }
         if(++nextc==lenrecbuffer){              // Y si despues de incrementar nextc es igual a lenrecbuffer ...
           nextc=0;                              // lo vuelvo a poner a cero
         }
      }
   }
}

void limpia_recbuffer(void){

   int i;                                        // Declaro variable de índice

   for(i=0;i!=lenrecbuffer;i++){                 // Bucle de 0 a lenrecbuffer 
      recbuffer[i]='\0';                         // Pongo a \0
   }
   nextc=0;
}

void wait_nperiodos(int n){                      // Rutina que espera n peridos

   int x;                                        // Declaro variable de índice

   for(x=0;x<n;++x){                             // Bucle de n periodos
      delay_ms(periodo);                         // Espero un periodo en milisegundos
   }
}

char morse_parse_char(char* mistring){           // Rutina que extrae el Caracter de la tabla de definiciones
                                                 // Entrada : Puntero a la definicion Devuelve: Caracter pulsado
   return mistring[1];                           // Devuelvo el segundo caracter de la tabla

}

char* morse_parse_code(char* mistring){          // Rutina que extrae el código Morse de la tabla
                                                 // Entrada: Puntero a la definicion, Devuelte: Puntero al codigo
   int i,j=0;                                    // Declaro variables de indices para copiar ...
   char c=' ';                                   // Caracter a procesar uno a uno
   char result[maxlendefinition];                // Buffer para el resultado

   for(i=3;i<maxlendefinition,c!='\0';i++,j++){  // Bucle que comienza en el caracter 3º y termina al \0 ó en Mñáxima longitud
      c=mistring[i];                             // Tomo los caracteres uno a uno ...
      result[j]=c;                               // y lo copio en el resultado
   }
   result[j]='\0';                               // Finalizo el string del resultado con el estandar \0
   return result;                                // Devuelvo el resultado

}

void morse_rs232(char* mistring){                // Rutina que monitoriza el código Morse extraído
                                                 // Entrada : Puntero a la definicion
   char  crtr;                                   // Variable para el Carácter pulsado
   char* code;                                   // Puntero a la Variable con el Código correspondiente

   crtr = morse_parse_char(mistring);            // Extraigo el caracter pulsado de la definicion
   code = morse_parse_code(mistring);            // Extraigo el codigo morse correspondiente a ese caracter

   printf("%c -> %s\r\n",crtr,code);             // Lo envio a la RS232
}

void morse_light_punto(void){

   output_High(PIN_E1);                          // Enciendo Led1 -> Puntos y Led0 -> Completo
   output_High(PIN_E0);
   wait_nperiodos(1);                            // Espero 1 x "periodo" de puntos
   output_Low(PIN_E1);
   output_Low(PIN_E0);                           // Apago  Led1 -> Puntos y Led0 -> Completo
   wait_nperiodos(1);                            // Espero 1 "periodo" de separacion entre puntos y rayas

}

void morse_light_raya(void){

   output_High(PIN_E2);                          // Enciendo Led2 -> Rayas y Led0 -> Completo
   output_High(PIN_E0);
   wait_nperiodos(3);                            // Espero 3 x "periodo" de rayas
   output_Low(PIN_E2);
   output_Low(PIN_E0);                           // Apago  Led2 -> Rayas y Led0 -> Completo
   wait_nperiodos(1);                            // Espero 1 "periodo" de separacion entre puntos y rayas
}

void morse_lights(char* mistring){               // Rutina que enciende los Led's de la RRBOARD2

   char* code;                                   // Puntero a la Variable con el Código correspondiente
   int   i;                                      // Declaro variable de indice para procesar ...
   char  c=' ';                                  // Caracter a procesar uno a uno e inicializo en blanco

   code = morse_parse_code(mistring);            // Extraigo el codigo morse correspondiente a ese caracter

   for(i=0;i<maxlendefinition-3,c!='\0';i++){    // Bucle que recorre el contenido de code hasta \0
      c=code[i];                                 // Tomo los caracteres a representar uno a uno ...
      switch(c){
         case ' ': wait_nperiodos(5);            // Espero 5 x "periodo" ( espacio entre palabras)
                   break;
         case '.': morse_light_punto();
                   break;
         case '-': morse_light_raya();
                   break;
      }
   }
}

void morse_process(char* mitext){

   int  i,x,y,z;                                 // Variables indice de ratreo de mitext, índice relativas
                                                 // a las tablas y al caracter recibido
   char xdefinition[maxlendefinition];           // Buffer para seleccionar la definicion correspondiente
   char  c=' ';                                  // Caracter a procesar uno a uno e inicializo en blanco
   
   for(i=0;i<lenrecbuffer,c!='\0';i++){
      // Cargo siguiente caracter a procesar 
      c=mitext[i];
      if(c!='\0'){
         // Inicializo índices de tablas a un valor imposible
         x=0xff;
         y=0xff;
         z=0xff;
         // Letras Minúsculas, las convierto a mayúsculas
         if( (c>'a'- 1) && (c<'z'+ 1)){
            c = c - 'a' + 'A';
         }
         // Letras Mayúsculas, calculo el índice en la tabla según su código ASCII
         if( (c>'A'- 1) && (c<'Z'+ 1)){
            x = c-'A';
         }
         // Letras Especiales, puestas a güebo
         if( (c=='Ñ') || (c=='ñ')){              // Ñ
            x =26;
         }
         // Números, calculo el índice en la tabla según su código ASCII
         if( (c>'0'- 1) && (c<'9'+ 1)){
            y = c-'0';
         }
         //Signos y puntuación, puestos a güebo
         if(c==' ') z = 0;                             // Espacio
         if(c==',') z = 1;                             // Coma
         if(c=='?') z = 2;                             // Interrogación
         if(c=='=') z = 3;                             // Igual
         if(c=='-') z = 4;                             // Guión
         if(c=='/') z = 5;                             // Barra
         if(c=='@') z = 6;                             // Arroba
         // Guardo en el buffer de main la definicion correspondiente a lo pulsado
         if(x < 255){sprintf(xdefinition,"%s",Letras[x]);}
         if(y < 255){sprintf(xdefinition,"%s",Numeros[y]);}
         if(z < 255){sprintf(xdefinition,"%s",Signos[z]);}
         // Presenta y/o suena (Ahora solo presenta)
         morse_rs232(xdefinition);
         morse_lights(xdefinition);
      }
   }
   
}

void on_reset(){

   disable_interrupts(global);                   // Deshabilito todas las interrupciones
   disable_interrupts(int_timer1);               // Deshabilito ...
   disable_interrupts(int_rda);
   disable_interrupts(int_ext);
   disable_interrupts(int_ext1);
   disable_interrupts(int_ext2);

   setup_adc_ports(NO_ANALOGS);                  // Configuro todo lo que voy
   setup_adc(ADC_OFF);                           // a usar y lo que no ...
   setup_spi(FALSE);
   setup_psp(PSP_DISABLED);
   setup_counters(RTCC_INTERNAL,RTCC_DIV_2);
   setup_timer_0(RTCC_OFF);
   setup_timer_1(T1_INTERNAL | T1_DIV_BY_1);
   setup_timer_2(T2_DISABLED,0,1);
   setup_timer_3(T3_DISABLED);
   setup_comparator(NC_NC_NC_NC);
   setup_vref(FALSE);
   port_b_pullups(FALSE);
   set_tris_e(0b00010000);
   set_tris_c(0b10000000);
   enable_interrupts(global);                    // Habilito todas las interrupciones (que esten habilitadas)
   enable_interrupts(int_rda);                   // Habilito la interrupcion por recepcion USART

   delay_ms(333);                                // Espero 333 milisegundos a que todo se estabilice

   printf("\r\n");                               // Me presento como deben hacer todos los PIC's de
   printf("Morse with 18F4550 in RRBOARAD2");    // buena familia
   printf("\r\n");
   printf("    v.%s by Redpic\r\n\n\n",Version);

   output_High(PIN_E0);                          // Enciendo los tres Led's de la RRBOARD2
   output_High(PIN_E1);
   output_High(PIN_E2);
   wait_nperiodos(lapsus/periodo);               // Espero lo que indica "lapsus" expresado en unidades de "periodo"
   output_Low(PIN_E0);
   output_Low(PIN_E1);
   output_Low(PIN_E2);                           // Apago los tres Led's de la RRBOARD2
   wait_nperiodos(lapsus/periodo);
   limpia_recbuffer();                           // Limpio el buffer de recepción
}

void main(){

   On_reset();

   do{                                           // Bucle Infinito

      if(Command!=COMMAND_NULL){
      
         if(Command==COMMAND_PROCESS){           // COMMAND_PROCESS : Convierte a Morse el contenido de recbuffer
            printf("\r\n\n");                      // Paso una linea en el monitor rs232
            morse_process(recbuffer);            // Mando procesar el contenido de recbuffer
            printf("\r\n");                      // Paso otra linea en el monitor rs232
            limpia_recbuffer();                  // Limpio el buffer de recepción
            Command=COMMAND_NULL;                // Limpio el ultimo comando procesado
         }
      }

   }while(TRUE);
}

