RF remote control receiver (433MHz ASK to decode data coded by HT6P20X series)

Hi: I made a code to receive RF data from a generic RF ASK 433 MHz remote control. Usually these RC have Holtek HT6P20x chip inside with 2 or 4 channels. (http://www.holtek.com/pdf/consumer/6p20v170.pdf)

The concept is simple, I programmed an external pin interrupt. The interrupt occurs with every pin change. I register de pin value and the pin time (micros() function) with every interrupt, in 2 circular buffers. Then in the loop program, if new data is available it is analyzed to recognize the Holtek encoding frame.

A lot of noise is received constantly by the RF receiver. This is normal in ASK receivers, I mus live with it. But Holtek chip send a pilot signal with every transmission and transmissions are repeated many times while you keep the RC button pressed, each one last few milliseconds, so to avoid noise receptions I check 2 consecutive pilot signals and then read and analice the data.
Still some noise data is recognized like a RC address and code and don't know how to avoid. Never the less, my car alarm works!

The code worked fine but I'd like to exhibit this code to the community to be criticized and improved.

Please, any kind of feedback is accepted an appreciated!

Thanks for your time and regards. :wink:

///////////////////////////////////////////////////////////////////////////////////////////////////
// DEFINE
///////////////////////////////////////////////////////////////////////////////////////////////////

#define  top_unsigned_long 4294967295



///////////////////////////////////////////////////////////////////////////////////////////////////
// GLOBAL VARIABLES
///////////////////////////////////////////////////////////////////////////////////////////////////

volatile static int pe=0,pl=0, rc_index;
volatile static long unsigned int buftime[100];
volatile static boolean bufdata[100];
static byte rc_code=0;
static long unsigned int rc_address=0;
static long unsigned int rc_habilitados[10];
static unsigned long x,address,code;
static boolean modo_aprendizaje=false;


///////////////////////////////////////////////////////////////////////////////////////////////////
// SETUP 
///////////////////////////////////////////////////////////////////////////////////////////////////

void setup()
{
  pinMode(2,INPUT);
  digitalWrite(2, HIGH); 
  pinMode(13,OUTPUT);
  Serial.begin(115200);
  attachInterrupt(0,Holtek_rx_pinchange,CHANGE);
}



///////////////////////////////////////////////////////////////////////////////////////////////////
// LOOP
///////////////////////////////////////////////////////////////////////////////////////////////////

void loop()
{
  int n;
  long unsigned int rc_aprendido;
  static boolean led=false;
  static unsigned long auxaddress;
  static int pasoapre=0;


// RC Read  ////////////////////////////////////////////////////////////////////////////  
  
  x=Holtek_decoder();
  
  if (x!=0ul) 
  {
    address=x>>4;
    code=x & 0x000F;
    Serial.print(address);Serial.print("   "); Serial.println(code);
  }

address=0;
code=0;
  
}









/////////////////////////////////////////////////////////////////////////////////////////////////////////
//  SUBPROGRAM
/////////////////////////////////////////////////////////////////////////////////////////////////////////*/

unsigned long Holtek_decoder()
{
  static long unsigned int dift,resultado,tbase1=450ul,tbase0=650ul;
  static int pasotrama=0,bit_no,byte_no=0,shift,countpiloto=0,countchanges=0;

resultado=0ul;

// Controls every received new bit

  if (pl!=pe)
  {  
  
  dift=0;
  if (pl>0) 
  {
    dift=buftime[pl]-buftime[pl-1];
    buftime[pl-1]=0ul;
  }
  
  if(countchanges++>58) countchanges=countpiloto=pasotrama=0;
  
    switch (pasotrama)
    {
      case 0:  //1st PILOT 23 clocks Zero & 58 pin changes.
        if (dift>11000ul && dift<11500ul && !bufdata[pl]) 
        {
          countpiloto++;
          countchanges=0;
          pasotrama=3;
        }
        rc_address=rc_code=0;
        break;    //waits for next bit
        
      case 1:  //2nd PILOT 23 clocks Zero & 58 pin changes.
        if (dift>11000ul && dift<11500ul && !bufdata[pl] && countchanges==58)  
        {
          countpiloto++;
          countchanges=0;
          pasotrama=3;
        }
        rc_address=rc_code=0;
        break;   //waits for next bit

      case 3:  //  1/3 bit clock synchro
        if (dift<=450ul && bufdata[pl] && countchanges==1 && countpiloto==1) 
        {
        pasotrama=4;
        }
        else pasotrama=0;
        rc_address=rc_code=0;
        break;  //waits for next bit
        
        // Receive RC ADDRESS
      case 4:  //receive 20 address bits (60 clocks) & 4 code bits (12 clocks)  (even if RC uses an 8 pinn chip, 24 bits are always sent)  (read Holtek datasheet)
        if (bufdata[pl] && dift>=tbase0)  //Coded "0"
        {
          shift++;
        }
        else if (bufdata[pl] && dift<=tbase1)  //Coded "1"
        {
          bitSet(rc_address,shift);
          shift++;
        }
        if (shift==20) 
          {
            shift=0;
            pasotrama=5;
          }
        break;   //waits for next bit     
        
        // Receive CODE
      case 5:    //receive 20 address bits (60 clocks) & 4 code bits (12 clocks)  (even if RC uses an 8 pinn chip, 24 bits are always sent)  (read Holtek datasheet)
        if (bufdata[pl] && dift>=tbase0)  
        {
          shift++;
        }
        else if (bufdata[pl] && dift<=tbase1)  
        {
          bitSet(rc_code,shift);
          shift++;
        }
        if (shift==4) 
          {
            shift=0;
            pasotrama=6;
          }
        break;   //waits for next bit  

        case 6:
          pasotrama=0;
          resultado=rc_address<<4|rc_code;
          break;    
      }
  
    if( (pl++) >= 100) pl=0;

  }

return (resultado);

}



/////////////////////////////////////////////////////////////////////////////////////////////////////////

/////////////////////////////////////////////////////////////////////////////////////////////////////////
//  INTERRUPTS
/////////////////////////////////////////////////////////////////////////////////////////////////////////*/

void Holtek_rx_pinchange()
{
  buftime[pe]=micros();
  bufdata[pe]=!digitalRead(2);  //Inverted so the registered time corresponds to the registeres bit level
  if ( (pe++) >= 100) pe=0;
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////

I like the idea with having an interrupt and writing the micros() in a buffer, this is the same idea I had when I was writing a similar program. However I am not familiar with this encoder, so I can't say anything about the decoding procedure.

Much as I hate waking long dead threads, I felt I had to confirm this works for the Freidland Battery operated PIR. F1023R Wireless PIR Movement Detector - Product ref 40054676-001 , which was sold in a B&Q in the UK up until a couple of years back.

I have a couple of these PIRs in my junk box, and dug them out for a bit of fun. A quick Google trawl on the partially obscured controller chip on the board told me they were Holtek based, and a little more digging brought me here.

I get a fairly usable result in the serial monitor from the above code every time the device is triggered...

1048575 15
1048575 15

.. not 100% reliable, I do see quite a lot of noise... but all in all not bad for starters.

If I get some spare time, I may look at enhancing the code...

I see from your code that the timebase is hard coded, (tbase1 = 450ul, tbase0 = 650ul;) would it not be more flexible to calculate the timebase from the array, then use that to set tbase1 and tbase0

My understanding of your code suggests that the array of 100 pin toggles represents 50 bits of information, we therefore should be able to estimate one cycle (wavelength) of the clock (1/fosc) as 1/150 of the time between the first transistion and the last (since we have three complete clock cycles per bit), and thus the tbase1 time is 1 high clock cycle and tbase0 timings as 2 high clock cycles (plus or minus any error).

That way the code becomes clock agnostic, in other words we dont need to worry about the exact speed of the source clock. I say this because there are a number of versions of this Holtek encoder, which typically operate at different cpu clocks. The version I have for example uses a 1MHz ceramic resonator, but other versions run using the internal RC oscillator (which will be both much slower, and also more prone to timing variations and thus errors).