< async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"">

Friday, June 27, 2014

A simple 2.4GHZ spectrum analyser with nrf24l01,character lcd and an arduino

Arduino nrf24l01 ghz spectrum scanner

In this weekend i planned to make a simple scanner for the 2.4ghz ism band to search for interesting findings. So a simple poormans spectrum analyser was hooked up with an lcd shield (for displaying characters) and a popular nrf24l01+ module from nordic semiconductor.

This is basically a portable version of the poor mans  scanner based on nrf24l01. I added an lcd display and used the custom charecters to draw a simple bar graphs to show the signal intensities. it was a bit tricky to use the spi pins with the dfrobot keypad shield (some fuzz with the digital pin 10 which is messsed up in the shield). Using a software spi with the nrf24l01 module didn't work as expected. So i ended up bending the D10 pin on the shield :) and using hardware spi with the nrf module.

A simple video is shown below. The bars shows my wifi router. The entire spectrum is fitted in to 16 possible bars (made by custom chars) in the character lcd. Turning on a bluetooth device shows random spots over the entire channels (hopping) and microwaves ovens appears all over the spectrum at lunchtimes!

end

Source code and wiring

Connect the nrf module to the spi pins (10,11,12,13)  and A1 (for CE) and an lcd to pin 8,9,4,5,6,7 

 #include <SPI.h>  
 #include <LiquidCrystal.h>  
 // The LCD is conected to pins 8, 9, 4, 5, 6, 7  
 LiquidCrystal lcd(8, 9, 4, 5, 6, 7);  
 byte minibars[8][8];  
 //credits to Poor Man's Wireless 2.4GHz Scanner  
 // credits to all others  
 // uses an nRF24L01p connected to an Arduino  
 //   
 // Cables are:  
 //   SS    -> 10  
 //   MOSI   -> 11  
 //   MISO   -> 12  
 //   SCK   -> 13  
 //   
 // and CE    -> A1  
 //  
 // created March 2011 by Rolf Henkel  
 //  
 #define CE A1  
 // Array to hold Channel data  
 #define CHANNELS 64  
 int channel[CHANNELS];  
 // nRF24L01P registers we need  
 #define _NRF24_CONFIG   0x00  
 #define _NRF24_EN_AA    0x01  
 #define _NRF24_RF_CH    0x05  
 #define _NRF24_RF_SETUP  0x06  
 #define _NRF24_RPD     0x09  
 //SoftSPI<SOFT_SPI_MISO_PIN, SOFT_SPI_MOSI_PIN, SOFT_SPI_SCK_PIN, SPI_MODE> spi;  
 // get the value of a nRF24L01p register  
 byte getRegister(byte r)  
 {  
  byte c;  
  PORTB &=~_BV(2); //D10  
  //PORTC &=~_BV(1); //analogue 1 as SS  
  c = SPI.transfer(r&0x1F);  
  c = SPI.transfer(0);   
  PORTB |= _BV(2);  
  //PORTC |= _BV(1); //analogue 1 as SS  
  return(c);  
 }  
 // set the value of a nRF24L01p register  
 void setRegister(byte r, byte v)  
 {  
  PORTB &=~_BV(2);  
  //PORTC &=~_BV(1);  
  SPI.transfer((r&0x1F)|0x20);  
  SPI.transfer(v);  
  PORTB |= _BV(2);  
  //PORTC |= _BV(1);  
 }  
 // power up the nRF24L01p chip  
 void powerUp(void)  
 {  
  setRegister(_NRF24_CONFIG,getRegister(_NRF24_CONFIG)|0x02);  
  delayMicroseconds(130);  
 }  
 // switch nRF24L01p off  
 void powerDown(void)  
 {  
  setRegister(_NRF24_CONFIG,getRegister(_NRF24_CONFIG)&~0x02);  
 }  
 // enable RX   
 void enable(void)  
 {  
   PORTC |= _BV(1);  
 }  
 // disable RX  
 void disable(void)  
 {  
   PORTC &=~_BV(1);  
 }  
 // setup RX-Mode of nRF24L01p  
 void setRX(void)  
 {  
  setRegister(_NRF24_CONFIG,getRegister(_NRF24_CONFIG)|0x01);  
  enable();  
  // this is slightly shorter than  
  // the recommended delay of 130 usec  
  // - but it works for me and speeds things up a little...  
  delayMicroseconds(100);  
 }  
 // scanning all channels in the 2.4GHz band  
 void scanChannels(void)  
 {  
  disable();  
  for( int j=0 ; j<100 ; j++)  
  {  
   for( int i=0 ; i<CHANNELS ; i++)  
   {  
    // select a new channel  
    setRegister(_NRF24_RF_CH,(128*i)/CHANNELS);  
    // switch on RX  
    setRX();  
    // wait enough for RX-things to settle  
    delayMicroseconds(40);  
    // this is actually the point where the RPD-flag  
    // is set, when CE goes low  
    disable();  
    // read out RPD flag; set to 1 if   
    // received power > -64dBm  
    if( getRegister(_NRF24_RPD)>0 )  channel[i]++;  
   }  
  }  
 }  
 void outputChannels(void)  
 {  
  int norm = 0;  
  for( int i=0 ; i<CHANNELS ; i++)  
   if( channel[i]>norm ) norm = channel[i];  
  for( int i=0 ; i<CHANNELS ; i++)  
  {  
   int pos;  
   if( norm!=0 ) pos = (channel[i]*10)/norm;  
   else     pos = 0;  
   if( pos==0 && channel[i]>0 ) pos++;  
   if( pos>8 ) pos = 8;  
   plot_minibars(i/4, pos*2);  
   channel[i] = 0;  
  }  
 }  
 void setup()  
 {  
   // Built the characters for bars.  
  for (int j=0; j<=7; j++)   
  {  
   for (int i=0; i<=7; i++)  
   {  
    if (i<=j)  
    { minibars[j][7-i] = B01110;}    
    else  
    { minibars[j][7-i] = 0;}  
   }  
  }   
  for (int i=0; i<=7;i++)  
  {  
   lcd.createChar(i, minibars[i]);  
  }  
  lcd.begin(16, 2);     
  for (int j=0; j<=7;j++)  
  {  
   lcd.setCursor(j, 0);  
   lcd.write(j);  
   lcd.setCursor(j, 1);  
   lcd.write(7);   
  }  
  // Setup SPI  
  SPI.begin();  
  SPI.setDataMode(SPI_MODE0);  
  SPI.setClockDivider(SPI_CLOCK_DIV2);  
  SPI.setBitOrder(MSBFIRST);  
  // Activate Chip Enable  
  pinMode(CE,OUTPUT);  
  disable();  
  // now start receiver  
  powerUp();  
  // switch off Shockburst  
  setRegister(_NRF24_EN_AA,0x0);  
  // make sure RF-section is set properly   
  // - just write default value...   
  setRegister(_NRF24_RF_SETUP,0x0F);   
  }  
 void loop()   
 {   
  scanChannels();   
  outputChannels();   
 }  
 void plot_minibars(int location, int strngth)  
 {  
  if (strngth>7)  
  {  
   lcd.setCursor(location, 1);  
   lcd.write(7);  
   lcd.setCursor(location, 0);  
   lcd.write(strngth-8);    
  }  
  else  
  {  
   lcd.setCursor(location, 1);  
   lcd.write(strngth);  
   lcd.setCursor(location, 0);  
   lcd.write(32);    
  }  
 }  

Monday, June 23, 2014

Online lcd custom character font generator with binary and hex codes for Arduino lcd displays

This can be used to generate fonts and patterns for using with arduino sketches. Insert the generated codes in the font or pattern definitions in the sketch. Both forms will work and the hex form will keep the source code small and neat. For example, see the following direct drive sketch. To generate a pattern , click on the grid and toggle it to on (1). Once you are done with the font / pattern, copy the binary values or hex values to your sketch



8x5 ONLINE CUSTOM CHARACTER GENERATOR FOR ARDUINO LCD

 0  0  0  0  0
 0  0  0  0  0
 0  0  0  0  0
 0  0  0  0  0
 0  0  0  0  0
 0  0  0  0  0
 0  0  0  0  0
 0  0  0  0  0
Binary values:

Hex values:


Example usage

 #include <LiquidCrystal.h>  
 LiquidCrystal lcd(12, 11, 5, 4, 3, 2);  
 byte smiley[8] = {  
  B00000,  
  B10001,  
  B00000,  
  B00000,  
  B10001,  
  B01110,  
  B00000,  
 };  
 void setup() {  
  lcd.createChar(0, smiley);  
  lcd.begin(16, 2);   
  lcd.write(byte(0));  
 }  
 void loop() {}  

For More details see here

Saturday, June 14, 2014

Controlling a 27mhz remote control car with AD9850 and arduino uno

Controlling an RC car with AD9850 dds module and an arduino

If you have an rc car, an ad9850 board (cheap modules available on ebay) and an arduino uno, it is possible to convert them to a computer controlled or programmable rc car. After seeing a post on converting a cheap rc car by jon, to a programmable one by modifying the remote control with an arduino, i decided to make a universal controller which doesn't need to solder or modify the cars electronics. So this approach can add an independent control channel and some fun with decryping and cloning rc remotes of simple toy grade cars (without any encryption)

RC car control signals

A simple rc car uses an amplitude modulated carrier (rf signal) to send the control signal. So most of simple toys uses a 4 pulses of a specific length followed by a multiple of pulses to send a specific command (more of this in a later post). I used gnuradio and funcube dongle to capture the signals and to analyse the control signals. Brandon has a similar project where he used a raspberry pi's pll to directly generate the rf signals and brute force to identify the correct pulse lengths. You can use audacity to measure the pulse lengths after capturing the audio from a radio receiver or sdr tuned to 27mhz signals from the rc remote [read more on decoding the signals using sdr].

AD9850 control signals and speed issues

One of the challenge with this project was the time delays while updating the ad9850 frequency words via arduino. It was overcomed by using the hardware spi for controlling the frequency and usng the PORT commands instead of the digitalwrites. An other small issue was with analogue read delays, which was used to read the keypad inputs. This was solved with an adc update and later i found that all these issues were addressed in a project by Zisis Chiotis which implements an fm modulation on an arduino! (at around 16khz)

connecting ad9850 to arduino spi pins

Using a single analogue pin to assign multiple key functions to control the rc car


Data connections to AD9850. See here for the corresponding pins


  • MOSI = DATA (pin 11 on uno) 
  • CLK = W_CLK (pin 13 on uno) 
  • FU_UD (PIN 6 on uno)  (see port command here)
  • GND = RESET

Source code

//#AD9850 rc remote control  
//blog.riyas.org  
//courtesy to Zisis Chiotis
  
#define PLEN 550  //optimised pulse length adjust this to match your remote:-audacity and sdr
#define PRELEN PLEN*3
#define FREQUENCY 542879
#define FREQUENCY2 0
#define btnRIGHT 0
#define btnUP   1
#define btnDOWN  2
#define btnLEFT  3
#define btnNONE  4
int adc_key_in = 0;
int key = 0;
#include <SPI.h>
uint32_t frequency = FREQUENCY;    //The desired frequency must be divided by 50 ex. 34,7MHz/50 = 694000  
uint32_t tword = frequency * 3436 / 100;    //tuning word calculation  
byte W[5] = { 0, 0, 0, 0, 0 };
const unsigned char PS_128 = (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0);    //these are the prescalers for the ADC sampling rate.  
const unsigned char PS_32 = (1 << ADPS2) | (1 << ADPS0);
const unsigned char PS_16 = (1 << ADPS2);
int mic = 512;
void
setup ()
{
  Serial.begin (9600);
  DDRD = DDRD | B01100000;    //  
  PORTD = 0x00;
  // Serial.begin(115200);          // everything can be controlled by serial comm  
  pinMode (A0, INPUT);        // here comes the audio frequency in  
  SPI.setDataMode (SPI_MODE0);    // mode 0 seems to be the right one  
  SPI.setClockDivider (SPI_CLOCK_DIV2);    // this is pretty fast  
  SPI.setBitOrder (LSBFIRST);    // AD9850 wants LSB first  
  SPI.begin ();
  // set up the ADC  
  ADCSRA &= ~PS_128;        // remove bits set by Arduino library  
  ADCSRA |= PS_32;        // setting the sampling rate at 16MHz/32 this makes the analogRead() complete in around 40μs  
}

void
loop ()
{          
  key = read_key ();
  switch (key)            
  {
  case btnRIGHT:
    {
      Serial.println ("FORWARD");
      sendcommand (10);
      break;
    }
  case btnLEFT:
    {
      Serial.println ("RIGHT");
      sendcommand (34);
      break;
    }
  case btnUP:
    {
      Serial.println ("BACKWARD");
      sendcommand (40);
      break;
    }
  case btnDOWN:
    {
      Serial.println ("LEFT");
      sendcommand (28);
      break;
    }
  case btnNONE:
    {
      //Serial.println("NONE");   
      break;
    }
  }
}

void
preample (int len)
{
  int i;
  for (i = 0; i < len; i++) {
    setfreq (FREQUENCY);
    delayMicroseconds (PRELEN);
    setfreq (FREQUENCY2);
    delayMicroseconds (PLEN);
  }
}

void
command (int len)
{
  int i;
  for (i = 0; i < len; i++) {
    setfreq (FREQUENCY);
    delayMicroseconds (PLEN);
    setfreq (FREQUENCY2);
    delayMicroseconds (PLEN);
  }
}

void
setfreq (uint32_t frequency)
{
  tword = frequency * 1718;    //calculating the tuning word for AD9850  
  W[0] = (byte) tword;
  W[1] = (byte) (tword >> 8);
  W[2] = (byte) (tword >> 16);    //converting it to bytes  
  W[3] = (byte) (tword >> 24);
  W[4] = 0;            //phase zero  
  //start sending with spi interface  
  PORTD = B01000000;
  PORTD = 0x00;            //pulse FU_UD  
  for (int j = 0; j < 5; j++) {
    SPI.transfer (W[j]);    //send the word  
  }
  PORTD = B01000000;
  PORTD = 0x00;            //pulse FU_UD  
}

int
read_key ()
{
  adc_key_in = analogRead (0);
  if (adc_key_in > 600)
    return btnNONE;
  if (adc_key_in > 500)
    return btnRIGHT;
  if (adc_key_in > 450)
    return btnUP;
  if (adc_key_in > 400)
    return btnDOWN;
  if (adc_key_in < 400)
    return btnLEFT;
  return btnNONE;
}

void
sendcommand (int n)
{
  for (int i = 0; i < 5; i++) {
    preample (4);
    command (n);
  }
}
end

Sunday, June 8, 2014

Simple guide to using funcube with gnuradio on ubuntu for fm reception

After a short break, i started to use gnuradio to learn more on the rf spectrum. To start with, i hooked my funcube dongle and used gqrx and worked well. But when i started to use the gnuradio, things have changed a lot in a couple of months. In my previous post, i showed a simple gnuradio companion sketch (read more here). For some strange reasons, i started spending the whole day installing, removing and compiling gnuradio :) So here is a short notes on what worked at the end.

At first i checked the frequency control application which came with funcube and showed the following error! FCD dongle not found even i have it plugged

No FCD detected

So solution is simple once you know it. I was using Ubuntu 12.04.4 LTS (Precise Pangolin) and a funcube pro plus. These are important as i spend a while with old version of qthid and funcube dongle (not pro) files. So ensure that you download  qthid-4.1-linux-i386.tar.gz from sourceforege. Even after that, i got the same error. So i just do a

 sudo cp funcube-dongle.rules /etc/udev/rules.d/

This could save a couple of hours as i wasted a while with a wrong version of qthid and the rules file. Make sure that the rules file support the pro plus (open in a text editor)

Gnuradio installation 


As usual i started from the source using the buildgnuradio script which makes the process so simple. But the result was not so good for me and ended up with a lot of credumps from the compiled binaries. So ended up wasting a lot of time. It seems after version 3.7 some of the modules or blocks are placed in a different order in the gnuradio, giving rise to missing blocks with old grc files. Unfortunately i deleted all the source files which nade my uninstallation of the gnuradio in pain. I manually deleted all the files related to gnuradio from /usr/local/bin and usr/local/lib and from the python dist. After that i installed the binaries from the repository (sudo apt-get install gnuradio) and to my surprise all worked like a charm. 

Attached a simple grc file i made to test with the broadcast fm stations. use the big slider for course tuning or just input the frequency in herts to the text box. And finally, the new version of gnuradio in the repository (3.7.0+1git20130729-g5eaeaa42-0ubuntu0~gqrx~precise3 ) has a support for funcube pro plus so you dont really need the qthid

Save the below file as bm_rx.grc and open with gnuradio-companion and click run

 <?xml version='1.0' encoding='ASCII'?>  
 <flow_graph>  
  <timestamp>Sun Jun 8 16:28:59 2014</timestamp>  
  <block>  
   <key>options</key>  
   <param>  
    <key>id</key>  
    <value>rc27decoder</value>  
   </param>  
   <param>  
    <key>_enabled</key>  
    <value>True</value>  
   </param>  
   <param>  
    <key>title</key>  
    <value>Decode RC remote</value>  
   </param>  
   <param>  
    <key>author</key>  
    <value>Riyas</value>  
   </param>  
   <param>  
    <key>description</key>  
    <value>Sniff rc remotes at 27mhz</value>  
   </param>  
   <param>  
    <key>window_size</key>  
    <value>1280, 1024</value>  
   </param>  
   <param>  
    <key>generate_options</key>  
    <value>wx_gui</value>  
   </param>  
   <param>  
    <key>category</key>  
    <value>Custom</value>  
   </param>  
   <param>  
    <key>run_options</key>  
    <value>prompt</value>  
   </param>  
   <param>  
    <key>run</key>  
    <value>True</value>  
   </param>  
   <param>  
    <key>max_nouts</key>  
    <value>0</value>  
   </param>  
   <param>  
    <key>realtime_scheduling</key>  
    <value></value>  
   </param>  
   <param>  
    <key>_coordinate</key>  
    <value>(10, 10)</value>  
   </param>  
   <param>  
    <key>_rotation</key>  
    <value>0</value>  
   </param>  
  </block>  
  <block>  
   <key>variable</key>  
   <param>  
    <key>id</key>  
    <value>samp_rate</value>  
   </param>  
   <param>  
    <key>_enabled</key>  
    <value>True</value>  
   </param>  
   <param>  
    <key>value</key>  
    <value>192000</value>  
   </param>  
   <param>  
    <key>_coordinate</key>  
    <value>(10, 170)</value>  
   </param>  
   <param>  
    <key>_rotation</key>  
    <value>0</value>  
   </param>  
  </block>  
  <block>  
   <key>analog_wfm_rcv</key>  
   <param>  
    <key>id</key>  
    <value>analog_wfm_rcv_0</value>  
   </param>  
   <param>  
    <key>_enabled</key>  
    <value>True</value>  
   </param>  
   <param>  
    <key>quad_rate</key>  
    <value>192000</value>  
   </param>  
   <param>  
    <key>audio_decimation</key>  
    <value>4</value>  
   </param>  
   <param>  
    <key>affinity</key>  
    <value></value>  
   </param>  
   <param>  
    <key>minoutbuf</key>  
    <value>0</value>  
   </param>  
   <param>  
    <key>_coordinate</key>  
    <value>(589, 92)</value>  
   </param>  
   <param>  
    <key>_rotation</key>  
    <value>0</value>  
   </param>  
  </block>  
  <block>  
   <key>audio_sink</key>  
   <param>  
    <key>id</key>  
    <value>audio_sink_0</value>  
   </param>  
   <param>  
    <key>_enabled</key>  
    <value>True</value>  
   </param>  
   <param>  
    <key>samp_rate</key>  
    <value>48000</value>  
   </param>  
   <param>  
    <key>device_name</key>  
    <value></value>  
   </param>  
   <param>  
    <key>ok_to_block</key>  
    <value>True</value>  
   </param>  
   <param>  
    <key>num_inputs</key>  
    <value>1</value>  
   </param>  
   <param>  
    <key>affinity</key>  
    <value></value>  
   </param>  
   <param>  
    <key>_coordinate</key>  
    <value>(674, 264)</value>  
   </param>  
   <param>  
    <key>_rotation</key>  
    <value>0</value>  
   </param>  
  </block>  
  <block>  
   <key>wxgui_fftsink2</key>  
   <param>  
    <key>id</key>  
    <value>wxgui_fftsink2_0</value>  
   </param>  
   <param>  
    <key>_enabled</key>  
    <value>True</value>  
   </param>  
   <param>  
    <key>type</key>  
    <value>complex</value>  
   </param>  
   <param>  
    <key>title</key>  
    <value>FFT Plot</value>  
   </param>  
   <param>  
    <key>samp_rate</key>  
    <value>samp_rate</value>  
   </param>  
   <param>  
    <key>baseband_freq</key>  
    <value>0</value>  
   </param>  
   <param>  
    <key>y_per_div</key>  
    <value>10</value>  
   </param>  
   <param>  
    <key>y_divs</key>  
    <value>10</value>  
   </param>  
   <param>  
    <key>ref_level</key>  
    <value>0</value>  
   </param>  
   <param>  
    <key>ref_scale</key>  
    <value>2.0</value>  
   </param>  
   <param>  
    <key>fft_size</key>  
    <value>1024</value>  
   </param>  
   <param>  
    <key>fft_rate</key>  
    <value>15</value>  
   </param>  
   <param>  
    <key>peak_hold</key>  
    <value>False</value>  
   </param>  
   <param>  
    <key>average</key>  
    <value>False</value>  
   </param>  
   <param>  
    <key>avg_alpha</key>  
    <value>0</value>  
   </param>  
   <param>  
    <key>win</key>  
    <value>None</value>  
   </param>  
   <param>  
    <key>win_size</key>  
    <value></value>  
   </param>  
   <param>  
    <key>grid_pos</key>  
    <value></value>  
   </param>  
   <param>  
    <key>notebook</key>  
    <value></value>  
   </param>  
   <param>  
    <key>freqvar</key>  
    <value>None</value>  
   </param>  
   <param>  
    <key>affinity</key>  
    <value></value>  
   </param>  
   <param>  
    <key>_coordinate</key>  
    <value>(477, 232)</value>  
   </param>  
   <param>  
    <key>_rotation</key>  
    <value>0</value>  
   </param>  
  </block>  
  <block>  
   <key>variable_slider</key>  
   <param>  
    <key>id</key>  
    <value>variable_slider_0</value>  
   </param>  
   <param>  
    <key>_enabled</key>  
    <value>True</value>  
   </param>  
   <param>  
    <key>label</key>  
    <value>Frequency</value>  
   </param>  
   <param>  
    <key>value</key>  
    <value>101100000</value>  
   </param>  
   <param>  
    <key>min</key>  
    <value>1</value>  
   </param>  
   <param>  
    <key>max</key>  
    <value>500000000</value>  
   </param>  
   <param>  
    <key>num_steps</key>  
    <value>1000</value>  
   </param>  
   <param>  
    <key>style</key>  
    <value>wx.SL_HORIZONTAL</value>  
   </param>  
   <param>  
    <key>converver</key>  
    <value>float_converter</value>  
   </param>  
   <param>  
    <key>grid_pos</key>  
    <value></value>  
   </param>  
   <param>  
    <key>notebook</key>  
    <value></value>  
   </param>  
   <param>  
    <key>_coordinate</key>  
    <value>(274, 230)</value>  
   </param>  
   <param>  
    <key>_rotation</key>  
    <value>0</value>  
   </param>  
  </block>  
  <block>  
   <key>fcdproplus_fcdproplus</key>  
   <param>  
    <key>id</key>  
    <value>fcdproplus_fcdproplus_0</value>  
   </param>  
   <param>  
    <key>_enabled</key>  
    <value>True</value>  
   </param>  
   <param>  
    <key>device_name</key>  
    <value></value>  
   </param>  
   <param>  
    <key>unit</key>  
    <value>1</value>  
   </param>  
   <param>  
    <key>lnaswitch</key>  
    <value>1</value>  
   </param>  
   <param>  
    <key>mixergainswitch</key>  
    <value>1</value>  
   </param>  
   <param>  
    <key>freq</key>  
    <value>variable_text_box_0</value>  
   </param>  
   <param>  
    <key>ppm</key>  
    <value>0</value>  
   </param>  
   <param>  
    <key>if_gain</key>  
    <value>0</value>  
   </param>  
   <param>  
    <key>affinity</key>  
    <value></value>  
   </param>  
   <param>  
    <key>minoutbuf</key>  
    <value>0</value>  
   </param>  
   <param>  
    <key>_coordinate</key>  
    <value>(252, 49)</value>  
   </param>  
   <param>  
    <key>_rotation</key>  
    <value>0</value>  
   </param>  
  </block>  
  <block>  
   <key>variable_text_box</key>  
   <param>  
    <key>id</key>  
    <value>variable_text_box_0</value>  
   </param>  
   <param>  
    <key>_enabled</key>  
    <value>True</value>  
   </param>  
   <param>  
    <key>label</key>  
    <value>frequency_input</value>  
   </param>  
   <param>  
    <key>value</key>  
    <value>variable_slider_0+variable_slider_1</value>  
   </param>  
   <param>  
    <key>converver</key>  
    <value>eval_converter</value>  
   </param>  
   <param>  
    <key>formatter</key>  
    <value>None</value>  
   </param>  
   <param>  
    <key>grid_pos</key>  
    <value></value>  
   </param>  
   <param>  
    <key>notebook</key>  
    <value></value>  
   </param>  
   <param>  
    <key>_coordinate</key>  
    <value>(116, 153)</value>  
   </param>  
   <param>  
    <key>_rotation</key>  
    <value>0</value>  
   </param>  
  </block>  
  <block>  
   <key>variable_slider</key>  
   <param>  
    <key>id</key>  
    <value>variable_slider_1</value>  
   </param>  
   <param>  
    <key>_enabled</key>  
    <value>True</value>  
   </param>  
   <param>  
    <key>label</key>  
    <value>freq_fine</value>  
   </param>  
   <param>  
    <key>value</key>  
    <value>50</value>  
   </param>  
   <param>  
    <key>min</key>  
    <value>0</value>  
   </param>  
   <param>  
    <key>max</key>  
    <value>10000000</value>  
   </param>  
   <param>  
    <key>num_steps</key>  
    <value>1000</value>  
   </param>  
   <param>  
    <key>style</key>  
    <value>wx.SL_HORIZONTAL</value>  
   </param>  
   <param>  
    <key>converver</key>  
    <value>float_converter</value>  
   </param>  
   <param>  
    <key>grid_pos</key>  
    <value></value>  
   </param>  
   <param>  
    <key>notebook</key>  
    <value></value>  
   </param>  
   <param>  
    <key>_coordinate</key>  
    <value>(135, 243)</value>  
   </param>  
   <param>  
    <key>_rotation</key>  
    <value>0</value>  
   </param>  
  </block>  
  <connection>  
   <source_block_id>fcdproplus_fcdproplus_0</source_block_id>  
   <sink_block_id>analog_wfm_rcv_0</sink_block_id>  
   <source_key>0</source_key>  
   <sink_key>0</sink_key>  
  </connection>  
  <connection>  
   <source_block_id>analog_wfm_rcv_0</source_block_id>  
   <sink_block_id>audio_sink_0</sink_block_id>  
   <source_key>0</source_key>  
   <sink_key>0</sink_key>  
  </connection>  
  <connection>  
   <source_block_id>fcdproplus_fcdproplus_0</source_block_id>  
   <sink_block_id>wxgui_fftsink2_0</sink_block_id>  
   <source_key>0</source_key>  
   <sink_key>0</sink_key>  
  </connection>  
 </flow_graph>  
end

Sunday, June 1, 2014

Cheap remote wireless temperature sensor with arduino and 433mhz rf module and DS18B20 or LM35

A simple wireless temperature sensor for homemade weather station

In this article, we use inexpensive 433/315MHZ inexpensive rf modules to make wireless temperature sensors. The same principle can be used to make any of your sensors wireless.

Compared to nrf24l01 modules, these are unidirectional and needs slightly higher (starting at 5v) working voltages. But for simple uses, these are quite handy as they consume only one digital pin on the Arduino while the nrf modules use 5 pins.

For temperature measurements, I used a ds18b20 digital sensor in this article. But it can be easily modified to use a lm35 sensor. To make the receiver compact and independent of a computer, I used an LCD module which is available as a keypad shield. But any LCD module with the standard LCD library will do the job.

The rf modules usually have 3 pins, two for power supply and one for data / Arduino. It is important to connect a small (17cm wire with 433MHz) to the antenna pins on the transmit module (see figure). In my experience, these links used to work pretty well from my terrace to the living room (around 10 meters and through the thick walls). The module even worked well after keeping inside the refrigerator for about 4-5 meters!

433mhz transmit module connections
433 mhz receiver module pin connection













In addition to the sketch, it uses two libraries. A one wire protocol for reading the temperature from ds18b20 sensors and a virtual wire library for sending the temperature reading over the wireless link.

For testing, I kept the remote module outside the room to get the outdoor temperature wirelessly without the trouble of connecting a long wire to the terrace.

Temperature can further be logged on to web based portals (for e.g. exocite) and can be accessed online
wireless temperature sensor with online logging


Download Libraries

Virtualwire
Onewire

Download and keep the unzipped files in the Arduino libraries folder and restart the Arduino ide.

Wireless temperature receiver using an LCD module 

 // wireless temperature receiver using a simple 43mhz module and an lcs/keypad shield  
 // Arduino pin 15 (Analogue 1) is connected to a ds18220 for indoor temperature  
 // Arduino pin 16 (Analogue 2) is connected to the 433mhz receiver for outdoor temperature 
 // Arduino pin 17 (Analogue 3) is connected to an led via 330 ohm resistor  
 // credits to arduino.cc and owners of each libraries and online communities  
 // more details http://blog.riyas.org/2014/06/cheap-remote-wireless-temperature-sensor-with-arduino-uno-433mhz-rfmodule.html
 #include <LiquidCrystal.h>  
 #include <OneWire.h>   
 #include <VirtualWire.h>  
 // select the pins used on the LCD panel  
 LiquidCrystal lcd(8, 9, 4, 5, 6, 7);  
 int DS18S20_Pin = 15; //DS18S20 pin  
 //Temperature chip i/o  
 OneWire ds(DS18S20_Pin); // on pin 15  
 void setup()  
 {  
  lcd.begin(16, 2);       // start the library  
  lcd.setCursor(0,0);  
  lcd.print("Outdoor: -----"); // print a simple message  
  lcd.setCursor(0,1);  
  lcd.print("Indoor:"); // print a simple message  
  Serial.begin(9600);  
  vw_set_ptt_inverted(true); // Required for DR3100  
  vw_set_rx_pin(16);  
  vw_setup(4000); // Bits per sec  
  vw_rx_start(); 
  pinMode(17,OUTPUT);  // for an led on pin 17 (Analogue 3)to blink with rf link
 
 }  
 void loop()  
 {  
  uint8_t buf[VW_MAX_MESSAGE_LEN];  
  uint8_t buflen = VW_MAX_MESSAGE_LEN;    
  if (vw_get_message(buf, &buflen)) // Non-blocking  
   {   
    lcd.setCursor(8,0);     
    char temp=0;//mod:tim:added a temporary character    
    for (int i = 0; i < buflen; i++)    
    {   
     temp=(char)buf[i];//mod:tim:convert uint to char  
     Serial.print(temp); //mod:tim:changed buff[i] to temp here  
     lcd.print(temp);   
    }  
     Serial.println("");      
    if(buf[0]=='1'){   
    digitalWrite(17,1);  //blink with active rf link
    }   
    if(buf[0]=='0'){  
    digitalWrite(17,0);  
    }  
   }  
 float temperature = getTemp();  
 Serial.println(temperature);    
 lcd.setCursor(9,1);      // move cursor to second line "1" and 9 spaces over  
 lcd.print(temperature);   // display temperature
 lcd.setCursor(0,1);      // move to the begining of the second line   
 }  
 float getTemp()  
 {  
  //returns the temperature from one DS18S20 in DEG Celsius  
  byte data[12];  
  byte addr[8];  
  if ( !ds.search(addr)) {  
    //no more sensors on chain, reset search  
    ds.reset_search();  
    return -1000;  
  }  
  if ( OneWire::crc8( addr, 7) != addr[7]) {  
    Serial.println("CRC is not valid!");  
    return -1000;  
  }  
  if ( addr[0] != 0x10 && addr[0] != 0x28) {  
    Serial.print("Device is not recognized");  
    return -1000;  
  }  
  ds.reset();  
  ds.select(addr);  
  ds.write(0x44,1); // start conversion, with parasite power on at the end  
  byte present = ds.reset();  
  ds.select(addr);    
  ds.write(0xBE); // Read Scratchpad  
  for (int i = 0; i < 9; i++) { // we need 9 bytes  
   data[i] = ds.read();  
  }  
  ds.reset_search();  
  byte MSB = data[1];  
  byte LSB = data[0];  
  float tempRead = ((MSB << 8) | LSB); //using two's compliment  
  float TemperatureSum = tempRead / 16;  
  return TemperatureSum;  
 }  

Wireless temperature transmitter

 //simple wireless temperature tranmitter   
 // DS18S20 sensor is connected to pin 8  
 // Rf modules tranmit (data) pin is connected to pin 7 on the arduino 
 // More info: http://blog.riyas.org/2014/06/cheap-remote-wireless-temperature-sensor-with-arduino-uno-433mhz-rfmodule.html 
 #include <VirtualWire.h>  
 #include <OneWire.h>   
 int DS18S20_Pin = 8; //DS18S20 Signal pin on digital 2  
 OneWire ds(DS18S20_Pin); // on digital pin 2  
 char *controller;  
 char msg[6];  
 void setup()   
   {  
      Serial.begin(9600);  
      pinMode(13,OUTPUT);  
      vw_set_ptt_inverted(true); //  
      vw_set_tx_pin(7);  
      vw_setup(4000);// speed of data transfer Kbps  
   }  
 void loop()  
   {   
      float temperature = getTemp();  
      Serial.println(temperature);  
      dtostrf(temperature, 6, 2, msg);  
      digitalWrite(13,0);  
      vw_send((uint8_t *)msg, strlen(msg)); // Send temperature.  
      vw_wait_tx();  
      delay(500);  
      digitalWrite(13,1); // blink the led on pin13  
      delay(500);  
   }  
 float getTemp()  
 {  
  //returns the temperature from one DS18S20 in DEG Celsius  
  byte data[12];  
  byte addr[8];  
  if ( !ds.search(addr)) {  
    //no more sensors on chain, reset search  
    ds.reset_search();  
    return -1000;  
  }  
  if ( OneWire::crc8( addr, 7) != addr[7]) {  
    Serial.println("CRC is not valid!");  
    return -1000;  
  }  
  if ( addr[0] != 0x10 && addr[0] != 0x28) {  
    Serial.print("Device is not recognized");  
    return -1000;  
  }  
  ds.reset();  
  ds.select(addr);  
  ds.write(0x44,1); // start conversion, with parasite power on at the end  
  byte present = ds.reset();  
  ds.select(addr);    
  ds.write(0xBE); // Read Scratchpad  
  for (int i = 0; i < 9; i++) { // we need 9 bytes  
   data[i] = ds.read();  
  }  
  ds.reset_search();  
  byte MSB = data[1];  
  byte LSB = data[0];  
  float tempRead = ((MSB << 8) | LSB); //using two's compliment  
  float TemperatureSum = tempRead / 16;  
  return TemperatureSum;  
 }  

en