Saturday, March 5, 2016

Static Wireless Sensor Network with Arduino NRF24l01

Hi,
   Right from my childhood I wanted to control appliances and had an eye for Automation
may be I'm just too lazy , two years back I wrote the software for this !

Video of my WSN NRF24L01+Arduino
code is somewhat like this .

NRF24L01 packet

class packet {
  public :
    unsigned char  frame_type; // data or synchronization 
    unsigned short int to_add; // packet destination address
    unsigned short int from_add; // packet origin address 
    unsigned short int h_from_add; // packet immediate hopped address
    unsigned short int frame;          // packet send on frame 
    unsigned long data1;                 // data holder
};


Arduino setup function , here were are setting up nrf24l01/serial/ZCD .

void setup()
{
  if ( cid == 0 ) {
    pinMode(2, OUTPUT);
  } else {
    pinMode(2,INPUT);
  }
  
  pinMode(DETECT, INPUT);     //zero cross detect
  digitalWrite(DETECT, HIGH); //enable pull-up resistor
  pinMode(GATE, OUTPUT);      //triac gate control

  Serial.begin(57600);

  while (!Serial) ;
  Serial.println("start ");

  if (!nrf24.init())
    Serial.println("NRF24 init failed");
    
  if (!nrf24.setChannel(B_Freq))
    Serial.println("setChannel failed");

   mask1 = mask | cid;
   if (!nrf24.setThisAddress((uint8_t*)(&mask1), 5))
     Serial.println("setThisAddress failed");

  if (!nrf24.setPayloadSize(len))
    Serial.println("setPayloadSize failed");

  if (!nrf24.setRF(NRF24::NRF24DataRate2Mbps, NRF24::NRF24TransmitPower0dBm))
    Serial.println("setRF failed");    

    ping_s = 0;
    cnt =0;
    frame = 150;
    route_packet = 0;
    route_pac_delay = 0;
    num_retries = 0 ;
    b_cnt = 0;
    data.h_from_add = cid ;

    data.frame_type = 0;
    noInterrupts();           // disable all interrupts
    TCCR1A = 0;
    TCCR1B = 0;
    TCNT1  = 0;

    OCR1A = 30000;            // compare match register 16MHz/256/2Hz
    TCCR1B |= (1 << WGM12);   // CTC mode
    TCCR1B |= (1 << CS11);    // 256 prescaler 
    TIMSK1 |= (1 << OCIE1A);  // enable timer compare interrupt
    interrupts();             // enable all interrupts 
    
    if ( cid == 0 )  {
          is_sync = 1;
    } else {
          is_sync = 0;
    }
    randomSeed(cid*10);
    b_cnt = random(200,500);
    
    ping_timeout=random(200,500);
    ping_retries=0;
    
    Serial.print("Curr ID ");
    Serial.println(data.h_from_add,DEC);
    attachInterrupt(1,zeroCrossingInterrupt, CHANGE );
}


Below Function will send data to next hop address based on routing table .

bool route_data () {
    routed = 0 ;
    for ( cnt = 0 ; cnt <= luk_len ; cnt ++ ) {
if ( (data.to_add >= luk_tab[cnt][0] && data.to_add <= luk_tab[cnt][1] ) ) {
                      routed = 1;
if ( cnt == luk_len ) {
send_to(up_link);
} else {
       send_to(luk_tab[cnt][0]);
}
                        break ;
if ( routed == 1 ) {
break ;
}
      }
   }
      
   if ( routed == 0 || routed != 1 ) {
// NOT ABLE TO SEND THE data .
   }
   return (routed==1) ;
}

Actual function which will send data via NRF24L01 , it will also select frequency based on to_address and frame number .

bool send_to (unsigned short int to_addr ) {
        if ( b_flag == 0 ) {
        f  = to_addr * 11117 + ( frame *31 ) ;
         f = (f/13) % 11 ;
                f = (f * 10) +1 ;
        } else {
                f = B_Freq;
        }
                
        if (!nrf24.setChannel(f))
          Serial.println("setChannel failed");
        
mask1 = mask | to_addr ;

  if (!nrf24.setTransmitAddress((uint8_t*)(&mask1), 5)) {
  Serial.println("setTransmitAddress failed");
routed = 2; 
}

        delayMicroseconds(700);
       
        if ( b_flag == 1 ) {
          data.frame = frame;
          rtt = TCNT1;
          data.data1 = rtt + 1700 ;
          data.frame_type = 3;
        }
        data.h_from_add = cid;       
        if ( debug == 1 && data.frame_type == 2) {
          Serial.print(to_addr,DEC);
          Serial.print(" to add ,S'dg ");
          Serial.print(data.data1,DEC);
          Serial.print(" frame is ");
          Serial.println(frame,DEC);
        }
        if (!nrf24.send((uint8_t*)&data, sizeof(data),b_flag)) {
            Serial.println("E4"); 
   routed = 3;
}
        
        if (!nrf24.waitPacketSent()) {
          Serial.print("Sndg Fail ");
 routed = 4;
        }
        return ( routed == 1 || b_flag ) ;
}

Receiver function , configures NRF24L01 in RX mode and selects frequency based on same formula used in TX mode .

bool listen_rx() {
  f  = cid * 11117 + ( frame *31 );
  f = (f/13) % 11 ;
  f = (f * 10) +1 ;

  nrf24.powerUpRx();
  if (!nrf24.setChannel(f))
     Serial.println("setChannel failed");
   if (nrf24.waitAvailableTimeout(5)) {
          if (!nrf24.recv((uint8_t*)&data_junk, &len)) {
            Serial.println("read failed");
   return 0;
 }

       if ( debug == 1 && data_junk.frame_type==2) {
          if ( serial_read == 'A' ) {
             trigger_delay = data_junk.data1 -65 ;
             Serial.print("setting val ");
             Serial.println(trigger_delay , DEC );        
          }
          Serial.print(data_junk.h_from_add,DEC);
          Serial.print(" rx from add ,S'dg ");
          serial_read = data_junk.data1;
          Serial.print(serial_read,DEC);
          Serial.print(" frame is ");
          Serial.println(frame,DEC);
          
        }
        
        if ( data_junk.frame_type == 2 && data_junk.to_add != cid) {
            data_in=data_junk;
            route_packet =1;
            route_pac_delay = random(2,8);
            num_retries = 0 ; 
        }
   }

  return 1;
}

Function used for synchronizing two nodes , received beacon packet will have counter value which is used to load into TCNT1 of received node .

void sync_me (bool ip) {
 
        nrf24.powerUpRx();
        if (!nrf24.setChannel(B_Freq))
  Serial.println("setChannel failed");
          
        mask1 = mask | up_link ;
        
        if (!nrf24.setTransmitAddress((uint8_t*)(&mask1), 5))
    Serial.println("setTransmitAddress failed");
         if ( ip == 1)  {
         }

         if ( ip == 1 ) {
             TCCR1B &= ~(1 << CS11);    // stop counter
    nrf24.waitAvailable() ;
             TCCR1B |= (1 << CS11);    // start counter
         } 

if (!nrf24.recv((uint8_t*)&data, &len))
 Serial.println("read failed");
        
TCNT1 = data.data1 - 500;
frame = data.frame;
mask1 = mask | data.h_from_add ;
is_sync =1; 

        nrf24.setTransmitAddress((uint8_t*)"NULL1", 5);
  
}

Function used to send beacon packet for slave nodes 

void send_beacon () {
 // Serial.print("Send beac ");
 //  Serial.println(frame,DEC);

   b_flag = 1;
   send_to(cid);
   b_flag = 0;
   b_cnt = random(300,600);

}

Function calling route_data() and from there send_to() .

void send_packet () {
  //Serial.println("send pack");
  data=data_in;
  data.frame_type =2;
  if ( ! route_data() ) {
num_retries++;
if ( num_retries > 10 ) {
                         Serial.println("Droping pack");
route_packet =0 ;
num_retries = 0 ;
}
} else {
route_packet = 0;
num_retries = 0 ;
}
route_pac_delay = random(5,15);
data.frame_type = 0;
}

MISC functions used for book keeping

bool process_state () {
  while ( ping_s == 0 ) {delayMicroseconds(1);}
  ping_s = 0 ;
  frame= frame+1 ;
  if ( frame > 150 ) {frame = 0;}
}

bool post_processing () {
// nrf24.powerDown();
if ( route_pac_delay > 0 ) {
  route_pac_delay--;
}
 b_cnt ++ ;
 ping_timeout++ ;
}

Functional loop used to send/receive/synchronize nodes .

void loop() {
 loop_var = 10;
 

 process_state();
 
 if ( cid == 0 ) {
   digitalWrite(2,HIGH); 
   digitalWrite(2,LOW); 
 }
 
     
 if ( is_sync == 0 ) {
    loop_var = 3;
   sync_me(1);
   Serial.println("sync'd");
 } else if ((cid+2) == frame  ) {
    loop_var = 4;
    send_beacon();
 }  else if ( (up_link+2)==frame ) {
    loop_var = 5;
       sync_me(0);
 } else if (route_packet == 1 && route_pac_delay == 0) {
    loop_var = 6;
      send_packet();
 } else if (route_packet == 0 && Serial.available()) {
      loop_var = 7;
     Serial.println(max_tcnt,DEC);
      data_in.data1 = Serial.read(); ;
      data_in.to_add = ping_node ;
      data_in.from_add = cid ;
      route_packet =1;
      num_retries = 0 ;
 } else {
    loop_var = 8;
    listen_rx();
 }

 nrf24.setTransmitAddress((uint8_t*)"NULL1", 5);

 post_processing();

 if ( ping_s == 1  ) {
   Serial.print("Error ");
   Serial.println(loop_var,DEC);
 //  delay(100000);
 }
}


I'll update more information and few pictures explaining how routing between nodes work .