< async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"">
Showing posts with label remote control. Show all posts
Showing posts with label remote control. Show all posts

Sunday, July 5, 2015

A simple RF remote with code hopping or rolling code on Arduino and AVR using cheap 433mhz rf modules

Secure door lock with arduino and rolling code/code hopping with AES
 In many of my weekend projects, i used some cheap 433 mhz RF modules or nrf23l01 modules for wirelessly controlling lights, temperature sensors, garage door openers e.t.c. But i never thought about the security for these protocols. It is pretty easy to sniff the rf signals with an sdr (software defined radio) and replay them to control most of these simple devices and tweak in to those simple protocols. In this project, i am trying to introduce some simple securing to those rf channels. It uses the avr crypto library and the AES 128 (advansed encryption system) to perform the encryption of the data transfer between the arduinos. The principle is very simple. It encrypt all the packets send between the arduinos using a secret key (128 bit in this example). So it appears meaning less to a third person who is sniffing the traffic. One of the challenge is the replay attack were the person can record and re-transmit the data. To avoid this there is some data in the transmission which changes with every instances of they communication (Rolling code or code hopping).  This is similar to one time codes used in for e.g banking, remote car keys e.t.c. Still there are some weaknesses in techniques which do not use a clock ( i excluded a real time clock to make it simple and for battery saving, time syncing e.t.c ). But it is very easy to add this feature in to the code. Other possibility is to use the google authentication with HMAC or TOTP (but not now ).

Rolling codes on arduino with a unique pattern send with every key press


There is a pairing option to sync the controller and the remote. Once synced, the remote receiver will respond to the new commands and discards any used messages. In addition there is a safety window to avoid the syncing issues from accidental key presses. (Read keeloq from microchip for simple alternatives)

Wiring and Construction


Wiring is pretty simple. For the transmit: 

Connect the rf modules as usual (vcc to 5volt, gnd to -ve  and the DATA (ATAD) to digital pin 7. Connect the pin 8 to 3.3volt pin (removing this connection starts the pairing mode)

RF modules and tx /rx sections


Receiver is in the same way. Connect the data pin to A0 of an other arduino and the vcc / Gnd as described above for the transmit module. I used a keypad shield with lcd for showing the messages. But you can remove the lcd and comment the codes (use the serial monitor for debugging). Press any of the button on keypad shield to pair and sync with the remote

Source code for the transmitter


  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
// Secure tx with code hopping using AES
// garage remote etc
// 128bit key
// credits to avr crypto library for aes
// More details at http://riyas.org
#define DEBUG_ENABLED
#include <avr/eeprom.h>
#include <VirtualWire.h>
#include <AESLib.h>
#pragma pack 1
typedef struct {
    unsigned long serial;
    long counter;
    char command;
    long extra;
    int code;
    char termin;
} PackedData;
typedef struct {
    long counter;
    unsigned long serial;
    int magic;
} PairingData;
typedef struct {
    long counter;
    unsigned long serial;
    uint8_t skey[16];
} Settings;
#define SERIAL_NUMBER 123456
#define PMAGIC 555
#define STATUS_LED 13
#define PAIR_BUTTON 8
#define TX_PIN 7
#define SEND_BUTTON 9
uint8_t key[] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};  //specific key for the fob
PackedData sData;
PairingData pData;
unsigned char msg[16];
long key_counter;
void pair(void) {
    long start =millis();
    int ledState = LOW;
    long previousMillis = 0; // will store last time LED was updated
    do{
        //sent the serial number and counter for syncing
        int len=sizeof(pData);
        vw_send((uint8_t *)&pData, len); // Send via rf
        vw_wait_tx();
        delay(1000);
        unsigned long currentMillis = millis();
        if(currentMillis - previousMillis > 100) {
            previousMillis = currentMillis;
            if (ledState == LOW)
            ledState = HIGH;
            else
            ledState = LOW;
            digitalWrite(STATUS_LED, ledState);
        }
    } while ((millis()-start)<10000); //for 5 seconds
}
void setup() {
    #ifdef DEBUG_ENABLED
    Serial.begin(9600);
    #endif
 pinMode(PAIR_BUTTON,INPUT_PULLUP); //switch for pairing
 pinMode(SEND_BUTTON,INPUT_PULLUP); //switch for sending
    pinMode(STATUS_LED,OUTPUT);
    pinMode(PAIR_BUTTON,INPUT); //switch for pairing
    vw_set_ptt_inverted(true); //
    vw_set_tx_pin(TX_PIN);
    vw_setup(4000);// speed of data transfer Kbps
 //read the stored counter & eepromdata
 eeprom_read_block((void*)&pData, (void*)0, sizeof(pData));
 //if serial number is not set
 if(pData.serial!=SERIAL_NUMBER)
  {
   //in case eeprom is empty/garbage as in first try
   pData.counter=1000;//random(10000,99999);
   pData.magic=PMAGIC;
   pData.serial=SERIAL_NUMBER;
  }
    sData.code=PMAGIC;
    sData.serial=SERIAL_NUMBER;
}
void loop(){
    if (digitalRead(PAIR_BUTTON)==0)
    {
        //pairing loop
        pair();
    }
 
 if (digitalRead(SEND_BUTTON)==0)
    {
        //send the command 
        sender();
    }
 
    delay(1000);
}

void sender(){
    //increment the counter
    pData.counter=pData.counter+1;
    sData.counter=pData.counter;
    //save the counter
    eeprom_write_block((const void*)&pData, (void*)0, sizeof(pData));     
    // Set the commands based on key
    // for testing i kept it 'a' but do a loop with analogue/digital read to get key state    
    sData.command='a';
    digitalWrite(STATUS_LED,1);
    int len = sizeof(sData);
    aes128_enc_single(key, &sData);
    #ifdef DEBUG_ENABLED
    Serial.print("Encrypted:");
    memcpy(msg, &sData, len);
    for(int k=0;k<16;k++)
    Serial.print(msg[k],HEX);
    Serial.println("");
    Serial.print("KEY:");
    for(int k=0;k<len;k++)
    Serial.print(key[k],HEX);
    Serial.println("");
    #endif
    vw_send((uint8_t *)&sData, len); // Send the 16 bytes
    vw_wait_tx();
    aes128_dec_single(key, &sData);
    PackedData * sdData = (PackedData *)&sData;
    #ifdef DEBUG_ENABLED
    Serial.print("Test Decrypt:");
    Serial.print(sdData->serial);
    Serial.println("");
    #endif
    digitalWrite(STATUS_LED,0); 
}

//END

Source code for the receiver


  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
#include <avr/eeprom.h>
#include <LiquidCrystal.h>
#include <VirtualWire.h>
#include <AESLib.h>
const int SAFEWINDOW = 50;
long lastcount;
char *controller;
unsigned char msg[16];
uint8_t key[] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
#pragma pack 1
//uint8_t key[16];
typedef struct {
    unsigned long serial;
    long counter;
    char command;
    long extra;
    int code;
    char termin;
} PackedData;
typedef struct {
    long counter;
    unsigned long serial;
    uint8_t skey[16];
} Settings;
typedef struct {
    long counter;
    unsigned long serial;
    int magic;
} PairingData;
// select the pins used on the LCD panel
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);
Settings StoredSettings;
int errorcounter=0;
int adc_key_in;
// read the buttons
int read_LCD_buttons()
{
    adc_key_in = analogRead(0); // read the value from the sensor
    if (adc_key_in < 50) return 1;// btnRIGHT;
    if (adc_key_in < 250) return 2;// btnUP;
    if (adc_key_in < 450) return 3;//btnDOWN;
    if (adc_key_in < 650) return 4;//btnLEFT;
    if (adc_key_in < 850) return 5;//btnSELECT;
    return 0;//btnNONE; // when all others fail, return this...
}
void setup()
{
    Serial.begin(9600);
    lcd.begin(16, 2); // start the library
    //memcpy (&StoredSettings.skey, &key[0], sizeof(key));
    //eeprom_write_block((const void*)&StoredSettings, (void*)0, sizeof(StoredSettings));
    delay(1000);               
    eeprom_read_block((void*)&StoredSettings, (void*)0, sizeof(StoredSettings));
    //memcpy (&key[0],&StoredSettings.skey, sizeof(key));
    Serial.print("EEPROMKEY:");
    for(int k=0;k<16;k++)
    Serial.print(StoredSettings.skey[k],HEX);
    Serial.println("");
    lcd.setCursor(0,1);
    lcd.print(StoredSettings.counter); // print a simple message
    lcd.setCursor(0,0);
    lcd.print("SN: "); // print a simple message
    lcd.setCursor(4,0);
    lcd.print(StoredSettings.serial);
    pinMode(2, OUTPUT);
    vw_set_ptt_inverted(true); // Required for DR3100
    vw_set_rx_pin(A5);
    vw_setup(4000); // Bits per sec
    vw_rx_start();
}
void pairing(void)
{
    uint8_t buf[VW_MAX_MESSAGE_LEN];
    uint8_t buflen = VW_MAX_MESSAGE_LEN;
    lcd.setCursor(0,0);
    lcd.print(" Pairing! "); 
    long start =millis();
    int ledState = LOW;
    long previousMillis = 0; // will store last time LED was updated
    do{
        //do pairing process and store serial and counter
        if (vw_get_message(buf, &buflen)) // Non-blocking
        {
        lcd.clear();
      
        PairingData * pairData = (PairingData *)buf; 
           
            if(pairData->magic==555){
                lcd.clear();
                lcd.setCursor(0,0);
                lcd.print(pairData->serial);
                lcd.setCursor(0,1);
                lcd.print(pairData->counter);
                StoredSettings.counter=pairData->counter;
                StoredSettings.serial=pairData->serial;
                eeprom_write_block((const void*)&StoredSettings, (void*)0, sizeof(StoredSettings));
                delay(2000);
                lcd.clear();              
                lcd.setCursor(0,0);
                lcd.print(" Done!");
                lcd.setCursor(0,1);
                lcd.print(" Synced");
                delay(2000);
                break;
            }
            else
            {
                lcd.clear();            
                lcd.setCursor(0,0);
                lcd.print(" Error!");
                lcd.setCursor(0,1);
                lcd.print("Try again!");
            }
        }
        delay(100);
        //end of pairing
        unsigned long currentMillis = millis();
        if(currentMillis - previousMillis > 100) {
            previousMillis = currentMillis;
            if (ledState == LOW)
            ledState = HIGH;
            else
            ledState = LOW;
            digitalWrite(2, ledState);
        }
    } while ((millis()-start)<10000); //for 5 seconds
}
void loop()
{


   if (read_LCD_buttons()!=0)
    {
   pairing();
    }
    uint8_t buf[VW_MAX_MESSAGE_LEN];
    uint8_t buflen = VW_MAX_MESSAGE_LEN;
    if (vw_get_message(buf, &buflen)) // Non-blocking
    {
        Serial.print("Encrypted:");
        memcpy(msg, buf, buflen);
        for(int k=0;k<16;k++)
        Serial.print(msg[k],HEX);
        Serial.println("");
        Serial.print("KEY:");
        for(int k=0;k<buflen;k++)
        Serial.print(key[k],HEX);
        Serial.println("");
        aes128_dec_single(key, msg);
        PackedData * sdData = (PackedData *)msg;
        Serial.print("Test Decrypt:");
        Serial.print(sdData->serial);
        Serial.println("");
        
        lcd.clear();
        lcd.setCursor(0,0);
        lcd.print(sdData->serial);
        lcd.setCursor(0,1);
        lcd.print(sdData->counter);
        
        digitalWrite(2,1); //for red led
        long currentcounter;
        if(sdData->code==555){
            lcd.clear();
            lcd.setCursor(0,0);
            lcd.print(sdData->serial);
            lcd.setCursor(0,1);
            lcd.print(sdData->counter);
            //do the job if the counter is with in safety window
            currentcounter= sdData->counter;
            if((currentcounter-StoredSettings.counter)<SAFEWINDOW && (currentcounter-StoredSettings.counter)>0)
            {
                lcd.setCursor(0,0);
                lcd.print("Access Granted!");
                StoredSettings.counter=sdData->counter;
                eeprom_write_block((const void*)&StoredSettings, (void*)0, sizeof(StoredSettings));
                //do the stuff here for eg running the servo or door lock motor
                char command = sdData->command;
                switch (command) {
                        case 'a':
                              lcd.clear();
                              lcd.setCursor(0,0);
                              lcd.print("OPENING DOOR"); 
                              //do the servo here                              
                              break;
                        case 'b':
                              lcd.clear();
                              lcd.setCursor(0,0);
                              lcd.print("CLOSING DOOR"); 
                              //do the servo here     
                              break;
                        case 'c':
                              lcd.clear();
                              lcd.setCursor(0,0);
                              lcd.print("NEXT THING"); 
                              //do the servo here     
                              break;                }
                
            }
          else
            {
                errorcounter++;
                lcd.clear();
                lcd.setCursor(0,0);
                lcd.print("Needs Pairing!");
                lcd.setCursor(8,1);
                lcd.print(errorcounter);
                lcd.setCursor(12,1);
                lcd.print(StoredSettings.counter);
                lcd.setCursor(0,1);
                lcd.print(currentcounter);               
            } 
        }
        else
        {
            lcd.clear();
            lcd.setCursor(0,0);
            lcd.setCursor(0,0);
            lcd.print(" Error!");
            lcd.setCursor(0,1);
            lcd.print(" Wrong Key??");
        }
        delay(100);
        digitalWrite(2,0);
    }
    delay(100); //just here to slow down a bit

}
END of post

Saturday, June 27, 2015

A simple UHF beacon for fox hunt or lost rc model using arduino and 433MHZ rf module

A simple 433mhz rf module and arduino pro mini as a morse code beacon
In this project a simple 433mhz rf module is used as a uhf beacon for testing purposes. This can be useful in fox hunting (locating hidden transmitter) or for finding lost RC models. An arduino pro mini is used for generating the morse code message, but any avr micro controller can be used.

A simple rf module (with three pins, VCC.GND and ATAD(DATA) is used. It generate an rf signal at 433.9mhz when the ATAD pin is high (so the same pin is connected to pin 13 of the arduino board). A simple firmware will control the pin 13 and hence can send a stored message at frequent intervals.

Wiring is very simple. Connect gnd pin to ground or negative, vcc to 5volt (or higher up to 12v, for better range), and the atad pin to pin 13 of the arduino.

This can be made even simple with an attiny85. Adding a solar charger is another option. If more power is needed, an extra amplifier (uhf ) with a low pass filter can be added. For the antenna i used a piece of wire (17.3cm) but alternatives like a slim jim can be used for better range.

Here is the source code (change the text string to match your need). This will send a morse code message of the text stored. 


  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
// A simple uhf beacon using 433 mhz rf module
// credits to the author of the morse code generator
// more details on http://blog.riyas.org

struct t_mtab { char c, pat; } ;

struct t_mtab morsetab[] = {
 {'+', 42},   
 {'-', 97},
 {'=', 49},
 {'.', 106},
 {',', 115},
 {'?', 76},
 {'/', 41},
 {'A', 6},
 {'B', 17},
 {'C', 21},
 {'D', 9},
 {'E', 2},
 {'F', 20},
 {'G', 11},
 {'H', 16},
 {'I', 4},
 {'J', 30},
 {'K', 13},
 {'L', 18},
 {'M', 7},
 {'N', 5},
 {'O', 15},
 {'P', 22},
 {'Q', 27},
 {'R', 10},
 {'S', 8},
 {'T', 3},
 {'U', 12},
 {'V', 24},
 {'W', 14},
 {'X', 25},
 {'Y', 29},
 {'Z', 19},
 {'1', 62},
 {'2', 60},
 {'3', 56},
 {'4', 48},
 {'5', 32},
 {'6', 33},
 {'7', 35},
 {'8', 39},
 {'9', 47},
 {'0', 63}
} ;

#define N_MORSE  (sizeof(morsetab)/sizeof(morsetab[0]))
#define SPEED  (13) 
#define txpin    (13)
#define DOTLEN  (1200/SPEED)
#define DASHLEN  (3*(1200/SPEED))


void
dash()
{
  digitalWrite(txpin, HIGH);
  delay(DASHLEN);
  digitalWrite(txpin, LOW);
  delay(DOTLEN);
}

void
dit()
{
  digitalWrite(txpin, HIGH);
  delay(DOTLEN);
  digitalWrite(txpin, LOW);
  delay(DOTLEN);
}


void
send(char c)
{
  int i ;
  if (c == ' ') {
    delay(7*DOTLEN) ;
    return ;
  }
  for (i=0; i<N_MORSE; i++) {
    if (morsetab[i].c == c) {
      unsigned char p = morsetab[i].pat ;

      while (p != 1) {
          if (p & 1)
            dash() ;
          else
            dit() ;
          p = p / 2 ;
      }
      delay(2*DOTLEN) ;
 }
  Serial.print("?") ;
}
}


void
sendmsg(char *str)
{
  while (*str)
    send(*str++) ;
}


void setup() {                
  pinMode(txpin, OUTPUT);     
}

// the loop 
void loop() {
 sendmsg("VVV VVV VVV TESTING TESTING ") ; 
 delay(1500) ;  //delay can be increased to save battery
}

Saturday, November 1, 2014

Shrinking microcontroller projects and arduinos with Attiny85 or digispark or digistump with V-USB [quick start digistump tutorial]

Small form factor attiny85 with Virtual USB (digistump) -Shrinking arduinos
AVR microcontrollers and arduino ide's are very popular among electronics hobbyist for their ease of usage and faster learning curves. It is much easier to learn programming microcontrollers with an arduino ide and typical prototyping boards like arduino uno or mega. But it is much nicer to move on to simple and cheaper solutions which provides the same power and lower cost. This can be achieved by using attiny85 or atmega328 along with a bunch of components.


Recently i came across a nice product from digispark (interestingly the schematics etc are available to the public) It is based on an attiny85 which is sufficient for doing many small tasks which fits in a 6k footprint, for e.g a simple temperature logger. More interestingly it uses V-USB , a virtual, slow usb using software on avr micros (also on PIC as well). More on V-USB can be read here  The unique micronucleus bootloader allows us to program it directly from the USB port and also allow us to use all most all the available pins on the attiny85.  Interestingly all these can be built from scratch (see e.g at this instructable). See below for a schematic (Very minimal!)

Digistump, tiny Arduino Schematic

Quick start guide for using and Programming a digispark/ digistump


The arduino ide is similar but is slightly different in its upload process (hex file to micro controller).
Basically you have to download the interface from here. For linux users it is a bit tricky as you need a udev rule (else you wont be able to program/ upload sketches)

AFter setting up the ide, it is straight forward to upload the sketches to attiny85 on digistump. Only difference here is , you need to connect the device to usb port after you have pressed the sketch upload button!

I started testing it with the blink sketch (it is slightly different in terms of digital pins, so choose the example sketchs specific to digispark from the ide interface). Further more it is also possible to compile the hex files directly using a make file and upload it direcly using the commandline version of the micronucleus application. More on it on a next post. This could be good way to learn more on programming avrs directly by using avr gcc as it can save some program space and goes fine with the memory constrains. 

I am planning to make a simple password manager with an attiny85. This could bea simple version of mooltipass. There are similar projects which can be seen here

Saturday, September 20, 2014

A simple contact less human computer interface with ultrasonic transsducers and arduino uno or atmega328 [part 1]

Arduino ultrasonic computer interface- as there are
 only two sensors the active area is small
In this simple project, i used a pair of ultrasonic sensors (HC-SR04 modules) with arduino uno to check weather they can be used to track the x and y cordinates in a two dimensional plane. This is a beginner stage of a project where the number of transducers can be extended to produce a 3d mouse based on sonic sensors. This can be used to control simple things without really touching the computer so that it can be used in kitchen , labs etc when hands are dirty. Similar things can be achieved with infrared sensor and better results with a pair of video camera (but needs a better hardware than atmega328) The setup is very simple and is shown in the figure below. Two HC-SR04 sensors are hooked up in to arduino uno. Connection is straight forward (all pins are marked on the sensor silk screen). Connect the grounds to the ground pin of the uno/atmega328 and the positive (vcc) pins to 5v pins on the arduino. It uses the NewPing library which can be downloaded here. A simple explanation on connecting and using the library can be read at arduino playground

The whole set up in action can be seen in the video below. Basically the limitation in the active pointer area is small due to the limited number of sensors used. But this can be used as a base and adding more sensors can improve the results. Currently i used it to plot on a gui based on python (matplotlib) and can be extended as a real mouse by adding a few more lines of code and the V-USB on arduino uno or using a leonardo/ newer version of micro-controllers which can act as a usb HID.




The arduino sketch is very simple and it basically sends x and y cordinates via serial to the computer and on the PC , just plot the x,y values on a 2d plane

Code

sonic_mouse.ino

// a simple sonic mouse, code adapted from New ping example
// Connect PIN 13 to Trigger of first sensor and 12 to the Echo pin
// Connect PIN 11 to Trigger of second sensor and 10 to the Echo pin
// If you use multiple sensor for x / y axis, use the max value from the read array along the axis
// Read more about it at http://blog.riyas.org

#include <NewPing.h>

#define SONAR_NUM     2 // Number or sensors.
#define MAX_DISTANCE 50 // Maximum distance (in cm) to ping.
#define PING_INTERVAL 33 // Milliseconds between sensor pings (29ms is about the min to avoid cross-sensor echo).

unsigned long pingTimer[SONAR_NUM]; // Holds the times when the next ping should happen for each sensor.
unsigned int cm[SONAR_NUM];         // Where the ping distances are stored.
uint8_t currentSensor = 0;          // Keeps track of which sensor is active.

NewPing sonar[SONAR_NUM] = {     // Sensor object array.
  NewPing(13, 12, MAX_DISTANCE), // Each sensor's trigger pin, echo pin, and max distance to ping.
  NewPing(11, 10, MAX_DISTANCE),
};

void setup() {
  Serial.begin(115200);
  pingTimer[0] = millis() + 75;           // First ping starts at 75ms, gives time for the Arduino to chill before starting.
  for (uint8_t i = 1; i < SONAR_NUM; i++) // Set the starting time for each sensor.
    pingTimer[i] = pingTimer[i - 1] + PING_INTERVAL;
}

void loop() {
  for (uint8_t i = 0; i < SONAR_NUM; i++) { 
    if (millis() >= pingTimer[i]) {         
      pingTimer[i] += PING_INTERVAL * SONAR_NUM;  
      if (i == 0 && currentSensor == SONAR_NUM - 1) SensorReadLoop(); 
      sonar[currentSensor].timer_stop();         
      currentSensor = i;                          
      cm[currentSensor] = 0;                      
      sonar[currentSensor].ping_timer(echoCheck); 
    }
  }
  // eXTRA  CONTROL CAN BE DONE HERE
}

void echoCheck() { 
  if (sonar[currentSensor].check_timer())
    cm[currentSensor] = sonar[currentSensor].ping_result;
}

void SensorReadLoop() { // ADDITIONALLY HERE you CAN ADD THE v-usb
  for (uint8_t i = 0; i < SONAR_NUM; i++) {
    Serial.print(cm[i]);
    Serial.print(" ");
  }
  Serial.print("\n");
}


Plotting the pointer data from arduino on the computer using python and matplotlib

Copy the following code in to a new file plot_mouse.py and execute it by typing python plot_mouse.py in a terminal. On windows, you need to modify it (first make sure to get the python serial and matplotlib working )

################################################################################
# plot_mouse.py
# Plot the data from sonic_mouse.ino on a pc
# It uses matplotlib
# install matplotlib, pyserial and numpy to get it working
# code adapted from https://gist.github.com/electronut/5641938
# more details on the project at http://blog.riyas.org
################################################################################

import sys, serial
import numpy as np
from time import sleep
from collections import deque
from matplotlib import pyplot as plt

# store the pointer data
class PointerData:
  def __init__(self, maxLen):
    self.ax = deque([0.0]*maxLen)
    self.ay = deque([0.0]*maxLen)
    self.maxLen = maxLen

  def addToBuf(self, buf, val):
    if len(buf) < self.maxLen:
      buf.append(val)
    else:
      buf.pop()
      buf.appendleft(val)

  def add(self, data):
    assert(len(data) == 2)
    self.addToBuf(self.ax, data[0])
    self.addToBuf(self.ay, data[1])  

class PlotData:
  def __init__(self, PointerData):
    plt.ion() 
    self.axline, = plt.plot(PointerData.ax,PointerData.ay,'r',linewidth=10)
    mng = plt.get_current_fig_manager()
    mng.resize(*mng.window.maxsize())    
    plt.ylim([0, 2000])
    plt.xlim([0, 2000])
  def update(self, PointerData):
    self.axline.set_xdata(PointerData.ax)
    self.axline.set_ydata(PointerData.ay)
    plt.draw()

# main() function
def main():
  if(len(sys.argv) != 2):
    print 'Example usage: python plot_mouse.py "/dev/ttyACM0"'
    exit(1)

  strPort = sys.argv[1];
  PointerData = PointerData(4)
  PlotData = PlotData(PointerData)
  # open serial port
  ser = serial.Serial(strPort, 115200)
  while True:
    try:
      line = ser.readline()
      #print line
      data = [float(val) for val in line.split()]
      if(len(data) == 2):
        PointerData.add(data)
        PlotData.update(PointerData)
    except KeyboardInterrupt:
      print 'exiting'
      break
  ser.flush()
  ser.close()
# call main
if __name__ == '__main__':
  main()

end

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, March 2, 2014

Sending infrared commands over Ethernet with arduino -Extending ir remotes over ip to attic

Infrared extender over Ethernet with arduino

Here is a small project using arduino and ethernet shield to send infrared remote commands over the ethernet (IR over IP) to a remotely placed infrared equipment, for e.g on the attic or upstairs of the building. In addition there is a possibility to access it over wifi/smart phone to send custom commands.

This is an extension of my previous post on IR blasters with raspberry pi and many general points like increasing the power output with a transistor is applicable here as well. So basically this circuit picks up infrared signals from any remote controls and send these raw data to the remote end and the arduino over there will emit it to control the target device. To make it simple and universally applicable, i used raw infrared patters and is relayed. The library involved can decode different popular formats which can add extra feature. Furthermore in this design, the target transmitter can be accessed with a browser and by passing ir codes as a url parameter, any infrared device can be controlled from a simple web browser as well. So there is a lot of possibilities to play around.

To get it running, you need two arduinos and ethernet shields. The first one acts as a server which is placed near the device to be controlled. The output is taken from pin 3 of the arduino and is connected to the IR led ( or via a transistor as shown here)

The source code for the ir extender is available here

Load this sketch to the server arduino

/Sending ir codes over ethernet
//This sketch accept IR from an IR sensor connected to Pin 6 and sends over IP
// parts of the code for storing ir in to buffer is from  Ken Shirriff's ir recorder http://arcfn.com
//
//                       THIS ONE PICKS IR FROM A REMOTE
//
#include <IRremote.h>
#include <SPI.h>
#include <Ethernet.h>
 

int RECV_PIN = 6;
IRrecv irrecv(RECV_PIN);
decode_results results;
 
// Network settings 
byte mac[] = {  0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; //MAC
byte gateway[] = { 192,168,0,1 };// Gateway
byte ip[] = { 192,168,0,123 };  //ip if dhcp fails 
 
// IP of the Target arduino with ir emitter
IPAddress server(192,168,0,102);  
EthernetClient client;

unsigned int rawCodes[RAWBUF]; // The durations if raw
int codeLen; // The length of the code
//Get the infrared codes from remote

void storeCode(decode_results *results) {
  int count = results->rawlen;
  Serial.println("Received code");
  codeLen = results->rawlen - 1;
    for (int i = 1; i <= codeLen; i++) {
      if (i % 2) {
        // Mark
        rawCodes[i - 1] = results->rawbuf[i]*USECPERTICK - MARK_EXCESS;
        Serial.print(" m");
      } 
      else {
        // Space
        rawCodes[i - 1] = results->rawbuf[i]*USECPERTICK + MARK_EXCESS;
        Serial.print(" s");
      }
      Serial.print(rawCodes[i - 1], DEC);
    }
    Serial.println("");
}

 
void setup()
{
  // For debug
  Serial.begin(9600);
  // Start ethernet
  Ethernet.begin(mac, ip, gateway);
  Serial.println("Initialised");
}

//sending the raw IR code over the ether net

void sendIR()
{
   
    // Send over HTTP
    if (client.connect(server, 80))
    {
      client.print("GET /");
      for (int i = 0; i < codeLen; i++) {
      client.print(rawCodes[i]);
      }
      client.println(" HTTP/1.1");
      client.println("Host: 192.168.0.101");
      client.println();
      client.println();
      client.stop();
    } 
  
}
 

void loop()
{
  if (irrecv.decode(&results))
  {
    storeCode(&results);
    sendIR();
    irrecv.resume(); // Receive the next value
  }
}


Now make sure that you set the ip address as a value which mach your sub-net i.e first three groups. Now navigate with your browser to make sure that the transmit end is up and running.

To make the IR accepter, a second arduino is hooked with an ir receiver to pin 6 (refer to previous post for pin connection) and load the receiver sketch and point your remote to it. The signals will be send to the server and will be relayed over there to target ir device.

//Sending ir codes over ethernet
//This sketch accept IR from an IR sensor connected to Pin 6 and sends over IP
// parts of the code for storing ir in to buffer is from  Ken Shirriff's ir recorder http://arcfn.com
//
//                       THIS ONE PICKS IR FROM A REMOTE
//
#include <IRremote.h>
#include <SPI.h>
#include <Ethernet.h>
 

int RECV_PIN = 6;
IRrecv irrecv(RECV_PIN);
decode_results results;
 
// Network settings 
byte mac[] = {  0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; //MAC
byte gateway[] = { 192,168,0,1 };// Gateway
byte ip[] = { 192,168,0,123 };  //ip if dhcp fails 
 
// IP of the Target arduino with ir emitter
IPAddress server(192,168,0,102);  
EthernetClient client;

unsigned int rawCodes[RAWBUF]; // The durations if raw
int codeLen; // The length of the code
//Get the infrared codes from remote

void storeCode(decode_results *results) {
  int count = results->rawlen;
  Serial.println("Received code");
  codeLen = results->rawlen - 1;
    for (int i = 1; i <= codeLen; i++) {
      if (i % 2) {
        // Mark
        rawCodes[i - 1] = results->rawbuf[i]*USECPERTICK - MARK_EXCESS;
        Serial.print(" m");
      } 
      else {
        // Space
        rawCodes[i - 1] = results->rawbuf[i]*USECPERTICK + MARK_EXCESS;
        Serial.print(" s");
      }
      Serial.print(rawCodes[i - 1], DEC);
    }
    Serial.println("");
}

 
void setup()
{
  // For debug
  Serial.begin(9600);
  // Start ethernet
  Ethernet.begin(mac, ip, gateway);
  Serial.println("Initialised");
}

//sending the raw IR code over the ether net

void sendIR()
{
   
    // Send over HTTP
    if (client.connect(server, 80))
    {
      client.print("GET /");
      for (int i = 0; i < codeLen; i++) {
      client.print(rawCodes[i]);
      }
      client.println(" HTTP/1.1");
      client.println("Host: 192.168.0.101");
      client.println();
      client.println();
      client.stop();
    } 
  
}
 

void loop()
{
  if (irrecv.decode(&results))
  {
    storeCode(&results);
    sendIR();
    irrecv.resume(); // Receive the next value
  }
}

Friday, February 28, 2014

Arduino - controlling high voltage devices-from dc motors to ceiling fan speed controller

Arduinos and raspberry pi's operate at a lower voltage (5v) and controlling led's, servos etc can be done this voltage or with a simple transistor based switching. In this post i will show different techniques used to control devices at a higher voltage and power levels. These techniques can be used in conjunction with many other projects for e.g remote controllers  to control high power devices like room heaters, lamps e.t.c In general, you need to couple the 5voltage control signals to the input of these switching circuits.

Safety Considerations

Some parts of these circuits operate at extremely lethal voltage and current levels. Do not attempt to work on these without understanding and following ALL of the relevant safety guidelines for high voltage and/or line connected electrical and electronic systems.

Controlling DC motors at 12 volt with Speed controle (PWM)

Controlling DC motors with PWM (pulse width modulation, to control the intensity/speed) is straight forward. Here we use a special transistor (TIP-122, datasheet) which is a darlington pair and can switch a device up to 100Volts (DC). It is a TO-220 package with all inside the package (see figure). Connect the PWM output (for speed control) to the base of TIP-122. Connect emitter pin to ground and collector pin to C fan/motor and the other end of fan to positive end of 12v DC.

Use any pwm pin (eg Pin 9 with analogWrite(9,speed_value);) and connect it to the base of the transistor (TIP-122) via 1k resistor

TIP-122 pin connection for controlling 12volt motor with arduino/raspberryp pi

Controlling AC lights, heaters, fans (120/220Volt)

There are two approaches. The simplest is to use a relay and this can be done with the same circuit shown above. The relay has a small electromagnet which switches on/off a high voltage line. Asecond approach which doesnt have any mechanical parts is a solid state relay (SSR). These devices have built in semiconductors which does the switching and there is an isolation in the form of opto-couplers  or so to isolate the low voltage arduino lines from the mains. These SSR can switch devices at 120 or 220 volts depending on the bulit and can be obtained from manufactures like omron also can be spotted on online stores like ebay.
Solid state relay to easily control ac 220v/120v devices with arduino

Controlling AC fan speeds with arduino/raspberry pi

It is normal to think that the pulse width modulation can be used to control the speed / brightness of ac devices by pwm ing the SSR. But it is not feasible and a different approach is needed to adjust the motor speeds in AC. The lights can be controlled by converting AC to DC as they works on both while AC motors need some tricks to adjust their speed. This is done by controlling the on time of triacs after a delay from zero crossing (more details on this is beyond the scope of this post). A manual control circuit for AC fan speed is shown below

AC 220v ceiling fan speed control with arduino pwm trick

But here is a trick with out the need for zero crossing and is borrowed from an article (dont remeber where) which used a similar approach on a drilling machine. See the modification above where a cds ldr is used in place of the variable resistor used to control the capacitors charging time and a pwm ed led is used to adjust the resistance value. This is a quick and dirty hack and may help some one. The correct way is complex and need to sense the zero crossing and the firing time of triac with respect to the ac cycles.



Sunday, February 23, 2014

Setting up Nordic nRF24L01 RF modules on Raspberry Pi for Home automation

Using NRF24l01 in hobby projects helps to make things wireless. I was using it with arduinos and was using one arduino attached to raspberry pi as the hub for various sensors used in home automation so that they can communicate with the raspberry pi and hence to the internet. So i just did a quick test to run the nrf module directly on the pi. There were plenty of documentations out there and i was a bit over whelmed.

So here is a pi-nrf24 gymnastic to test the things out on the weekend (see figure ). Every thing is hanging on the wall for a quick test. I used the latest build of raspbmc (as my primary use of the pi is for watching media). There are special software like Occidentalis which is specifically made for easy i2c, spi etc (from Adafruit)
Gymnstics with raspberry pi and nrf24l01
I used Raspbmc and the first step was to get the SPI working and was pretty easy with the latest build. But for some reasons i need to use the following commands to make the spi up and running. So just type the code below before doing more experiments with SPI on raspbmc

 sudo boblightd --help

A bit more on spi getting up on Pi can be read here ( very nice article by Brian). In the next step i downloaded the rf24 library for raspberry pi and can be accessed on git hub at here. To make it simple i have a fork of the part needed for the raspberry pi at here

So ssh in to the pi and issue the following commands to compile it on the pi

git clone https://github.com/riyas-org/rf24pi
cd rf24pi
make 
sudo make install

If everything went fine, you can hook up your pi with nrf module. You can do it nicely but it is also possible to follow my gymnastic way as shown above/below

Raspberry pi as nrf240l hub for wireless sensors
To see the pins on the nrf module you can see this post. The gpio pins on the pi can be identified as follows
GPIO piout on raspberry pi for spi

Connect the module as follows

RPi GPIO9    (Pin 21)  to RF Module Pin 7 ( MISO )
RPi GPIO10  (Pin 19)  to RF Module Pin 6 ( MOSI )
RPi GPIO 11 (Pin 23)  to RF Module Pin 5 ( SCK )
RPI 3.3V       (Pin 17)  to RF Module Pin 2 ( VCC/3.3V )
RPi Gnd        (Pin 25)  to RF Module Pin 1 (GND)

RPi GPIO 25 (Pin 22) to RF Module Pin 3 ( CE )
RPi GPIO8 (Pin 24) to RF Module Pin 4 ( CSN )



To test cd to examples folder and run the scanner (sudo ./scanner) and it should show the rf signals around on different channels.

As a starting point, it may be easier to test a wifi scanner on the pi as described in this post, which will ensure that the module is working and connected properly (with a single module)

PIN Connections
Connecting NRF24l01 module to the pi (Rev2 board)