# Problemas al decodificar HT12E con microcontrolador.



## Arsenic (May 2, 2017)

Me encuentro trabajando en un proyecto de un decodificador con microcontrolador de un HT12E. Según las hojas de datos del mismo, éstos trabajan en un margen de frecuencias de entre 1,75 a 2 KHz. Aunque eso depende del resistor que se le conecte entre los pines del oscilador interno. Es posible realizar la decodificación, ya que éste envía un pulso y un bit de sincronía para establecer la comunicación. Ahora bien, el tiempo del bit de sincronía puede variar por el nivel de batería, el resistor que lleva entre sus patas 15 y 16, entre otros factores. Es posible realizar dicha decodificación, haciendo uso del timer0 como temporizador, cuyo desborde evitaría que lleguen pulsos de más de 2 ciclos (ya que los descarta y los toma como inválidos). También se utiliza el timer1 como contador, para determinar la duración del pulso alto en piloto y así comparar los siguientes que llegan. Se puede utilizar la interrupción por cambio de estado RB4-RB7 para detectar los mismos (El pin RB4 iría al RX digital del módulo receptor). He logrado hacer funcionar uno, pero no "discrimina". Toma hasta el "ruido" que se le introduce a la radio. Es decir, falta pulirlo. Pero una vez pulido funcionará perfectamente. Si alguien puede aportar una solución a mi problema, se lo agradeceré. Les dejo todo en un *.rar, así pueden descargar la simulación en proteus, además del código fuente en CCS. ...

Olvidé el adjunto. Aquí está.


----------



## juanma2468 (May 3, 2017)

Podrías explicar un poco lo que hace tu código como para poder ayudarte de forma más fácil? Sobre todo la forma en la que detectas el código y como lo decodificas.


----------



## Arsenic (May 3, 2017)

juanma2468 dijo:


> Podrías explicar un poco lo que hace tu código como para poder ayudarte de forma más fácil? Sobre todo la forma en la que detectas el código y como lo decodificas.



Encantado. Según la hoja de datos del HT12E, la traza se envía mediante pulsos cuya duración depende del nivel de tensión en la batería y el resistor conectado a los pines 15-16, el cual define dicha frecuencia de oscilación. Es por tanto incalculable dicha frecuencia, ya que depende de variaciones que están fuera de nuestro alcance. Volviendo a los códigos que transmite, la traza sería la siguiente:
PILOTO (12Bits L + 1/3bit H) | DIRECCIÓN (12Bits) | DATOS (8Bits) |
El código piloto contiene además un bit de sincronía (1), el cual sirve para medir la duración del pulso. Y eso es lo que el código hace al principio: Medir la duración del pulso. Esto es:

Pilot code:
LLL LLL LLL LLL LLL LLL LLL LLL LLL LLL LLL LLL  |H
Periódo de 12 bits de duración de pulsos Low (36 Alpha) + 1 Alpha de pulso High (período de sincronía).

El código piloto tiene una duración de 12bits (todos pulsos bajos) y un tercio de bit en pulso alto. Ese tercio de bit mide exactamente 1 Alpha (Donde Alpha = 1 ciclo reloj).

Los bits codificados por el HT12E se transmiten de la siguiente forma:

LHH = bit 0
LLH = bit 1
Cada bit dura exactamente 3 alpha.

es preciso discriminar lo que se recibe para descartar paquetes de otras fuentes (interferencia), por lo que sabiendo lo anterior, esto se hace mediante medición de la duración de pulsos altos y bajos, con cierta tolerancia añadidas por los ciclos máquina que ocupan todas las instrucciones programadas en el microcontrolador.

Este proceso de decodificación hace simplemente eso: 
1) Mide el pulso en alto del código piloto, el cual será el valor de un ciclo máquina, denominado Alpha.
2) Recibe el pulso o los pulsos negativo(s), luego el pulso o los pulsos positivo(s), los almacena y verifica si su duración corresponde o no a un bit enviado por el transmisor o si se trata de una interferencia.
3) Parsea los datos y los ordena para luego presentarlos en el LCD (en formato decimal).

Eso sería más o menos todo. El desborde del timer0 se programó para que discrimine según duración máxima de 2ms de pulso bajo o alto. El timer1 para llevar el tiempo de duración de los pulsos recibidos, mientras que la interrupción por cambio de estado de RB4-RB7 sirve para detectar los flancos de subida/bajada de la salida digital del RX. Espero haber respondido tu pregunta.


----------



## juanma2468 (May 3, 2017)

Y tu problema es que el ruido te lo toma a veces como un código válido? Este programa sólo sirve para el HT12 o también sirve con otros encoders? Has probado algún otro?


----------



## Arsenic (May 3, 2017)

No he probado ningún otro. Sé que tengo un decoder para OTP que sí funciona. Pero no funciona con el HT12E. Al parecer el formato es el mismo pero el rango de frecuencias de pulsos varía notablemente. Además el de OTP me almacena 22bits, mientras que el HT12E sólo 12 bits en total. Estos paquetes enviados no tienen reconocimiento de final de código (endcode) como el OTP. Eso también me ayudaba a descartar ruido, ya que los paquetes que no terminaban en 0101 (endcode) directamente los filtraba a todos. Sí, efectivamente: Mi problema es que me toma cualquier ruido y no tengo forma de discriminar mejor esos paquetes.


----------



## juanma2468 (May 4, 2017)

Cuantas tramas de código  analiza tu programa para decir si es o no un código válido?  Por ahí añadiendo un bucle que repita la secuencia y que por lo menos el código se repita 2 o 3 o 4 veces harías que descarte un poco códigos erroneos por ruido. Por otro lado, otro dato que puedes usar para detectar un código válido es el espacio entre una trama y la otra, lo lees la primera vez, y luego en el bucle de repetición buscas ese mismo valor, tomando un margen de seguridad, si lo vuelves a encontrar puede que sea un código válido y lo vuelves a analizar sino lo descartas.


----------



## Arsenic (May 4, 2017)

Bueno... sólo una trama de 12 bits. Es todo.


----------



## juanma2468 (May 4, 2017)

El ruido es muy propenso a que en algún momento determinado haga que tu decodificador interprete que se recibió un código válido. Por ejemplo el MC145026 y MC145027 pares encoder decoder, como protocolo tienen que pare ser válido un código tiene que llegar 2 veces el mismo código, de lo contrario lo descarta, de esa forma evita el ruido, ya que es casi imposible, o muy muy poco probable que 2 veces seguidas se de la misma secuencia en un lapso de tiempo corto.



----------------- Actualización --------------------

Mirá aca te dejo un código hecho en CCS por mi que permite reconocer los encoders:  PT2262, PT2264, PT2242, PT2240, SC2260, SC2262, HX2260, HX2262, HS2262,  HS2260, AX5026, SMC918, SMC926, AX5326, RT1527, FP1527, FP527, HS1527,  HX1527, HT600, HT680, HT6201, HT6010, HT6012, HT6014, HT6013, HT12A, HT12E. Te comento que funciona, actualmente lo tengo implementado en centrales para portones automáticos y sin problemas. Fijate que realizo un bucle en el que verifico que el código llegue bien 2 veces, de lo contrario lo descarto. Luego si es un código correcto lo almaceno en la EEPROM para luego poder comparar cuando llegue el mismo código. Esto me permite copiar el código de un control cualquiera a la central, así lo aprende, y luego cuando recibe un código se fija si lo tiene almacenado en su memoria, si lo tiene activa el portón, sino, no hace nada y lo ignora. Cualquier duda que tengas consultame.

```
#FUSES NOWDT                    //No Watch Dog Timer
#FUSES INTRC_IO                 //Internal RC Osc, no CLKOUT 
#FUSES NOPROTECT                //Code not protected from reading 
#FUSES BROWNOUT                 //Brownout reset 
#FUSES NOMCLR                   //Master Clear pin used for I/O 
#FUSES NOCPD                    //No EE protection 
#FUSES PUT                      //Power Up Timer
#FUSES RESERVED                 //Used to set the reserved FUSE bits  
#use delay(clock=4000000)  

// Definiciones del puerto A  

#define Datos PIN_A0 
#use fast_io(A)  

int i,Trama_encontrada,Contador_bits,Codigo[3],Auxiliar[3]; 
int Contador_codigos,Bien,Mal,Nuevo_codigo,Aux,Salto; 
long Tiempo_entre_tramas,Aux_tiempo_entre_tramas; 
long Tiempo_ET_delta_neg,Tiempo_ET_delta_pos,Tiempo_bits,Tiempo_bit,Bit,Tiempo;  

void main() 
{    
   set_tris_a (0b11111111); // Entradas:A0,A1,A2,A3,A4,A5    
   Salto = read_eeprom (252);    
   if (Salto==255)       
      Salto = 0;    
   setup_timer_1 (T1_INTERNAL|T1_DIV_BY_1); // Base de tiempo de 1 useg    
   for (;;)    
   {    
      Tiempo_entre_tramas = 0;    
      Trama_encontrada = false;    
      Contador_bits = 0;    
      Nuevo_codigo = 0;    
      Contador_codigos = 0;    
      Mal = 0;    
      Bien = 0;    
      for (i=0;i<25;i++)    
      {       
         while (input(Datos) == 1);       
         set_timer1(0);         
         while (input(Datos) == 0);       
         Aux_tiempo_entre_tramas = get_timer1();       
         if (Tiempo_entre_tramas < Aux_tiempo_entre_tramas)          
            Tiempo_entre_tramas = Aux_tiempo_entre_tramas;    
      }    
      Tiempo_ET_delta_neg = Tiempo_entre_tramas-Tiempo_entre_tramas/64;    
      Tiempo_ET_delta_pos = Tiempo_entre_tramas+Tiempo_entre_tramas/64;    
      for (i=0;i<25;i++)    
      {       
          while (input(Datos) == 1);       
          set_timer1(0);         
          while (input(Datos) == 0);       
          if ((Tiempo_ET_delta_neg < get_timer1())&(get_timer1() < Tiempo_ET_delta_pos))       
          {          
              i = 25;          
             Trama_encontrada = true;       
          }    
       }    
       if (Trama_encontrada == true)    
       {       
           while (input(Datos) == 1);       
           set_timer1(0);         
           while (input(Datos) == 0);       
           while ((Tiempo_ET_delta_neg > get_timer1())|(get_timer1() > Tiempo_ET_delta_pos)&(Contador_bits<25))       
           {           
               Contador_bits++;         
               while (input(Datos) == 1);         
               set_timer1(0);           
               while (input(Datos) == 0);       
            }       
            if (Contador_bits < 25)       
            {          
                while (input(Datos) == 0);          
                set_timer1(0);            
                while (input(Datos) == 1);          
                while (input(Datos) == 0);          
                Tiempo_bits = get_timer1();          
                while (input(Datos) == 0);          
                set_timer1(0);            
                while (input(Datos) == 1);          
                while (input(Datos) == 0);;          
                Tiempo = get_timer1();          
                if (((Tiempo_bits - Tiempo_bits/64) > Tiempo)|(Tiempo > (Tiempo_bits + Tiempo_bits/64)))          
                {             
                    if (Tiempo < Tiempo_bits)                
                       Tiempo_bit = Tiempo_bits/2;             
                    else                
                       Tiempo_bit = Tiempo/2;          
                }          
                else             
                       Tiempo_bit = Tiempo_bits/2;          
                do          
                {             
                   do             
                   {                
                        while (input(Datos) == 1);                
                        set_timer1(0);                  
                        while (input(Datos) == 0);                
                        Tiempo = get_timer1();             
                    }while ((Tiempo_ET_delta_neg > Tiempo)|(Tiempo > Tiempo_ET_delta_pos));             
                    for (i=0;i<Contador_bits;i++)             
                    {                
                       while (input(Datos) == 1);                
                       set_timer1(0);                  
                       while (input(Datos) == 0);                
                       Bit = get_timer1();                
                       if (Bit < Tiempo_bit)                   
                          shift_left(Codigo,3,1);                
                       else                   
                          shift_left(Codigo,3,0);             
                    }             
                    if ((Codigo[0]==0)&(Codigo[1]==0)&(Codigo[2]==0))                
                        Mal = 1;             
                    if ((Nuevo_codigo == 0)&(Mal == 0))             
                    {                
                       for (i=0;i<3;i++)                   
                          Auxiliar[i]=Codigo[i];             
                    }             
                    if ((Mal == 0)&(Nuevo_Codigo == 1))             
                    {                
                        if (Auxiliar[0] == Codigo[0])                
                        {                   
                            if (Auxiliar[1] == Codigo[1])                   
                            {
                                if (Auxiliar[2] == Codigo[2])                         
                                   Bien = 1;
                                else                      
                                {
                                   Mal = 1;
                                   Bien = 0;
                                 }
                             }
                             else
                             {
                                 Mal = 1;
                                 Bien = 0;
                             }
                        }                
                        else
                        {
                           Mal = 1;
                           Bien = 0;
                        }
                    }             
                    Nuevo_codigo = 1;
                    Contador_codigos++;             
                    for (i=0;i<3;i++)                
                       Codigo[i] = 0;          
                }while((Mal == 0)&(Contador_Codigos < 4));
                if (Bien == 1)          
                {             
                   for (i=0;i<3;i++)
                   {                
                      Aux = Auxiliar[i];                
                      write_eeprom(i+Salto,Aux);             
                   }             
                   Salto+=3;             
                   write_eeprom(252,Salto);          
                }       
            }    
        }    
    } 
}
```


----------



## Arsenic (May 4, 2017)

Wow! Bueno, yo estuve trabajando en algo así... pero sólo funciona para el HT6P20 y utilizo el mismo formato del typedef y el struct, combinado con el switch porque me es más simple. Además el HT6P20 tiene endcode y me es muy simple discriminar la interferencia. Pero sí, la idea es esa básicamente. Combinar 2 arrays y que a ambos les llegue lo mismo. Luego los comparo y es como si hubiera un código de 24 bits. Ahora lo hago.


----------



## Arsenic (May 5, 2017)

Acabo de leer que el PT2261 y el HT12E envían 4 words (word = 12 bits = Address + data) por cada vez que el pin NOT(TE) va a GND. Es decir que no es necesario ponerlo a chequear repetidas veces. Se puede con un frame de datos hacer el filtro. Sólo basta tomar el frame completo (pilot + 4 words = 48bits-pilot) y compararlos cada 12 bits. De esa forma tendrías listo el filtro y eso te serviría para todos los integrados que has mencionado. Adjunto las hojas de datos de los que mencioné. 

Nota: Iré modificando el programa para que pueda decodificar los demás integrados que me mencionabas antes.


----------



## juanma2468 (May 5, 2017)

Esta bien lo que dices, pasa que hay muchas formas de encarar el mismo problema, en tu caso tu entras toda la trama (4 words) y a partir de ahi analizas si corresponde o no a un código, pero para ello debes perder todo el tiempo de entrar toda la trama. En mi caso lo trabajo distinto, directamente analizo bit a bit a medida que va entrando y si por algún motivo un bit tiene una duración distinta a la que había comenzado a entrar, entonces directamente no sigo analizando el código. Luego si el primer código fue correcto repito la secuencia N veces, donde N es un número que yo puedo ajustar a mi gusto. Pero de todas formas esta interesante tu código, quizás es un poco más estructurado que el mio y seguramente lleva menos código de programación, con lo cual se acorta el tiempo de respuesta del micro.


----------



## Arsenic (May 6, 2017)

Una pregunta: ¿Has probado tu código con los otros integrados? Porque parece ser que el formato de los frames del PT2261 difiere del que envía el HT12E. Yo ya pude terminar con el deco para el HT12E. Te adjunto nuevo código y simulación. Además fue probado en protoboard, y está funcionando 100%.


----------



## juanma2468 (May 6, 2017)

Si, mi codigo contempla todo los caso, identifica el bit de sincronización y a partir de ello identifica a que tipo de formato corresponde y lo decodifica. Esta probado con el formato del ht12e y con el ev1527.



--------------------- Actualizado -------------------------

Estuve viendo tu código, y por lo que veo vos designas que como mucho tu Alpha puede valer 2ms. Lo cual significa que si ingresa un ancho mayor tu timer0 desborda y descarta lo que estas midiendo. Ahora bien que pasa si tu Alpha vale más de 2ms? o sea lo pienso para otros integrados en el que el Alpha pueda llegar a valer más que eso. Con la verificación de las 2 palabras word0 y word1 pudiste solucionar el problema de la falsa activación por ruido? Otra variable con la que podes jugar para minimizar falsas activaciones es con la "Tolerancia" achicandola.


----------



## Arsenic (May 6, 2017)

Quiero conseguirme todos los llaveros que pueda. Así logro hacer uno completamente universal. Desconozco todos los tipos de decodificación que hay. Hasta ahora he decodificado el HT12E y el OTP. No sé qué formato es el que acabo de decodificar. Creo que es AFK o Manchester. 

Con la verificación en 2 pasos he podido efectivamente quitar el error de activación por falsos códigos. He dejado 2 arrays más, con el objeto de fortalecerlo en áreas donde la interferencia sea mayor (zonas urbanizadas, industrias, ...). La hoja de datos del HT12E nos brinda una tabla. Dicha tabla contiene información sobre la fosc y cómo ésta varía según el resistor ROSC y la tensión aplicada. En mi caso tengo un control que tiene un resistor de 1,8Mohms y es alimentado a 12V (rango de frecuencias de 1,25-2KHz), siendo su inversa máxima de 0,0005 segundos. Considerando el cálculo del timer0 y además que el pulso a veces es 2 veces ese valor, sumado a eso los tiempos de ejecución, creo que estamos ante un desborde aceptable de 1,5ms. Puse 2ms para asegurarme. Además ni siquiera es necesario programar una interrupción por desborde de timer0, pero sí que está bueno para filtrar el período bajo del código piloto y más efectivo para "medir" el ancho de pulso mediante el cual luego filtro gran parte de la interferencia, al no corresponderse con un bit.

De acuerdo con la simulación, el número de tolerancia aún puede acortarse aproximadamente unas 4 veces.


----------



## juanma2468 (May 6, 2017)

Pues fijate por ejemplo que el EV1527 es diferente el comienzo al HT12E. En el HT12E tenes un piloto de 12 bits y luego un bit de sincronización. En el EV1527 es exactamente alreves, primero un bit de sincronización y luego un piloto. Puede mirar por acá los dos tipos de codificación más comunes, que son los que utilizan los integrados que yo te puse anteriormente.

PD: Fijate un post anterior que justo vos escribias al mismo tiempo que yo y creo que no alcanzaste a ver lo que actualice. Se solucionó el problema del ruido?


----------



## Arsenic (May 6, 2017)

Yo también actualice. Efectivamente, se solucionó.


----------



## juanma2468 (May 8, 2017)

Me he tomado el atrevimiento de modificar un poco tu código para optimizar la memoria tanto la RAM como la ROM ya que me parecia demasiado excesivo usar 12 byte por cada palabra para solo colocar un bit en cada byte y posteriormente pasar esos 12 bytes a un registro de Direccion y otro de Datos. Asi que lo que modifique basicamente fue que directamente se obtenga la Dirección y los Datos sin tener que pasar por los Words. Otra cosa que me pareció innecesario fue el timer0, directamente se realiza una comparación ni bien se obtiene el valor de Alpha y si resulta menor que 250 o sea 2mseg continúa analizando, de lo contrario, da error. Otra cosa que modifique fue la del control de los bits, directamente analiza cada estado alto o bajo y verifica si cumple con las condiciones necesarias a partir del valor del Alpha, de lo contrario da error y no sigue. No lo simule ya que no tengo Proteus 8 o superior y tampoco lo probe en forma física, por lo que puede no estar funcional, pero lo vas a saber modificar para que funcione. Luego me comentas si funcionó y si te sirvió las mejoras. =)


----------



## Arsenic (May 9, 2017)

Me haré a la tarea. De todas formas, esto es sólo un paso más hacia mi objetivo real. Muchísimas gracias por tu interés! De verdad.


----------



## juanma2468 (May 9, 2017)

Voy a intentar realizar una fusión entre mi código y el tuyo a ver que sale del apareamiento. La combinación de lo mejor de cada uno tendrá la cualidad de alto tiempo de respuesta y gran versatilidad de decodificación, o al menos a eso apunto. En la medida que avance lo postearé, no se si tu objetivo esta siendo el mismo, pero creo que vamos hacia el mismo lado.


----------



## Arsenic (May 10, 2017)

Mi objetivo es hacer un decodificador universal, que permita usar cualquier tipo de control remoto, siempre que sea en 433MHz. Estaría excelente lograrlo. Estuve probando y parece que el código que hice yo también puede leer OTP. El tema es que no lo estaría decodificando, ni guardando porque tiene variantes.


----------



## juanma2468 (May 10, 2017)

Yo ya comencé a fusionar los códigos, en cuanto termine lo subo para que lo veas y puedes emplearlo si quieres.


----------



## juanma2468 (May 12, 2017)

Bueno, luego de pedalear un buen rato, acá esta listo el código "injerto" producto de la fusión de ambos códigos. En el archivo RAR incluyo el proyecto en CCS, la simulacion en Proteus 7.9, el esquema usado, un proyecto que simula un control remoto con 2 botones, el boton 1 posee un código de 12 bits y el botón 2 un código de 24 bits, para poder probar la simulación. De igual forma está probado en forma física con varios controles y con todos funcionó. El proyecto está realizado con un PIC 16F684, y primero hay que grabar el código del control remoto en la memoria eeprom, para ello hay que presionar el pulsador y soltar, el led verde se apagará y en ese momento hay que presionar el boton del control que queremos grabar, si lo tomo de forma correcta, el led verde parpadeará 10 veces. Para grabar otro código hay que volver a repetir los pasos anterirores, en total se puede grabar un total de 80 códigos en memoria. En el caso de querer borrar la memoria (borra toda la memoria), se debe presionar el pulsador y dejar apretado hasta que el led se vuelva a encender, en ese momento soltar el pulsador, al terminar de borrarse la memoria el led parpadeará 5 veces. Una vez grabado al menos 1 código en memoria, al recibise de forma correcta dicho código se activará el led rojo, si se vuelve a recibir nuevamente el mismo código se apagará, repitiendose así el ciclo apagado-prendido. Espero les resulte útil.


----------



## Arsenic (May 12, 2017)

Excelente trabajo. Admirable lo tuyo! Ya mismo voy a intentarlo, porque al parecer con mi código no se podía decodificar OTP y FSK juntos.


----------



## juanma2468 (May 12, 2017)

Fijate y me avisas que tal te fue con el código. Lo que me ha pasado a veces luego de dejarlo un rato prendido es que deja de tomarme los controles como si por algún motivo las interrupciones por cambio de estado se detuvieran, lo desenchufo y vuelvo a conectar la alimentación y sale andando. Eso habría que chusmearlo con mas detalle, te lo encargo a ti si te parece, asi vamos a medias con el codigo [emoji6]. Si ves conveniente modificarlo haslo y me comentas. [emoji1]


----------



## Arsenic (May 12, 2017)

Encantadísimo! No obstante, primero debería darme a la tarea de conseguir los otros controles, ya que no tengo más que 2 de ellos, uno basado en el HT12E y el otro en el HT6P20.


----------



## juanma2468 (May 12, 2017)

Primero probalo con esos y luego si todo salió bien pasas a probar con los otros


----------



## Arsenic (May 12, 2017)

Bien. Aunque Proteus emula perfectamente ambos formatos. Fue de hecho gracias a Proteus que pude lograr hacerlo funcionar ya que no consto de oscilador, ni analizador lógico... lo único que tengo para medir es un multímetro por ahora.


----------



## juanma2468 (May 12, 2017)

Si, yo tambien lo hice andar con ayuda de Proteus, aun que debo decirte que al comienzo en Proteus me andaba y en forma física no, y por mas osciloscopio que tuviera no me ayudaba ya que no sabía por que parte del programa andaba, asi que lo que hice fue utilizar un pin como salida para indicar el estado y saber en que parte del programa se encontraba, sacando pulsito, un pulso, 2 pulsos, etc y ahi si con la ayuda del osciloscopio, pude detectar en que parte fallaba y solucionarlo.


----------



## Arsenic (May 12, 2017)

JA! Es curioso lo que señalas. A mí me pasó lo contrario: Funcionaba en la vida real, dejaba de funcionar en Proteus. De todas formas lo usé más como debugger que como simulador en sí.


----------



## juanma2468 (May 23, 2017)

Y has logrado algún avance con el programa o el proyecto Arsenic?


----------



## FaoguerraLezama (Jun 3, 2017)

Hola que tal soy nuevo en el foro y en la electrónica, trato de realizar un modulo de radio frecuencia pero este no funciona.

-Tengo un modulo de envio con un chip ht12e se alimenta de una batería 9v con un regulador de voltaje a 5v (l7805) regulo el voltaje a 5v, alimento el chip en el pin 18 con 5v el pin 8 lo conecto a tierra con el pin 9 y con eso fijo la dirección de conexión entre los dos encapsulados.

-El modulo de recepción lo conecto con un cable usb que le da corriente 5v directamente al encapsulado ht12d el pin 8 lo conecto con el pin 9 para fijar la dirección igual a la del modulo de envío, solo trato de hacer funcionar los dos encapsulados no he puesto el rx y tx, solo trato de revisar que los dos chip funcionen entre si pero no lo logro.

-Conecte el pin 17 del ht12d con un led en la pata positiva la pata negativa del led va a una resistencia de 220 ohm y la otra pata de la resistencia esta conectada a tierra, el led debería de prender al conectar el pin 17 del ht12e lo conecto con el 14 del pin ht12d debería de prender el led ¿CORRECTO?

Que opinan que puede estar mal? algún consejo por favor


----------



## Arsenic (Ago 29, 2017)

juanma2468 dijo:


> Y has logrado algún avance con el programa o el proyecto Arsenic?



En estos días me pongo, te lo prometo. La cantidad de trabajo que tengo es casi inhumano. No paro ni sábados, ni domingos, ni feriados. Perdón por la demora, pero sí que en estos días tal vez pueda dedicarle un poco a este proyecto en particular. Creo que con esto ya podremos decodificar cualquier tipo de traza One Time Pad comercial de cualquier IC.





FaoguerraLezama dijo:


> Hola que tal soy nuevo en el foro y en la electrónica, trato de realizar un modulo de radio frecuencia pero este no funciona.
> 
> -Tengo un modulo de envio con un chip ht12e se alimenta de una batería 9v con un regulador de voltaje a 5v (l7805) regulo el voltaje a 5v, alimento el chip en el pin 18 con 5v el pin 8 lo conecto a tierra con el pin 9 y con eso fijo la dirección de conexión entre los dos encapsulados.
> 
> ...



El pin 14 del encoder HT12E (NOT-TE) va conectado al negativo (GND). En el datasheet hay un circuito que está mal, presta atención a eso. El pin 14 del decoder (HT12A) es el Latching Mode. Ese pin debe quedar libre (no conectado a GND), ya que además de enviar el frame completo, añade 7 palabras más con un 1 de dato (algo completamente innecesario a efectos prácticos y contraproducente en cuanto a la durabilidad de la batería). 

El transmisor no necesita de ningún regulador, ya que su rango de tensiones varía entre los 2.4V y 12V. Podrías prescindir de él. Por su parte, el receptor (HT12A) sí requiere de una tensión máxima de 5V, por lo que probablemente tendrías que utilizar algún 7805, si es que la tensión de entrada es mayor a esa.

Pero primero que nada, yo probaría unir ambos negativos de la alimentación (GND), ya que al no haber un RX/TX, éstos no tienen una referencia común. Recordá que ningún integrado necesita pullup físico, porque internamente viene incorporado.

Revisa que las direcciones sean idénticas. Chequea la resistencia del oscilador y/o el cristal que le hayas colocado al otro IC. Creo que además de unir los pines 17 entre sí (DOUT), necesitan de una señal de reloj en sincronía.

Saludos y éxitos!


----------



## frankko (Dic 4, 2017)

Saludos a todos en el foro, soy aficionado y algunas veces construyo algún dispositivo electrónico, ahora vi este hermoso trabajo para controles remotos, hecho por juamma2468 y quería implementarlo en un pic16f886, pero me di cuenta de que si recompilas el proyecto para el 16f684 o 16f688 obtengo un archivo hexadecimal que pone en el micro no funciona como el proporcionado en el zip. Me gustaría ejecutarlo en un 16f886, entonces le pregunto a juamma2468 si puede verificarlo amablemente y adjuntar el archivo nuevamente. Muchas gracias de antemano.
Frankko


----------



## juanma2468 (Dic 4, 2017)

Hola frankko, te adjunto la ultima versión de código que tengo totalmente funcional. Lo que te paso es el código para registrar un código en la memoria eeprom del pic 16F684 para luego poder reconocerlo y asi poder activar/desactivar la salida. Como hice un recorte y pego, puede que tengas algunas variables de más dando vuelta, cualquier problema me avisas pegandome el log de los errores que te de al compilar. También te adjunto el esquemático para que sepas como van conectado las cosas. Para grabar el código lo que tenes que hacer es apretar el pulsador hasta que se apaga el led, cuando lo hace soltas el pulsador y precionas el botón del control a registrar (esto miestras el led está apagado), si lo toma y registra el led parpadeará unas 10 veces rápidamente, luego vuelve a prenderse. Después de eso ya podrás usar el receptor. Cualquier duda me avisa, saludos.
FE de erratas.
En la parte de código donde dice
If (Activacion==true)
{
If (input(salida)==0)
Salida=1;
Else
Salida=0;
}
En realidad tendría que ser
If (Activacion==true)
Salida=1;
Else
Salida=0;


----------



## frankko (Dic 5, 2017)

Hola Juanma, antes que nada gracias por tu respuesta, fuiste muy amable. Tan pronto como lo intente, le haré saber.
Saludos a todos
Frankko


----------



## frankko (Dic 22, 2017)

hola Juanma,
Probé el código que amablemente me adjuntaste y solo funciona con el simulador Proteus, almacena el código, elimina etc., pero una vez que se activa la salida, no se desactivará al soltar el botón. En el prototipo físico que monté portando el puerto 16f886 del que adjunto el archivo, consigo 'encender solo dos flashes del LED, y no voy más allá. Tal vez tienes que cambiar la tolerancia? ¿Puedes ayudarme a resolverlo? De lo contrario, no puedo continuar. Gracias, saludos a todos y Feliz Navidad.


----------



## juanma2468 (Dic 23, 2017)

frankko dijo:
			
		

> hola Juanma,
> Probé el código que amablemente me adjuntaste y solo funciona con el  simulador Proteus, almacena el código, elimina etc., pero una vez que se  activa la salida, no se desactivará al soltar el botón. En el prototipo  físico que monté portando el puerto 16f886 del que adjunto el archivo,  consigo 'encender solo dos flashes del LED, y no voy más allá. Tal vez  tienes que cambiar la tolerancia? ¿Puedes ayudarme a resolverlo? De lo  contrario, no puedo continuar. Gracias, saludos a todos y Feliz Navidad.


Podrías subir un video para ver bien que es lo que te hace, ya que de esa forma me voy a dar cuenta más facil de por donde puede venir el problema, saludos.


----------



## lycans2005 (Sep 25, 2019)

juanma2468 dijo:


> Podrías subir un video para ver bien que es lo que te hace, ya que de esa forma me voy a dar cuenta más facil de por donde puede venir el problema, saludos.



Saludos, intento probar tu codigo en este pic pero no funciona como describres.... que será?


```
#include <18F26K22.h>
#Device  PASS_STRINGS=IN_RAM 
#ZERO_RAM       
#FUSES INTRC_IO, NOWDT, BROWNOUT, PUT, NOPBADEN, NOHFOFST
#fuses NOPLLEN        //HW PLL disabled, PLL enabled in software
#fuses MCLR           //Master Clear pin enabled
#fuses PROTECT        //Code protected from reads
#use delay(internal=8MHz)  //CCS
#use rs232(baud=9600,bits=8,parity=N,xmit=PIN_C6,rcv=PIN_C7,ERRORS,stream=uart1)  // Configuración UART1 (Hardware)
#use rs232(baud=9600,bits=8,parity=N,xmit=PIN_B6,rcv=PIN_B7,ERRORS,stream=uart2)  // Configuración UART2 (Hardware)
//#use rs232(baud=9600,bits=8,parity=N,xmit=PIN_C3,rcv=PIN_C2,ERRORS,stream=debug)  // Configuración debug (Software)   (Invertido, para el cable   RX=C2, TX=C3)



#use fast_io(B)
//#use fast_io(C)

// Definiciones del puerto A

#define Led       PIN_B0
#define Led_Ok    PIN_B1
#define Pulsador  PIN_B2

#define Datos     PIN_B4

// Definiciones globales

int i,j,l;
int Salto,Aux,Activado;

#byte IOCA = 0x096

enum
{
   RX_Ocupado = 0,
   RX_Libre,
   RX_Espacio_Tramas,
   RX_Contar_Bits,
   RX_Medicion_Tiempo_Bits,
   RX_Sincronizar,
   RX_Decodificar,
   RX_Codigo_Ok,
   RX_Codigo_Error,
};

typedef struct {
   unsigned int Codigo[3];
   unsigned int Estado;
   unsigned int Resultado;
   unsigned int Grabar;
   unsigned int Contador;
   unsigned int Contador_Bits;
   unsigned int Buscar_Tiempo_ET;
   unsigned int Nuevo_Bit;
   unsigned long Tiempo_Entre_Tramas;
   unsigned long Aux_Tiempo_Entre_Tramas;
   unsigned long PulsoBajo;
   unsigned long PulsoAlto;
   unsigned long Tiempo_ET_delta_neg;
   unsigned long Tiempo_ET_delta_pos;
   unsigned long Tiempo_Bit[2];
   unsigned long Bit;
} RXInfo;
RXInfo RX;

#define Tolerancia (int) 200 // Tiempo = Tolerancia*1useg

void Parpadeo_led (int j){
   for (l=0;l<j;l++)   {
      output_high (Led);
      delay_ms (200);
      output_low (Led);
      delay_ms (200);
   }
}

#INT_RB
void  Ingreso_de_codigo (void) //Maquina para la lectura de bits
{
   switch (RX.Estado)   {
      case RX_Libre:
      {
         if(input(Datos) == 0)            // Entro solo si esta en bajo
         {
            set_timer1(0);                //Inicializo el timer1
            RX.Tiempo_Entre_Tramas = 0;   //Inicializo el valor de Tiempo_Entre_Tramas con 0
            RX.Aux_Tiempo_Entre_Tramas = 0;
            RX.Contador = 0;
            RX.Estado = RX_Espacio_Tramas;//Siguiente paso
         }
      }break;
      case RX_Espacio_Tramas:
      {   
         if(input(Datos) == 1)
         {
            RX.Aux_Tiempo_Entre_Tramas = get_timer1();
            RX.Contador++;
         }
         else
         {
            set_timer1(0);
         }
      }break;
      case RX_Contar_bits:
      {
         if(input(Datos) == 0)            // Entro solo si esta en bajo
            set_timer1(0);                //Inicializo el timer1
         else
         {
            RX.Tiempo_Entre_Tramas = get_timer1();
            set_timer1(0);
            RX.Contador_Bits++;
         }
      }break;
      case RX_Medicion_Tiempo_Bits:
      {
         if (input(Datos) == 1)
         {
               RX.PulsoBajo = get_timer1();
               set_timer1(0);
         }
         else
         {
               RX.PulsoAlto = get_timer1();
               set_timer1(0);
         }
      }break;
      case RX_Sincronizar:
      {
         RX.Contador++;
      }break;                 
      case RX_Decodificar:
      {
         if (input(Datos) == 1)         {
            RX.Bit = get_timer1();
            RX.Contador++;
            RX.Nuevo_Bit = true;
         }
         else
            set_timer1(0);
      }break;
   }
   clear_interrupt(int_rb);
}

void main(){
   set_tris_b (0b10111100); // Entradas:A2,A3,A4,A5 - Salidas:A0,A1
   output_b (0);
   IOCA = 32;
   Salto = read_eeprom (252);
   if (Salto==255)      Salto = 0;
   setup_timer_1 (T1_INTERNAL|T1_DIV_BY_2); // Base de tiempo de 1 useg
   RX.Estado = RX_Libre;
   enable_interrupts (int_rb);
   enable_interrupts (global);
   output_high (Led);
   ///////////////////////////////////////////////////////////////////////////
   for (;;)   {
      // Rutina para grabar controles remotos y configurar
      if (input(Pulsador)==1)      {
         disable_interrupts (int_rb);
         output_low (Led);
         delay_ms (1000);
         // Rutina para grabar controles remotos
         if (input(Pulsador) == 0)         {
            if (Salto < 240)// Puedo grabar hasta 80 codigos de 24 bits
            {
               RX.Grabar = 1;
               RX.Buscar_Tiempo_ET = False;
               RX.Tiempo_ET_delta_neg = 0;
               RX.Tiempo_ET_delta_pos = 0;
               RX.Contador_Bits = 0;
               RX.Tiempo_Bit[0] = 0;
               RX.Tiempo_Bit[1] = 0;
               RX.Estado = RX_Libre;
            }
            else
            {
               Parpadeo_led (4);
               output_high (Led);
               delay_ms(500);
            }
         }
         else
         {// Borro la memoria
            output_high (Led);
            while (input(Pulsador) == 1);
            for (i=0;i<255;i++)
               write_eeprom(i,0xFF);
            Salto = 0;
            Parpadeo_led (5);
            output_high (Led);
         }
         output_high (Led);
         enable_interrupts (int_rb);
      }
      // Rutina de lectura de código
      switch (RX.Estado)
      {
         case RX_Ocupado:
         {
            RX.Bit = 0;
            if (RX.Grabar == 1)
            {
               disable_interrupts (int_rb);
               RX.Grabar = 0;
               for (i=0;i<3;i++)
               {
                  Aux = RX.Codigo[i];
                  write_eeprom(i+Salto,Aux);
               }
               Salto+=3;
               write_eeprom(252,Salto);
               Parpadeo_led (10);
               output_high (Led);
               RX.Estado = RX_Codigo_Error;
            }
            else
            {
               if (Salto != 0)
               {
                  for (i=0;i<Salto;i+=3)
                  {
                     if (read_eeprom(0+i) == RX.Codigo[0])
                     {
                        if (read_eeprom(1+i) == RX.Codigo[1])
                        {
                           if (read_eeprom(2+i) == RX.Codigo[2])
                           {
                              RX.Estado = RX_Codigo_Ok;
                              i = Salto;
                           }
                           else
                              RX.Estado = RX_Codigo_Error;
                        }
                        else
                           RX.Estado = RX_Codigo_Error;
                     }
                     else
                        RX.Estado = RX_Codigo_Error;
                  }
               }
               else
                  RX.Estado = RX_Codigo_Error;
               for (i=0;i<3;i++)
                  RX.Codigo[i] = 0;
            }
         }break;
         case RX_Espacio_Tramas:
         {
            if (RX.Buscar_Tiempo_ET == False)
            {
               if (RX.Contador < 26)
               {
                  if (RX.Tiempo_entre_tramas < RX.Aux_tiempo_entre_tramas)
                     RX.Tiempo_entre_tramas = RX.Aux_tiempo_entre_tramas;
               }
               else
               {
                  RX.Tiempo_ET_delta_neg = RX.Tiempo_entre_tramas-Tolerancia;
                  RX.Tiempo_ET_delta_pos = RX.Tiempo_entre_tramas+Tolerancia;
                  RX.Contador = 0;
                  RX.Buscar_Tiempo_ET = True;
                  RX.Estado = RX_Libre;
               }
            }
            else
            {
               if (RX.Contador < 26)
               {
                  if ((RX.Tiempo_ET_delta_neg < RX.Aux_Tiempo_entre_tramas)&(RX.Aux_Tiempo_entre_tramas < RX.Tiempo_ET_delta_pos))
                  {
                     RX.Buscar_Tiempo_ET = False;
                     RX.Tiempo_Entre_Tramas = 0;
                     RX.Estado = RX_Contar_Bits;
                  }
               }
               else
                  RX.Estado = RX_Codigo_Error;
            }
         }break;
         case RX_Contar_Bits:
         {
            if (RX.Contador_Bits < 26)
            {
               if ((RX.Tiempo_ET_delta_neg < RX.Tiempo_entre_tramas)&(RX.Tiempo_entre_tramas < RX.Tiempo_ET_delta_pos))
               {
                  if ((RX.Contador_Bits == 13)|(RX.Contador_Bits == 25))
                  {
                     RX.Estado = RX_Medicion_Tiempo_Bits;
                  }
                  else
                     RX.Estado = RX_Codigo_Error;
               }
            }
            else
               RX.Estado = RX_Codigo_Error;
         }break;
         case RX_Medicion_Tiempo_Bits:
         {
            if ((RX.PulsoBajo > 0)&&(RX.PulsoAlto > 0))
            {
               if (RX.Tiempo_Bit[0] == 0)
                  RX.Tiempo_Bit[0] = RX.PulsoBajo + RX.PulsoAlto;
               else
               {
                  RX.Tiempo_Bit[1] = RX.PulsoBajo + RX.PulsoAlto;
                  if ((RX.Tiempo_Bit[0] < 5000)&&(RX.Tiempo_Bit[1] < 5000))
                  {
                     if (((RX.Tiempo_Bit[0] - Tolerancia) > RX.Tiempo_Bit[1])|(RX.Tiempo_Bit[1] > (RX.Tiempo_Bit[0] + Tolerancia)))
                     {
                        if (RX.Tiempo_Bit[1] < RX.Tiempo_Bit[0])
                           RX.Tiempo_Bit[0] = RX.Tiempo_Bit[0]/2;
                        else
                           RX.Tiempo_Bit[0] = RX.Tiempo_Bit[1]/2;
                     }
                     else
                        RX.Tiempo_Bit[0] = RX.Tiempo_Bit[0]/2;
                     RX.Contador = 0;
                     RX.Estado = RX_Sincronizar;
                  }
               }
               RX.PulsoBajo = 0;
               RX.PulsoAlto = 0;
            }
         }break;
         case RX_Sincronizar:
         {
            if (RX.Contador_Bits == 13)
            {
               if (RX.Contador == 22)
               {
                  RX.Estado = RX_Decodificar;
                  RX.Contador = 0;
               }
            }
            if (RX.Contador_Bits == 25)
            {
               if (RX.Contador == 46)
               {
                  RX.Estado = RX_Decodificar;
                  RX.Contador = 0;
               }
            }
         }break;
         case RX_Decodificar:
         {
            if (RX.Nuevo_Bit == True)
            {
               RX.Nuevo_Bit = False;
               if (RX.Contador < RX.Contador_Bits)
               {
                  if (RX.Bit < 2000)
                  {
                     if (RX.Bit < RX.Tiempo_bit[0])
                        shift_left(RX.Codigo,3,1);
                     else
                        shift_left(RX.Codigo,3,0);
                  }
                  else
                     RX.Estado = RX_Codigo_Error;
               }
               else
               {
                  RX.Estado = RX_Ocupado;
               }
            }
         }break;
         case RX_Codigo_Ok:
         {
            if (Activado == false)
               Activado = true;
            else
               Activado = false;
            RX.Estado = RX_Codigo_Error;
         }break;
         case RX_Codigo_Error:
         {
            if (RX.Grabar == 1)
            {
               RX.Grabar = 0;
               Parpadeo_Led (2);
               output_high (Led);
            }
            RX.Buscar_Tiempo_ET = False;
            RX.Tiempo_ET_delta_neg = 0;
            RX.Tiempo_ET_delta_pos = 0;
            RX.Contador_Bits = 0;
            RX.Tiempo_Bit[0] = 0;
            RX.Tiempo_Bit[1] = 0;
            RX.Estado = RX_Libre;
         }break;
      }
      // Rutina de funcionamiento
      if (Activado == true)
         output_high (Led_Ok);
      else
         output_low (Led_Ok);
      // Rutina de borrado de controles remotos
      enable_interrupts (int_rb);
   }
}
```


----------



## danielnovara22 (Jun 14, 2021)

Hola juanma, he estado tratando de hacer funcionar correctamente el codigo de manera fisica en una proto. A veces lo logro, a veces no. Que me recomentarías?
Tengo un control de un porton, que codifica con el HT12E. lo que no logro es que funcione correctamente la secuencia para "leer y guardar" nuevos controles.



juanma2468 dijo:


> Hola frankko, te adjunto la ultima versión de código que tengo totalmente funcional. Lo que te paso es el código para registrar un código en la memoria eeprom del pic 16F684 para luego poder reconocerlo y asi poder activar/desactivar la salida. Como hice un recorte y pego, puede que tengas algunas variables de más dando vuelta, cualquier problema me avisas pegandome el log de los errores que te de al compilar. También te adjunto el esquemático para que sepas como van conectado las cosas. Para grabar el código lo que tenes que hacer es apretar el pulsador hasta que se apaga el led, cuando lo hace soltas el pulsador y precionas el botón del control a registrar (esto miestras el led está apagado), si lo toma y registra el led parpadeará unas 10 veces rápidamente, luego vuelve a prenderse. Después de eso ya podrás usar el receptor. Cualquier duda me avisa, saludos.
> FE de erratas.
> En la parte de código donde dice
> If (Activacion==true)
> ...


Hola Juanma, Estuve trabajando con estos códigos, y los que has pasado previamente. Estoy haciendo un proyecto para la facultad. Básicamente tengo que hacer una receptora para hacer funcionar un relé. En simulación funciona bastante bien, el tema es que en la placa que arme, a veces anda, a veces no. Mi emisor se basa en el HT12E. Trabajo con el PIC16f630 con un osc interno de 4M. al intentar compilar este código precisamente me sobran variables. Has podido avanzar en algo con esto?


----------



## resistencio (Ago 25, 2022)

Y no hay caso che, esta fusión de códigos no funca...es una involución a la sorprendente *VERSION 3.0 de JUANMA*, cuando la hizo sin intentar fusionar nada...No tengo especificamente el 16F684, y migré a 16F628a, ya ahí la primera difierencia es que no existe INT_RA en este micro, asi que fuí por la INT_RB, como hizo *lycans2005, *a quien tampoco le anduvo...Desde hace 5 años que nadie salvo el propio *JUANMA*, lo pudo hacer funcionar parece...Igual para jugar un rato esta bueno...Voy a seguir probando con otras interrupciones a ver si sale algo.


----------

