KGB
Karve-Genealogy-Blogs
Karve Family Trees
- Karanjgaon-Urse-Bhadas-Uksan-Atkar-Pen-Varsai-Pune
- Panchnadi-Gimvane-Shirde-Belapur-Baroda-Murbad
- Zirad (Panchnadi-Shirde-Gimvane)
- Panchnadi-Palaspe
- Panchnadi-Avlas-Bangalore-Badlapur
- Karwar-Asnoti-Sadashivgad
- Kolthare-Kihim
- Akshi-Nagaon
- Kolthare-Malkapur-Amba
- Talsure-Chas-Buldhana
- Talsure-Bhivandi-Kavad
- Talsure-Mahuli-Shahpur-Kalyan
- Talsure-Kalyan-Vadrap-Neral
- Panchnadi-Talsure-Murdi-Turade-Panvel
- Panchnadi-Koil-Nere
Welcome to www.karve.in
KGB is a an effort to electronically document the Karve Family Tree. The title KGB stands for Karve-Genealogy-Blogs. Genealogy is nothing but the study or investigation of ancestry and family histories. Called in Marathi as Kul-Vruttant. This site is a a result of my ongoing efforts to learn PHP. I also aim to develop a software for the same.
Also in this site are blogs which will help us to share our thoughts, ideas, views, suggestions on any topic. I solicit your sincere efforts to progress this cause
Fun with Interrupts : Decoding Quadrature Encoders on TI Launchpad MSP-EXP430G2
February 14th, 2012 | No Comments »
This is one of those programming assignments I had undertaken to learn more about interrupts and timers. I have used a detented quadrature encoder before and used it in a small project . However the routine for these earlier trysts with encoder were not 100% original and somehow I had a feeling that I had not grasped the routine.
Although there are number of routines available online to use these encoders, nothing gives you a kick like the one you get re-inventing the wheel.
The encoder which I am using is actually a knob having detents (clicks) at every turn increment. The knob also has a keyswitch akin to a centre-click on mice.
This is those kind of knobs which you see on Hi-Fi audio systems/radio for volume or frequency controls. This is the link to the product page. The output of these encoders is 2 bit gray code. One of the benefit using these encoders is that they can replace push buttons and if coded well can be used for menu operations.
The encoder outputs the data on two channel (CH A and CH B ); the output signal sequence depends on two factors viz bias (active high or active low) and direction of rotation. In my test routine I have catered for an active high configuration .i.e the common pin is connected to GND and CH A, CH B and Button pins are connected directly to MCU pins which have been pulled up. So, if you take a multi-meter and measure voltage on CH A , CH B or Button pin , you will read VCC with respect to Ground. The subsequent explanation is based on the abovementioned configuration.
As I mentioned above, at steady state the CH A , CH B or Button pins will read VCC. However when the knob is turned or pressed the pins will be pulled low momentarily and again pulled. Illustrated below is the signal sequence for rotation in both direction
So, as is evident from the above two diagrams, to determine direction of rotation we just have to interpret the change in bit values. A transition of bit value from 1 to 0 would indicate a CCW rotation or a transition from 3 to 2 would indicate a clockwise rotation.
The code is sufficiently commented for ease of understanding. Please free to post a comment for your suggestions and queries
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 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 | /* ---------------------------------------------------------------------- * Demo program routine to read/interpret a detented rotary encoder * Coded by Ishan Karve (ishan at the rate gmail dot com) * 14 Feb 2012 * Platform : TI MSP-EXP430G2 Launchpad running MSP430G2553 * Encoder : Rotary Encoder Switch With Keyswitch * http://www.sureelectronics.net/goods.php?id=221 * Software Suite : Code Compose Studio, Version: 4.2.3.00004 * ---------------------------------------------------------------------- * * Routine Caters for * Clockwise Rotation. * Counter Clockwise Rotation * Single Button Click * Double Button Click * To Do * Click and Turn (CW or CCW) */ #include <msp430g2553.h> #include <stdbool.h> //Defines for Launchpad LEDs #define LED1 BIT6 #define LED0 BIT0 //define for quarature encoder #define CH_B BIT0 //P2.0 Encoder Pin #define CH_A BIT1 //P2.1 Encoder Pin #define BUTTON BIT2 //P2.2 Encoder Pin #define COMM BIT5 //P1.5 Encoder Pin //function declaration void pattern_match(); //determine direction of rotation void TX(char *tx_message); //Serial UART TX static char *i2a(unsigned i, char *a, unsigned r); //convert int to char char *itoa(int i, char *a, int r); //convert int to char void ADC_init(void); //variable declaration char buffer[5]; int a=0; //variable stores interrupt edge int b=0; //variable stores interrupt edge int cval=3; //store current value to determine rotation int pval=3; //stores previous value to determine rotation direction int tr_state=0; //variable to store transition state int num=0; //arbitary number. will increment or decrement iff tr_satate=4 int tr; //holds trigget state for variable tr_state int pnum=0; int inc_fact=1; //this variable holds the increment factor int click=0; //holds button click count int sclick=false; //flag holds single click, has to be reset in program loop int dblclk=false; //flag holds double click, has to be reset in program loop void main(){ //stop watchdog WDTCTL = WDTPW + WDTHOLD; //setup internal DCO BCSCTL1 = CALBC1_1MHZ; // Set DCO to 1MHz DCOCTL = CALDCO_1MHZ; //Setup output ports P1DIR |= (LED0+LED1+COMM); P1OUT &=~(LED0+LED1+COMM); //setup interrupts for quadrature encoder P2DIR&= ~(CH_A + CH_B + BUTTON); //set ports as inputs P2IE |= (CH_A + CH_B + BUTTON); //enable interrupts for Quad Encoder //active-high configuration P2REN |=(CH_A + CH_B + BUTTON); //enable pullups on respective ports P2OUT |=(CH_A + CH_B + BUTTON); P2IFG&=~(CH_A + CH_B + BUTTON); //clear interrupt flag P2IES&=~(CH_A + CH_B + BUTTON); //Interrupt on Hi->Lo transition //Setup USCI for UART communication at 9800-N-1 P1SEL = BIT1 + BIT2; // Set P1.1 to RXD and P1.2 to TXD P1SEL2 = BIT1 + BIT2; UCA0CTL1 |= UCSSEL_2; // Have USCI use SMCLK AKA 1MHz main CLK UCA0BR0 = 104; // Baud: 9600, N= CLK/Baud, N= 10^6 / 9600 UCA0BR1 = 0; // Set upper half of baud select to 0 UCA0MCTL = UCBRS_1; // Modulation UCBRSx = 1 UCA0CTL1 &= ~UCSWRST; // Start USCI TX("Rotary Encoder Demo\r\n"); /* setup timer to watch for double click * the double clik period is 0.5 second * mcu is running at 1 MHZ, hence we will divide (prescale) * the clock by 8 to get a frequency of 125 KHZ. Clock source * is SMCLK * * interrupt_period = (ticks/timer_clock_freq) * hence for a period of 0.5 second no of ticks = 62500 */ TACTL= TASSEL_2 + ID_3 + TACLR; //SMCLJK + divide by 8 + clear counter TACCTL0=CCIE; //enable timer TACCR0=62500; //set timer interrupt count //note timer is not enabled. timer will be enabled in button interrupt routine _enable_interrupt(); for (;;) { if (num!=pnum){ itoa(num, buffer, 10); TX(buffer); TX("\r\n"); pnum=num; } } } void pattern_match(){ /* Transition sequence is as follows (Active High Config) * --Clockwise-- * CH_A -> H L L H H * CH_B -> H H L L H * ---Counter-Clockwise--- * CH_A -> H H L L H * CH_B -> H L L H H * * If the above pattern is stored in a 2 bit array with CH_A as Bit 0 and CH_B as Bit 1 * The value shall be as follows (integer) * CW -> 3-2-0-1-3 * CCW -> 3-1-0-2-3 */ int cw[]={3,2,0,1,3}; int ccw[]={3,1,0,2,3}; int i; for (i=1;i<5;i++){ if (pval==cw[i-1] && cval==cw[i]) { if (tr){tr_state++;} else (tr=1); if (tr_state>=5) { num=num+inc_fact; tr_state=0; } } } for (i=1;i<5;i++){ if (pval==ccw[i-1] && cval==ccw[i]) { if (!tr){tr_state++;} else (tr=0); if (tr_state>=5) { num=num-inc_fact; tr_state=0; } } } } static char *i2a(unsigned i, char *a, unsigned r) { if (i/r > 0) a = i2a(i/r,a,r); *a = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"[i%r]; return a+1; } char *itoa(int i, char *a, int r) { if ((r < 2) || (r > 36)) r = 10; if (i < 0) { *a = '-'; *i2a(-(unsigned)i,a+1,r) = 0; } else *i2a(i,a,r) = 0; return a; } void TX(char *tx_message) { unsigned int i=0; //Define end of string loop int char *message; // message variable unsigned int message_num; // define ascii int version variable message = tx_message; // move tx_message into message while(1) { if(message[i]==0) // If end of input string is reached, break loop. {break;} message_num = (int)message[i]; //Cast string char into a int variable UCA0TXBUF = message_num; // write INT to TX buffer i++; // increase string index __delay_cycles(10000); //transmission delay if(i>50) //prevent infinite transmit { P1OUT |= LED1; break; } } // End TX Main While Loop } // End TX Function // Port 2 interrupt service routine #pragma vector=PORT2_VECTOR __interrupt void Port_2(void) { pval=cval; //store current_value //disable all interrupts P2IE&=~(CH_A + CH_B + BUTTON); //check interrupt flags to find which interrupt was triggred //Check for CH_A interrupt if ((P2IFG & CH_A)==CH_A) { if ((P2IN & CH_A)==CH_A) {a=1;}else {a=0;} //read pin state if ((P2IN & CH_B)==CH_B) {b=1;}else {b=0;} //read pin state cval=(a << 0 ) | (b <<1); P2IES^=CH_A; //toggle interrupt edge } //Check for CH_B interrupt if ((P2IFG & CH_B)==CH_B) { if ((P2IN & CH_A)==CH_A) {a=1;}else {a=0;} //read pin state if ((P2IN & CH_B)==CH_B) {b=1;}else {b=0;} //read pin state cval=(a << 0 ) | (b <<1); P2IES^=CH_B; //toggle interrupt edge } //Check for Buttton interrupt also caters for double click if ((P2IFG & BUTTON)==BUTTON) { click++; if (click>=2) { click=0; dblclk=true; //set flag true, has to be set false once action is taken on flag P1OUT ^= (LED0+LED1); //toggle both leds } TACTL |= MC_1; //enable timer } pattern_match(); //clear all interrupts P2IFG&=~(CH_A + CH_B + BUTTON); //enable all interrupts P2IE|=(CH_A + CH_B + BUTTON); } #pragma vector=TIMER0_A0_VECTOR __interrupt void Timer0(void) { if (click==1){sclick=true;P1OUT ^= LED0;} TACTL |= MC_0; //disable timer click=0; } |
You can download the project files here or source c file
Ishan Karve on February 14, 2012Fun with Timers : Decoding an Sony SIRC protocol on TI Launchpad MSP-EXP430G2
February 13th, 2012 | No Comments »
This post is one of those which have resulted out of my self learning . Over the time I have become fairly conversant with Arduino (ATMega type) and PIC and hence I decided to take the plunge into the world of TI Launchpad to understand more about TI’s value line processors. The last post on reading LM35 temperature sensor emanated from a similar urge.

Understanding concept of timers and their effective use has always eluded me. Though superficially it looks simple, however once you get to the implementation part, things start looking really uphill.
Getting to work around MSP timers was especially challenging, since I am fairly new to these devices and their configurations.
One of my greatest obstacle in these so called self learning initiatives has been the lack of test equipment. The only one I have is a super el-cheapo multi-meter. That’s it. I know that’s blasphemous, however considering what I do and money I get, I excuse myself.
This week I decided to decode a Sony Remote Control Protocol (SIRC) as I had a spare TSOP IR sensor lying on my table, begging to be used.
Since I had used this protocol earlier, I was conversant with its basic structure.
Simply put, the data is sent in a stream of bits, LSB first. The bit stream consists of 13 bits including the start bit. The ‘mark’ period of the start bit is 2.4mS and the ‘space’ period is 600uS, which is common. Binary 1 is ‘mark’ of 1.2mS and ‘space’ of 600uS, Binary 0 is’mark’ and ‘space’ of 600uS each.
So all the three pulses, Start, One and Zero have different duty cycles and time periods.
The 13 symbol bit stream can be broken down into three parts Start Bit (1 bit), Command (7 bit) and Address (5 bit)
Since I did not have a signal generator handy, I decided to use a Arduino to do its job. I programmed to send a stream of pulses to emulate a key press of the remote. I did this so that I can tweak with the bit timings and be sure that that end program is working correctly. I have not used any PWM for generating pulse only bit banging.
Arduino Sketch:
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 | void setup() { pinMode(3, OUTPUT); pinMode(4, OUTPUT);//LED output } void one(){ digitalWrite(3, HIGH);digitalWrite(4, HIGH); delayMicroseconds(1200); digitalWrite(3,LOW);digitalWrite(4, LOW); delayMicroseconds(600); } void zero(){ digitalWrite(3, HIGH);digitalWrite(4, HIGH); delayMicroseconds(600); digitalWrite(3,LOW);digitalWrite(4, LOW); delayMicroseconds(600); } void start() { digitalWrite(3, HIGH);digitalWrite(4, HIGH); delayMicroseconds(2400); digitalWrite(3,LOW);digitalWrite(4, LOW); delayMicroseconds(600); } void loop() { start(); one(); one(); zero(); zero(); one(); zero(); zero(); one(); zero(); zero(); zero(); zero(); delay(5000); } |
Launchpad Code (Code Composer Studio)
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 | /* ---------------------------------------------------------------------- * This program scan 50 samples on Port2.0 and dumps their pulse widths * Coded by Ishan Karve (ishan at the rate gmail dot com) * 12 Feb 2012 * Platform : TI MSP-EXP430G2 Launchpad running MSP430G2553 * Software Suite : Code Compose Studio, Version: 4.2.3.00004 * ---------------------------------------------------------------------- */ #include #include ////////////////Defines//////////////// #define LED1 BIT6 #define LED0 BIT0 #define DAT BIT0 //P2.0 //input signal port #define VCC BIT5 //P1.5 #define GND BIT4 //P1.4 char charbuffer[8]; int i=0; int j=0; unsigned int capture_array[51]; // RAM array for captures int tick=0; int cap=0; int pre_cap=0; int first_pulse=0; ////////////////Function Protos//////////////// void TX(char *tx_message); static char *i2a(unsigned i, char *a, unsigned r); char *itoa(int i, char *a, int r); static char *i2a(unsigned i, char *a, unsigned r) { if (i/r > 0) a = i2a(i/r,a,r); *a = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"[i%r]; return a+1; } char *itoa(int i, char *a, int r) { if ((r < 2) || (r > 36)) r = 10; if (i < 0) { *a = '-'; *i2a(-(unsigned)i,a+1,r) = 0; } else *i2a(i,a,r) = 0; return a; } void TX(char *tx_message) { unsigned int i=0; //Define end of string loop int char *message; // message variable unsigned int message_num; // define ascii int version variable message = tx_message; // move tx_message into message while(1) { if(message[i]==0) // If end of input string is reached, break loop. {break;} message_num = (int)message[i]; //Cast string char into a int variable UCA0TXBUF = message_num; // write INT to TX buffer i++; // increase string index __delay_cycles(10000); //transmission delay if(i>50) //prevent infinite transmit { P1OUT |= (LED1+LED0); break; } } // End TX Main While Loop } // End TX Function int main(void) { WDTCTL = WDTPW + WDTHOLD; // Stop watchdog timer //setup clock to 1MHZ BCSCTL1 = CALBC1_1MHZ; // Set DCO to 1MHz DCOCTL = CALDCO_1MHZ; ////////////////USCI setup//////////////// P1SEL = BIT1 + BIT2; // Set P1.1 to RXD and P1.2 to TXD P1SEL2 = BIT1 + BIT2; // UCA0CTL1 |= UCSSEL_2; // Have USCI use SMCLK AKA 1MHz main CLK UCA0BR0 = 104; // Baud: 9600, N= CLK/Baud, N= 10^6 / 9600 UCA0BR1 = 0; // Set upper half of baud select to 0 UCA0MCTL = UCBRS_1; // Modulation UCBRSx = 1 UCA0CTL1 &= ~UCSWRST; // Start USCI ////////////////General GPIO Defines//////////////// P1DIR |= (LED0 + LED1+GND+VCC); //define output ports P1OUT &= ~(LED0 + LED1+GND); //turn ports low P1OUT|=VCC; P2IE |= DAT; P2IFG &= ~DAT; P2SEL = DAT; // Set P1.1 to TA0 /////////////////SETUP TIMER TA1CCTL0 = CM_1 + SCS + CCIS_0 + CAP + CCIE; // Rising edge + CCI0A (P2.0)// + Capture Mode + Interrupt TA1CTL = TASSEL_2 + MC_2; // SMCLK + Continuous Mode __enable_interrupt(); for(;;) { } } // Timer1 interrupt service routine #pragma vector=TIMER1_A0_VECTOR __interrupt void TIMER1(void) { if (first_pulse==0) { pre_cap=TA1CCR0; first_pulse=1; goto here; //break from interrupt service routine } tick = TA1CCR0; cap = tick- pre_cap; capture_array[i]=cap; i++; if (i == 12) { P1OUT^=LED0;//toggle led //turnoff timer 1 TA1CTL = MC_0; //dump samples for (j=0;j itoa(capture_array[j], charbuffer, 10); TX(charbuffer); TX("\r\n"); } TX("------------------\r\n"); first_pulse=0; i=0; //start timer TA1CTL = TASSEL_2 + MC_2; // SMCLK + Continuous Mode } pre_cap = tick; // store this capture value here: P1OUT^=LED1; } |
Once I was at this step I was pretty sure that I was on right track and 80% job was done. The timing as seen above are pulse period (Mark+Space duration). This is because I am capturing the pulse on the rising edge.
The next step was just to connect an actual sensor and test the code. Here is the output. Channel 1 button is pressed.
As you may see, there are some variations, this is because the key presses being repeated and delay caused by serial transmission.
Modified Code
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 | /* ---------------------------------------------------------------------- * Decode Sony IR Remote (RM-GA020) using MSP430 Microcontroller * Coded by Ishan Karve (ishan at the rate gmail dot com) * 12 Feb 2012 * Platform : TI MSP-EXP430G2 Launchpad running MSP430G2553 * Software Suite : Code Compose Studio, Version: 4.2.3.00004 * ---------------------------------------------------------------------- */ #include ////////////////Defines//////////////// #define LED1 BIT6 #define LED0 BIT0 #define DAT BIT0 //P2.0 //input signal port #define VCC BIT5 //P1.5 #define GND BIT4 //P1.4 char charbuffer[8]; int i=0; int j=0; unsigned int capture_array[51]; // RAM array for captures int tick=0; int cap=0; int pre_cap=0; int first_pulse=0; int start_bit=0; ////////////////Function Protos//////////////// void TX(char *tx_message); static char *i2a(unsigned i, char *a, unsigned r); char *itoa(int i, char *a, int r); static char *i2a(unsigned i, char *a, unsigned r) { if (i/r > 0) a = i2a(i/r,a,r); *a = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"[i%r]; return a+1; } char *itoa(int i, char *a, int r) { if ((r < 2) || (r > 36)) r = 10; if (i < 0) { *a = '-'; *i2a(-(unsigned)i,a+1,r) = 0; } else *i2a(i,a,r) = 0; return a; } void TX(char *tx_message) { unsigned int i=0; //Define end of string loop int char *message; // message variable unsigned int message_num; // define ascii int version variable message = tx_message; // move tx_message into message while(1) { if(message[i]==0) // If end of input string is reached, break loop. {break;} message_num = (int)message[i]; //Cast string char into a int variable UCA0TXBUF = message_num; // write INT to TX buffer i++; // increase string index __delay_cycles(10000); //transmission delay if(i>50) //prevent infinite transmit { P1OUT |= (LED1+LED0); break; } } // End TX Main While Loop } // End TX Function int main(void) { WDTCTL = WDTPW + WDTHOLD; // Stop watchdog timer //setup clock to 1MHZ BCSCTL1 = CALBC1_1MHZ; // Set DCO to 1MHz DCOCTL = CALDCO_1MHZ; ////////////////USCI setup//////////////// P1SEL = BIT1 + BIT2; // Set P1.1 to RXD and P1.2 to TXD P1SEL2 = BIT1 + BIT2; // UCA0CTL1 |= UCSSEL_2; // Have USCI use SMCLK AKA 1MHz main CLK UCA0BR0 = 104; // Baud: 9600, N= CLK/Baud, N= 10^6 / 9600 UCA0BR1 = 0; // Set upper half of baud select to 0 UCA0MCTL = UCBRS_1; // Modulation UCBRSx = 1 UCA0CTL1 &= ~UCSWRST; // Start USCI ////////////////General GPIO Defines//////////////// P1DIR |= (LED0 + LED1+GND+VCC); //define output ports P1OUT &= ~(LED0 + LED1+GND); //turn ports low P1OUT|=VCC; P2IE |= DAT; P2IFG &= ~DAT; P2SEL = DAT; // Set P1.1 to TA0 /////////////////SETUP TIMER TA1CCTL0 = CM_1 + SCS + CCIS_0 + CAP + CCIE; // Rising edge + CCI0A (P2.0)// + Capture Mode + Interrupt TA1CTL = TASSEL_2 + MC_2; // SMCLK + Continuous Mode __enable_interrupt(); for(;;) { } } // Timer1 interrupt service routine #pragma vector=TIMER1_A0_VECTOR __interrupt void TIMER1(void) { if (first_pulse==0) { pre_cap=TA1CCR0; first_pulse=1; goto here; //break from interrupt service routine } tick = TA1CCR0; cap = tick- pre_cap; //wait till start bit is intercepted if ((cap >=29000) && (cap =1190) && (cap =1790) && (cap } if (i == 11) { P1OUT^=LED0;//toggle led //turnoff timer 1 TA1CTL = MC_0; //dump samples for (j=0;j itoa(capture_array[j], charbuffer, 10); TX(charbuffer); TX("\r\n"); } TX("------------------\r\n"); first_pulse=0; i=0; //start timer TA1CTL = TASSEL_2 + MC_2; // SMCLK + Continuous Mode } pre_cap = tick; // store this capture value here: P1OUT^=LED1; } |
Now that I have reached here, the last thing to do is to group the bits into bytes, change their order and output it on the screen. So below is the final code.
Final Code
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 | /* ---------------------------------------------------------------------- * Decode Sony IR Remote (RM-GA020) using MSP430 Microcontroller * Coded by Ishan Karve (ishan at the rate gmail dot com) * 12 Feb 2012 * Platform : TI MSP-EXP430G2 Launchpad running MSP430G2553 * Software Suite : Code Compose Studio, Version: 4.2.3.00004 * ---------------------------------------------------------------------- */ #include ////////////////Defines//////////////// #define LED1 BIT6 #define LED0 BIT0 #define DAT BIT0 //P2.0 //input signal port #define VCC BIT5 //P1.5 #define GND BIT4 //P1.4 char charbuffer[8]; int i=0; int j=0; unsigned int capture_array[51]; // RAM array for captures int tick=0; int cap=0; int pre_cap=0; int first_pulse=0; int start_bit=0; ////////////////Function Protos//////////////// void TX(char *tx_message); static char *i2a(unsigned i, char *a, unsigned r); char *itoa(int i, char *a, int r); static char *i2a(unsigned i, char *a, unsigned r) { if (i/r > 0) a = i2a(i/r,a,r); *a = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"[i%r]; return a+1; } char *itoa(int i, char *a, int r) { if ((r < 2) || (r > 36)) r = 10; if (i < 0) { *a = '-'; *i2a(-(unsigned)i,a+1,r) = 0; } else *i2a(i,a,r) = 0; return a; } void TX(char *tx_message) { unsigned int i=0; //Define end of string loop int char *message; // message variable unsigned int message_num; // define ascii int version variable message = tx_message; // move tx_message into message while(1) { if(message[i]==0) // If end of input string is reached, break loop. {break;} message_num = (int)message[i]; //Cast string char into a int variable UCA0TXBUF = message_num; // write INT to TX buffer i++; // increase string index __delay_cycles(10000); //transmission delay if(i>50) //prevent infinite transmit { P1OUT |= (LED1+LED0); break; } } // End TX Main While Loop } // End TX Function int main(void) { WDTCTL = WDTPW + WDTHOLD; // Stop watchdog timer //setup clock to 1MHZ BCSCTL1 = CALBC1_1MHZ; // Set DCO to 1MHz DCOCTL = CALDCO_1MHZ; ////////////////USCI setup//////////////// P1SEL = BIT1 + BIT2; // Set P1.1 to RXD and P1.2 to TXD P1SEL2 = BIT1 + BIT2; // UCA0CTL1 |= UCSSEL_2; // Have USCI use SMCLK AKA 1MHz main CLK UCA0BR0 = 104; // Baud: 9600, N= CLK/Baud, N= 10^6 / 9600 UCA0BR1 = 0; // Set upper half of baud select to 0 UCA0MCTL = UCBRS_1; // Modulation UCBRSx = 1 UCA0CTL1 &= ~UCSWRST; // Start USCI ////////////////General GPIO Defines//////////////// P1DIR |= (LED0 + LED1+GND+VCC); //define output ports P1OUT &= ~(LED0 + LED1+GND); //turn ports low P1OUT|=VCC; P2IE |= DAT; P2IFG &= ~DAT; P2SEL = DAT; // Set P1.1 to TA0 /////////////////SETUP TIMER TA1CCTL0 = CM_1 + SCS + CCIS_0 + CAP + CCIE; // Rising edge + CCI0A (P2.0)// + Capture Mode + Interrupt TA1CTL = TASSEL_2 + MC_2; // SMCLK + Continuous Mode __enable_interrupt(); for(;;) { } } // Timer1 interrupt service routine #pragma vector=TIMER1_A0_VECTOR __interrupt void TIMER1(void) { int mask=0; int addr=0; int cmd=0; if (first_pulse==0) { pre_cap=TA1CCR0; first_pulse=1; goto here; //break from interrupt service routine } tick = TA1CCR0; cap = tick- pre_cap; //wait till start bit is intercepted if ((cap >=29000) && (cap =1190) && (cap =1790) && (cap } if (i == 11) { P1OUT^=LED0;//toggle led //turnoff timer 1 TA1CTL = MC_0; //dump samples for (j=0;j itoa(capture_array[j], charbuffer, 10); TX(charbuffer); TX("\r\n"); } TX("------------------\r\n"); //extract command and address mask = 0x01; for (j=0; j if (capture_array[j]) cmd = cmd | mask; mask < } mask = 0x01; for (j=7; j if (capture_array[j]) addr = addr | mask; mask < } TX("CMD:"); itoa(cmd, charbuffer, 10); TX(charbuffer); TX(" ADDR:"); itoa(addr, charbuffer, 10); TX(charbuffer); TX("\r\n"); first_pulse=0; i=0; //start timer TA1CTL = TASSEL_2 + MC_2; // SMCLK + Continuous Mode } pre_cap = tick; // store this capture value here: P1OUT^=LED1; } |
Following Links have provided me valuable insight to help me understand the topic
- http://processors.wiki.ti.com/index.php/MSP430_LaunchPad_LED_Timer
- http://www.msp430launchpad.com/2010/07/timers-and-clocks-and-pwm-oh-my.html
- http://www.ti.com/lit/ml/slap113/slap113.pdf
- http://www.ccs.neu.edu/home/noubir/Courses/CSU610/S07/MSP430-Clock-Timers.pdf
- http://picprojects.org.uk/projects/sirc/sonysirc.pdf
- http://www.43oh.com/forum/viewtopic.php?f=9&t=358
- http://www.43oh.com/wp-content/uploads/2010/10/Lanchpad_Maxbotix_Ultrasonic_Rangefinder_MSP430G2211_main.c
- http://forum.sparkfun.com/viewtopic.php?t=8443
Getting started with Cygwin.
February 7th, 2012 | 4 Comments »
Recently I was inundated with queries, asking me how to run shell scripts using Cygwin. This illustrated guide is primarily meant to address the issue of running shell scripts posted on this site on a WindoZe machine.
I assume that your machine has a working internet connection and is connected to internet. I am going to be economical on words and let the screenshot speak.
Step 1. Download Cygwin.
Step 2. Execute the Cygwin executable (setup.exe)




















Step 3. After the install is complete, copy the script to Cygwin home folder. By default home folder is located at C:\cygwin\home\User
Step 4. Run Cygwin terminal from the Start menu -> Programs -> Cygwin
Step 5. Chmod +x the script to make it executable
Step 6. Run the script.

Step 7. The script throws up errors as there is no Desktop folder in the home directory
Step 8. mkdir Desktop and run script again.

Ishan Karve on February 7, 2012
Fun with TI Launchpad : Read LM35 sensor using Launchpad MSP430G2 (MSP430G2553)
February 6th, 2012 | No Comments »
Small code to read temperature from an LM35 sensor and output the temperature to Serial port. Uses hardware UART module and ADC 10 on launchpad.I do not claim originality for this code. Serial functions are borrowed from Nathan Zimmerman hardware UART example.
Hardware :TI MSP430G2 Launchpad
Software : Code Composer Studio Version: 4.2.3.00004
Connections are very simple.Hookup the LM35 sensor as follows
Vin->P2.0
GND->P1.4
Dout->P1.5
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 | #include <msp430g2553.h> #include <stdbool.h> ////////////////Defines//////////////// #define LED1 BIT6 #define LED0 BIT0 #define Error BIT0 #define GND BIT4 #define DAT BIT5 #define VCC BIT0 //port P2.0 char buffer[4]; float temp; int n_temp,r_temp; ////////////////Function Protos//////////////// void TX(char *tx_message); static char *i2a(unsigned i, char *a, unsigned r); char *itoa(int i, char *a, int r); void ADC_init(void); static char *i2a(unsigned i, char *a, unsigned r) { if (i/r > 0) a = i2a(i/r,a,r); *a = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"[i%r]; return a+1; } char *itoa(int i, char *a, int r) { if ((r < 2) || (r > 36)) r = 10; if (i < 0) { *a = '-'; *i2a(-(unsigned)i,a+1,r) = 0; } else *i2a(i,a,r) = 0; return a; } void TX(char *tx_message) { unsigned int i=0; //Define end of string loop int char *message; // message variable unsigned int message_num; // define ascii int version variable message = tx_message; // move tx_message into message while(1) { if(message[i]==0) // If end of input string is reached, break loop. {break;} message_num = (int)message[i]; //Cast string char into a int variable UCA0TXBUF = message_num; // write INT to TX buffer i++; // increase string index __delay_cycles(10000); //transmission delay if(i>50) //prevent infinite transmit { P1OUT |= Error; break; } } // End TX Main While Loop } // End TX Function void ADC_init() { /* Configure ADC Channel */ ADC10CTL1 = INCH_5 + ADC10DIV_3 ; // Channel 5, ADC10CLK/4 ADC10CTL0 = SREF_0 + ADC10SHT_3 + ADC10ON + ADC10IE; //Vcc & Vss as reference ADC10AE0 |= BIT5; //P1.4 ADC option } ////////////////////////////////MAIN PROGRAM LOOP////////////////////////////////////////////////// void main(void) { WDTCTL = WDTPW + WDTHOLD; // Stop WDT BCSCTL1 = CALBC1_1MHZ; // Set DCO to 1MHz DCOCTL = CALDCO_1MHZ; ////////////////USCI setup//////////////// P1SEL = BIT1 + BIT2; // Set P1.1 to RXD and P1.2 to TXD P1SEL2 = BIT1 + BIT2; // UCA0CTL1 |= UCSSEL_2; // Have USCI use SMCLK AKA 1MHz main CLK UCA0BR0 = 104; // Baud: 9600, N= CLK/Baud, N= 10^6 / 9600 UCA0BR1 = 0; // Set upper half of baud select to 0 UCA0MCTL = UCBRS_1; // Modulation UCBRSx = 1 UCA0CTL1 &= ~UCSWRST; // Start USCI ////////////////General GPIO Defines//////////////// P1DIR |= (LED1+GND+LED0); //Define GPIOs as outputs else GPIOs are inputs P2DIR |=VCC; P1OUT =0; //All Outputs off except for Green_LED P2OUT|=VCC; P1OUT&=~GND; //////////////SETUP ADC10 TO READ FROM CH A4 (P1.4)///////////////////////////////////////////// ADC_init(); __enable_interrupt(); // Enable interrupts. int value=0; ////////////////Main Loop//////////////// while(1) { __delay_cycles(1000); // Wait for ADC Ref to settle ADC10CTL0 |= ENC + ADC10SC; // Sampling and conversion start __bis_SR_register(CPUOFF + GIE); // LPM0 with interrupts enabled value = ADC10MEM; temp=value*0.317; //temperature as float //split the float into integer and fraction part //C = A % B is equivalent to C = A ? B * (A / B) r_temp=((temp*10)-10) * ((temp*10)/10); itoa(temp, buffer, 10); TX("$LM35:"); TX(buffer); TX("."); itoa(r_temp, buffer, 10); TX(buffer); TX("!\r\n"); P1OUT ^= LED0; _delay_cycles(500000); } // End While } // End Main Program // ADC10 interrupt service routine #pragma vector=ADC10_VECTOR __interrupt void ADC10_ISR (void) { __bic_SR_register_on_exit(CPUOFF); // Return to active mode P1OUT ^= LED1; } |
Fun with interrupts on MSP-EXP430FR5739 FRAM Experimenter Board
January 23rd, 2012 | No Comments »
Just a small program coded for learning interrupts. Code is commented and self explanatory.
Hardware :TI MSP-EXP430FR5739 FRAM Experimenter Board
Software : Code Composer Studio Version: 4.2.3.00004
Code:
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 | /* Blink LEDs using button S1 and S2 and interrupts. * In MSP-EXP430FR5739 FRAM board there are 8 user programmable LEDs * four of them are connected to PORTJ pins 0..3 and * another four are to PORT3 4..7 pins. * LEDs are connected to GND via current limiting resistors. * Button S1 and S2 are connected to GND via PORT4 0&1 respectively * --coded by Ishan Karve (ishan.karve@gmail.com), Feb 12 as part of his self learning process */ #include <msp430fr5739.h> //defines for MSP-430FR5739 EXP Board #define S1 BIT0 //P4.0 #define S2 BIT1 //P4.1 #define LED0 BIT0 //PJ.0 #define LED1 BIT1 //PJ.1 #define LED2 BIT2 //PJ.2 #define LED3 BIT3 //PJ.3 #define LED4 BIT4 //P3.4 #define LED5 BIT5 //P3.5 #define LED6 BIT6 //P3.6 #define LED7 BIT7 //P3.7 //define for quarature encoder #define CH_A BIT #define CH_B BIT #define BUTTON BIT #define COMM BIT void main(){ //stop watchdog WDTCTL = WDTPW + WDTHOLD; //Select pins 4,5,6,7 of Port 3 and 0 to 3 of Port J for output PJDIR|=(LED0+LED1+LED2+LED3); P3DIR|=(LED4+LED5+LED6+LED7); //turn off leds on both P3 and PJ PJOUT&=~(LED0+LED1+LED2+LED3); P3OUT&=~(LED4+LED5+LED6+LED7); //setup interrupts for buttons S1 and S2 P4DIR=0x00; //all ports as inputs P4IE|=(S1+S2); //enable interrupts for S1 button //Button S1 and S2 are in active-low configuration P4REN=(S1+S2);//enable pullups on resoective ports //Since S1,S2 are in an active low config, //the other end of the resistor has to be pulled high P4OUT=(S1+S2); P4IFG&=~(S1+S2); //clear interrupt flag _enable_interrupt(); for (;;) { //do nothing.interrupt routine will take charge } } // Port 4 interrupt service routine #pragma vector=PORT4_VECTOR __interrupt void Port_4(void) { if(P4IFG&S1) { //check if S1 was pressed P4IFG &= ~S1; // clear the interrupt flag PJOUT^=(LED0+LED1+LED2+LED3);//toggle leds on Port J } if(P4IFG&S2) { //check if S2 was pressed P4IFG &= ~S2; // clear the interrupt flag P3OUT^=(LED4+LED5+LED6+LED7);//toggle leds on Port 3 } } |
APC UPS Shutdown Manager
January 8th, 2012 | 18 Comments »
I work in an office where working late in the night is the norm including weekends.. This post is not about my office or about the work we do late into the night but about a problem statement posed to me by my subordinates. It goes like this..
The office or rather the building rules demand that we shut down (hard off) all electrical equipment once the office is finally shut for the day. This means that our office servers also requires to be shut down along with the UPS (APC SMART 2200). Now this has nothing to do with our office trying to be green but more to do about obviating any fire risks.
So, whats the problem. The problem is that, once we (management) leave the office, the duty staff has to shut down all the equipment including the servers. The issue is that our two servers running WindoZe 2008 R2 and WindoZe 2003 take about 15 minutes to shut down cleanly. And the UPS then needs to be shutdown afterwards. 15 minutes may seem a small time interval for an office which works routinely for 15+ hours in a day in a single shifts 7 days a week. But at night 2330 when its time to go home every minute looks like an hour to the subordinate staff. So the problem was narrated to me over a short tea break. Since I believe in working Smarter and not Harder, I decided to save 15 man minutes every day.
Thus (coming to the point), this post describes a circuit / contraption I have devised which shuts down the UPS once it detects that both the computers dependent upon it have secured for the day.
Detection of the state of the servers is done by pinging the servers at a predetermined time interval. However generally since persistent ICMP pings are blocked by firewalls, I have decided to do an ARP request to find the state of servers. So in a nutshell if a server responds to ARP request it is deemed to be alive.
The Arduino board communicates with the UPS over a serial port. The serial communication is done at 2400 8N1, No handshake. The communication protocol used is well discussed and elaborated here. Once the micro-controller detects that the servers are down for a defined time interval, it is assumed that the servers are off and a shutdown command is then issued to the UPS via Serial Port.
However, during the day time there is a possibility that the network (read physical media) might itself be under maintenance or the network switch might be down due to a power outage, so there emerges a possibility that the server may not respond to any ARP requests, leading to a shutdown of the UPS and thereby causing an unclean server shutdown.
To circumnavigate this issue, it becomes essential that the microcontroller knows the time of the day and shutdowns the servers only after 2000 hrs . The solution to this problem is a simple RTC clock. Now how does the RTC update its own time.. NTP. One of my server functions as an NTP server and I decided to use the same to update my RTC clock one when its booted up. Once this was done, the code was modfied accordingly.
Knowing time of the day was also essential since I had to switch on the UPS and then the servers. UPS is switched on via a serial command and the servers are switched on via a Wake on Lan (WOL) magic packet. Not implemented since I have configured the Server’s Bios to WakeUp on restoration of power on.
I have also thrown in a DS18B20 temperature sensor to measure the ambient room temperature.
The system state including UPS statistics are available on a webpage… The Arduino board also behaves like a webserver.
Code (Sans NTP +RTC Part)
#include <OneWire.h> #include "EtherShield.h" #include <Wire.h> #include <RTClib.h> //---------------defines for EtherShield---------------------- #define MYWWWPORT 80 //www server port #define BUFFER_SIZE 1200 static uint8_t buf[BUFFER_SIZE+1]; //data buffer static uint8_t mymac[6] = {0x54,0x55,0x58,0x12,0x34,0x56 }; static uint8_t myip[4] = {159,12,23,148}; static uint8_t Server1ip[4] = {159,12,23,160}; static uint8_t Server2ip[4] = {159,12,23,150}; static uint8_t ntpServer[4] = {159,12,23,150}; long server_poll_interval=30000; //query/ping server status every n seconds uint16_t print_webpage(uint8_t *buf); //function prototype EtherShield es=EtherShield(); //instantiate class //---------------defines for UPS Manager---------------------- long serialtimeout=500; //time in milliseconds to timeout serial port response long serial_poll_interval=60000; //query ups every minute int shutdown_grace=5; //time in minutes after which shutdown command will be issued to ups int ups_shut_flag=2;//1-> UPS OFF 0->UPS ON 2-> dont know long shutdown_interval=0; int server1=0; //server1 flag int server2=0; //server2 flag boolean ups_state=false; //ups flag char line_voltage[6]; char line_freq[6]; char batt_voltage[6]; char load_voltage[6]; char load_factor[6]; char batt_level[6]; char estimated_runtime[6]; char status_flag[6]; char ups_flag[6]; char response[5]; char internal_temp[6]; char external_temp[6]; char time_string[50]; //---------------defines for RTC , NTP & DS18S20 Temperature Module-------------------- OneWire ds(6); //Sensor connecd to pin 6 #define SECONDS_FROM_1900_TO_1970 2208988800 RTC_DS1307 RTC; //---------------function---------------------------------------------------------------- uint16_t http200ok(void) { return(es.ES_fill_tcp_data_p(buf,0,PSTR("HTTP/1.0 200 OK\r\nContent-Type: text/html\r\nPragma: no-cache\r\n\r\n"))); } // prepare the webpage by writing the data to the tcp send buffer uint16_t print_webpage(uint8_t *buf) { // get_RTC_time(time_string); uint16_t plen; plen=http200ok(); plen=es.ES_fill_tcp_data_p(buf,plen,PSTR("<center><p><h1>Welcome to Ishan-s UPS Shutdown Manager V0.1 </h1></p> ")); plen=es.ES_fill_tcp_data_p(buf,plen,PSTR("<hr>")); plen=es.ES_fill_tcp_data_p(buf,plen,PSTR("<table border=1 align=center>")); plen=es.ES_fill_tcp_data_p(buf,plen,PSTR("<tr><th>Server 1 Status</th><th>Server2 Status</th><th>UPS Status</th></tr>")); plen=es.ES_fill_tcp_data_p(buf,plen,PSTR("<tr><td align=center><strong>")); if(server1==1) plen=es.ES_fill_tcp_data_p(buf,plen,PSTR("ON")); else plen=es.ES_fill_tcp_data_p(buf,plen,PSTR("OFF")); plen=es.ES_fill_tcp_data_p(buf,plen,PSTR("</strong></td><td align=center><strong>")); if(server2==1) plen=es.ES_fill_tcp_data_p(buf,plen,PSTR("ON")); else plen=es.ES_fill_tcp_data_p(buf,plen,PSTR("OFF")); plen=es.ES_fill_tcp_data_p(buf,plen,PSTR("</strong></td><td align=center><strong>")); if(ups_state) plen=es.ES_fill_tcp_data_p(buf,plen,PSTR("ON")); else plen=es.ES_fill_tcp_data_p(buf,plen,PSTR("OFF")); plen=es.ES_fill_tcp_data_p(buf,plen,PSTR("</strong></td></tr>")); plen=es.ES_fill_tcp_data_p(buf,plen,PSTR("</table>")); plen=es.ES_fill_tcp_data_p(buf,plen,PSTR("<h2 align=center>UPS Status</h2>")); plen=es.ES_fill_tcp_data_p(buf,plen,PSTR("<table align=center border=1>")); plen=es.ES_fill_tcp_data_p(buf,plen,PSTR("<tr><th>Line Voltage (Volts)</th><td>")); plen=es.ES_fill_tcp_data_len(buf,plen,line_voltage,6); plen=es.ES_fill_tcp_data_p(buf,plen,PSTR("</td></tr>")); plen=es.ES_fill_tcp_data_p(buf,plen,PSTR("<tr><th>Line Frequency (Hz)</th><td>")); plen=es.ES_fill_tcp_data_len(buf,plen,line_freq,6); plen=es.ES_fill_tcp_data_p(buf,plen,PSTR("</td></tr>")); plen=es.ES_fill_tcp_data_p(buf,plen,PSTR("<tr><th>Battery Voltage (Volts)</th><td>")); plen=es.ES_fill_tcp_data_len(buf,plen,batt_voltage,6); plen=es.ES_fill_tcp_data_p(buf,plen,PSTR("</td></tr>")); plen=es.ES_fill_tcp_data_p(buf,plen,PSTR("<tr><th>Output Voltage (Volts)</th><td>")); plen=es.ES_fill_tcp_data_len(buf,plen,load_voltage,6); plen=es.ES_fill_tcp_data_p(buf,plen,PSTR("</td></tr>")); plen=es.ES_fill_tcp_data_p(buf,plen,PSTR("<tr><th>UPS Load (%)</th><td>")); plen=es.ES_fill_tcp_data_len(buf,plen,load_factor,6); plen=es.ES_fill_tcp_data_p(buf,plen,PSTR("</td></tr>")); plen=es.ES_fill_tcp_data_p(buf,plen,PSTR("<tr><th>Battery Level (%)</th><td>")); plen=es.ES_fill_tcp_data_len(buf,plen,batt_level,6); plen=es.ES_fill_tcp_data_p(buf,plen,PSTR("</td></tr>")); plen=es.ES_fill_tcp_data_p(buf,plen,PSTR("<tr><th>Internal Temp(deg C)</th><td>")); plen=es.ES_fill_tcp_data_len(buf,plen,internal_temp,6); plen=es.ES_fill_tcp_data_p(buf,plen,PSTR("</td></tr>")); plen=es.ES_fill_tcp_data_p(buf,plen,PSTR("<tr><th>External Ambient Temp(deg C)</th><td>")); plen=es.ES_fill_tcp_data_len(buf,plen,external_temp,6); plen=es.ES_fill_tcp_data_p(buf,plen,PSTR("</td></tr>")); plen=es.ES_fill_tcp_data_p(buf,plen,PSTR("<tr><th>Estimated Runtime (Minutes)</th><td>")); plen=es.ES_fill_tcp_data_len(buf,plen,estimated_runtime,6); plen=es.ES_fill_tcp_data_p(buf,plen,PSTR("</td></tr>")); plen=es.ES_fill_tcp_data_p(buf,plen,PSTR("</table>")); plen=es.ES_fill_tcp_data_p(buf,plen,PSTR("<hr>")); plen=es.ES_fill_tcp_data_len(buf,plen,time_string,50); if (plen >= BUFFER_SIZE) { plen=es.ES_fill_tcp_data_p(buf,0,PSTR("HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n")); plen=es.ES_fill_tcp_data_p(buf,plen,PSTR("<center><p><h1>Welcome to Ishan-s UPS Shutdown Manager V0.1 </h1></p> ")); plen=es.ES_fill_tcp_data_p(buf,plen,PSTR("<p><h1>Error: buffer is too small...</h1></p> ")); } return(plen); } void setup(){ pinMode(16,OUTPUT); pinMode(17,OUTPUT); digitalWrite(16,LOW); digitalWrite(17,HIGH); pinMode(5,OUTPUT); pinMode(7,OUTPUT); digitalWrite(5,HIGH); digitalWrite(7,LOW); Serial.begin(2400); Wire.begin(); RTC.begin(); // Initialise SPI interface es.ES_enc28j60SpiInit(); // initialize enc28j60 es.ES_enc28j60Init(mymac); // init the ethernet/ip layer: es.ES_init_ip_arp_udp_tcp(mymac,myip, MYWWWPORT); //calculate shutdown interval shutdown_interval=(shutdown_grace*60000)/server_poll_interval; //init serial port get_ntp_time(); } void loop(){ unsigned long starttime,servertime; int shutcount=0; uint16_t plen, dat_p; while(1) { while ((millis() - starttime > serial_poll_interval) && (ups_shut_flag!=1)){ ups_state=false; query_ups('Y',response); if (response[0]=='S' && response[1]=='M'){ query_ups('L',line_voltage); query_ups('F',line_freq); query_ups('B',batt_voltage); query_ups('O',load_voltage);query_ups('P',load_factor); query_ups('f',batt_level); query_ups('j',estimated_runtime); query_ups('Q',status_flag); query_ups('C',internal_temp); query_ups('R',response); ups_state=true; } //read TIME & temperature dtostrf(get_temp(),6,2,external_temp); starttime=millis(); } Serial.flush(); while (millis() - servertime > server_poll_interval) { server1=arp_ping(Server1ip); //ping server1 server2=arp_ping(Server2ip); //ping server2 if (server1==1 || server2==1){ shutcount=0; ups_shut_flag=0; } //do not switchoff if any of the server is alive if (server1==0 && server2==0){ shutcount++; } if ((shutcount>shutdown_interval) && ups_shut_flag!=1){ shutdown_ups();//issue powerdown command to ups } servertime=millis(); } // read packet, handle ping and wait for a tcp packet: dat_p=es.ES_packetloop_icmp_tcp(buf,es.ES_enc28j60PacketReceive(BUFFER_SIZE, buf)); /* dat_p will be unequal to zero if there is a valid http get */ if(dat_p==0){ // no http request continue; } // tcp port 80 begin if (strncmp("GET ",(char *)&(buf[dat_p]),4)!=0){ // head, post and other methods: dat_p=http200ok(); dat_p=es.ES_fill_tcp_data_p(buf,dat_p,PSTR("<h1>200 OK</h1>")); sendtcp(dat_p); } // just one web page in the "root directory" of the web server if (strncmp("/ ",(char *)&(buf[dat_p+4]),2)==0){ dat_p=print_webpage(buf); sendtcp(dat_p); } }//end of while loop }//end of main loop void sendtcp(uint16_t dat_p){ es.ES_www_server_reply(buf,dat_p); } char *query_ups(char cmd,char *temp){ int i=0; long Stime; //flush serial port Serial.flush(); //senda command on serial port Serial.print(cmd);//wakeup UPS from dumb mode Stime=millis(); delay(50); //wait for reply till timeout while(millis()-Stime<serialtimeout){ //read serial input if(Serial.available()>0){ while(Serial.available()>0){ char inChar=Serial.read(); //add it to the inputString: *(temp+i)=inChar; i++; } } } *(temp+i)='\0'; //null terminate the buffer return temp; } int arp_ping(uint8_t *remIP){ int state=0; uint32_t endtime,dat_p; uint16_t timeout=5000;//time out ping after 500 milliseconds endtime=millis(); //send an arp request to get the mac address of the remote IP es.ES_client_set_gwip(remIP); while(1){ if(millis()-endtime>=timeout){ state=0; return 0; }//time out ping int plen=es.ES_enc28j60PacketReceive(BUFFER_SIZE,buf); dat_p=es.ES_packetloop_icmp_tcp(buf,plen); if(dat_p==0){ //we idle here if(es.ES_client_waiting_gw()){ continue; } } if(!es.ES_client_waiting_gw()){ //got arp reply state=1; return 1; } } } void shutdown_ups(){ //get time from rtc clock DateTime now = RTC.now(); int hour=now.hour(); int minute=now.minute(); //execute shutdown only after 1700 if (hour>=15) if (minute>=30){ query_ups('Y',response); delay(500); query_ups('Z',response); delay(1600); query_ups('Z',response); query_ups('R',response); ups_shut_flag=1; pinMode(2,OUTPUT); pinMode(8,OUTPUT); digitalWrite(2,HIGH); digitalWrite(8,LOW); } } float get_temp(){ byte i; byte present = 0; byte type_s; byte data[12]; byte addr[8]={0x10, 0x28, 0xC, 0xBB, 0x0, 0x8, 0x0, 0xBE}; float celsius; type_s=2; //ds18s20 ds.reset(); ds.select(addr); ds.write(0x44,1); // start conversion delay(1000); present = ds.reset(); ds.select(addr); ds.write(0xBE); // Read Scratchpad for ( i = 0; i < 9; i++) {data[i] = ds.read();} // convert the data to actual temperature unsigned int raw = (data[1] <<Ishan Karve on January 8, 2012| data[0]; raw = raw << 3; // 9 bit resolution default if (data[7] == 0x10) { // count remain gives full 12 bit resolution raw = (raw & 0xFFF0) + 12 - data[6]; } celsius = (float)raw / 16.0; return celsius; } boolean get_ntp_time(){ uint32_t endtime,d; boolean stat=false; uint16_t timeout=3000; //timeout ping after 2 seconds endtime=millis(); uint16_t dat_p; uint16_t clientPort = 123; char dstr[4]; int sec = 0; int plen = 0; // Get IP Address details // Main processing loop now we have our addresses while(1) { if(millis()-endtime>=timeout){return false;} // handle ping and wait for a tcp packet dat_p=es.ES_packetloop_icmp_tcp(buf,es.ES_enc28j60PacketReceive(BUFFER_SIZE, buf)); // Has unprocessed packet response if (dat_p > 0) { uint32_t time = 0L; if (es.ES_client_ntp_process_answer(buf,&time,clientPort)) { if (time) { time -= SECONDS_FROM_1900_TO_1970; //calculate no of seconds after 2000 time +=19800; //add offset of +5hr 30 min for IST (Indian Standard Time) DateTime now(time); DateTime RTC_now = RTC.now(); //sync RTC to NTP RTC.adjust(DateTime(now.year(),now.month(),now.day(),now.hour(),now.minute(),now.second())); return true; } } } if (stat==false){ if (set_mac(ntpServer)) { es.ES_client_ntp_request(buf,ntpServer,123);}//reset timeout counter stat=true; } } } boolean set_mac(uint8_t *remIP){ uint32_t endtime,dat_p; uint16_t timeout=3000; //timeout ping after 500 milli seconds endtime=millis(); es.ES_client_set_gwip(remIP); while(1){ if(millis()-endtime>=timeout){return false;}//timeout ping int plen=es.ES_enc28j60PacketReceive(BUFFER_SIZE,buf); dat_p=es.ES_packetloop_icmp_tcp(buf,plen); if(dat_p==0) { // we are idle here if (es.ES_client_waiting_gw() ){ continue; } } if (!es.ES_client_waiting_gw() ){return true;} } }
Arduino NTP Client using EtherShield library
January 4th, 2012 | 59 Comments »
This code is snippet of a larger project I am working on. The project requires time synchronization so I decided to use NTP to retrieve time from server as the project hardware (Arduino ATMega328 board + ENC28J60 based shield) already had the the QR to execute the requirement.
The NTP server is a Windoze 2008 R2 server and my sketch is based on one published on github. However some tweaks have been done to make it compatible with my arduino board.
The sketch is for a simple network and does not cater for routers, firwall gateways and works..
Here are some of the salient changes.
- Static IP being used.
- Timestamp offset corrected to 1970 instead of 2000.
- set_mac() function added to set correct mac address of the destination ntp server.. Without this the packets are sent with destination mac address as 00:00:00:00:00:00
- A positive offset of 19800 seconds has been added to get Indian Standard Time.
#include <Wire.h> #include <RTClib.h> #include <EtherShield.h> // Jan 1 #define SECONDS_FROM_1900_TO_1970 2208988800 static uint8_t mymac[6] = { 0x54,0x55,0x58,0x10,0x00,0x25}; // IP and netmask allocated by DHCP static uint8_t myip[4] = { 159,12,23,149 }; static uint8_t mynetmask[4] = { 255,255,255,0 }; static uint8_t gwip[4] = {159,12,23,150}; // From http://support.ntp.org/bin/view/Servers/StratumTwoTimeServers byte ntpServer[4] = {159,12,23,150}; // Packet buffer, must be big enough to packet and payload #define BUFFER_SIZE 550 static uint8_t buf[BUFFER_SIZE+1]; EtherShield es=EtherShield(); uint32_t lastUpdate = 0; uint32_t time; static const char day_abbrev[] PROGMEM = "SunMonTueWedThuFriSat"; void setup(){ Serial.begin(19200); Serial.println("EtherShield NTP Client"); // Initialise SPI interface es.ES_enc28j60SpiInit(); // initialize enc28j60 es.ES_enc28j60Init(mymac); es.ES_init_ip_arp_udp_tcp(mymac,myip,80); Serial.print( "ENC28J60 version " ); Serial.println( es.ES_enc28j60Revision(), HEX); if( es.ES_enc28j60Revision() <= 0 ) { Serial.println( "Failed to access ENC28J60"); while(1); // Just loop here } // init the ethernet/ip layer // es.ES_init_ip_arp_udp_tcp(mymac,myip, 80); // es.ES_client_set_gwip(gwip); // e.g internal IP of dsl router lastUpdate = millis(); } // Output a ip address from buffer from startByte void printIP( uint8_t *buf ) { for( int i = 0; i < 4; i++ ) { Serial.print( buf[i], DEC ); if( i<3 ) Serial.print( "." ); } } void loop(){ uint16_t dat_p; uint16_t clientPort = 123; char dstr[4]; int sec = 0; int plen = 0; // Get IP Address details // Main processing loop now we have our addresses while(1) { // handle ping and wait for a tcp packet dat_p=es.ES_packetloop_icmp_tcp(buf,es.ES_enc28j60PacketReceive(BUFFER_SIZE, buf)); // Has unprocessed packet response if (dat_p > 0) { time = 0L; if (es.ES_client_ntp_process_answer(buf,&time,clientPort)) { Serial.print("Time:"); Serial.println(time); // secs since year 1900 if (time) { time -= SECONDS_FROM_1900_TO_1970; //calculate no of seconds after 2000 time +=19800; //add offset of +5hr 30 min for IST (Indian Standard Time) DateTime now(time); Serial.print(now.year(), DEC); Serial.print('/'); Serial.print(now.month(), DEC); Serial.print('/'); Serial.print(now.day(), DEC); Serial.print(' '); int i=0; while (i<3){ dstr[i]= pgm_read_byte(&(day_abbrev[(now.dayOfWeek()-1) * 3 + i])); i++; } dstr[3]='\0'; Serial.print( dstr ); Serial.print(' '); Serial.print(now.hour(), DEC); Serial.print(':'); Serial.print(now.minute(), DEC); Serial.print(':'); Serial.print(now.second(), DEC); Serial.println(); } } } // Request an update every 5s if( lastUpdate + 5000L < millis() ) { // time to send request lastUpdate = millis(); Serial.print("Send NTP request "); if (set_mac(ntpServer)) es.ES_client_ntp_request(buf,ntpServer,clientPort); } } } boolean set_mac(uint8_t *remIP){ uint32_t endtime,dat_p; uint16_t timeout=2000; //timeout ping after 500 milli seconds endtime=millis(); Serial.print("Senidng ARP Request to ");printIP(remIP);Serial.println(); es.ES_client_set_gwip(remIP); while(1){ if(millis()-endtime>=timeout){Serial.print("Timeout Waitin for Ping Reply ");Serial.print(endtime);Serial.println();return false;}//timeout ping int plen=es.ES_enc28j60PacketReceive(BUFFER_SIZE,buf); dat_p=es.ES_packetloop_icmp_tcp(buf,plen); if(dat_p==0) { // we are idle here if (es.ES_client_waiting_gw() ){ continue; } } if (!es.ES_client_waiting_gw() ){Serial.println("Got ARP Reply");return true;} } }Ishan Karve on January 4, 2012
Sketch to ping remote computer using Arduino (Using EtherShield Library and an ENC28J60 Shield)
December 26th, 2011 | No Comments »
Well over the past weekend I had been scouring the net trying to get a cue as to how to ping a remote computer using an Arduino Duemilanove and an ENC28J60 based shield (I am using SureElectronics Ethernet Communication Module USB SPI – ENC28J60 Remote Control).

SureElectronics Ethernet Shield
After hours of Wiresharking my code and scouring the Ethershield library for possible cues I was finally able to ping a remote computer
Without much ado here is a dump of the sketch. I have used Andy’s EtherShield library.
Caution: -To make this sketch work following changes are required in ip_arp_udp_tcp.c file in the Ethershield library.
Replace
buf[IP_TOTLEN_L_P]=0x82;//gives error when viewed in wireshark
By
buf[IP_TOTLEN_L_P]=0x54;
Replace
buf[IP_PROTO_P]=IP_PROTO_UDP_V;;//sends packet as UDP instead of ICMP
By
buf[IP_PROTO_P]=IP_PROTO_ICMP_V;
The stuff works, but it stops sending packets after 3rd iteration. I have not resolved the problem.(Solved. Problem was in variable type definition of endtime. Had defined it as uint16_t instead of uint32_t.)
#include
#define PING_client
static uint8_t mymac[6] = {0x54,0x55,0x58,0x10,0x00,0x25};
static uint8_t myip[4] = { 172,16,100,25 };
static uint8_t pcip[4] = { 172,16,100,2 };
static uint8_t gwip[4] = { 172,16,100,1 };
static uint8_t fooip[4] = { 172,16,100,100 }; //unavailable pc. simulating a offline pc
#define BUFFER_SIZE 700
static uint32_t timer,count;
static uint8_t buf[BUFFER_SIZE+1];
int state=0;
EtherShield es=EtherShield();
void setup(){
Serial.begin(19200);
// Initialise SPI interface
es.ES_enc28j60SpiInit();
// initialize enc28j60
es.ES_enc28j60Init(mymac);
// init the ethernet/ip layer:
es.ES_init_ip_arp_udp_tcp(mymac,myip,80);
Serial.println("Starting"); delay(3000);
}
// Output a ip address from buffer from startByte
void printIP( uint8_t *buf ) {
for( int i = 0; i < 4; i++ ) {
Serial.print( buf[i], DEC );
if( i<3 )
Serial.print( "." );
}
}
void printHex( uint8_t hexval ) {
if( hexval < 16 ) {
Serial.print("0");
}
Serial.print( hexval, HEX );
Serial.print( " " );
timer = -9999999; // start timing out right away
}
void loop(){
pinMode(14, OUTPUT); // sets the digital pin as output
pinMode(15, OUTPUT); // sets the digital pin as output
digitalWrite(14,HIGH);
digitalWrite(15,LOW);
ping(pcip);
ping(gwip);
ping(fooip);
}
void ping(uint8_t *remIP){
uint32_t endtime,dat_p;
uint16_t timeout=5000; //timeout ping after 2seconds
endtime=millis();
//send an arp request to get the mac address of teh remoteIP
// Serial.print("Senidng ARP Request to ");printIP(remIP);Serial.println();
es.ES_client_set_gwip(remIP);
while(1){
if(millis()-endtime>=timeout){Serial.print("Timeout Waitin for Ping Reply ");Serial.print(endtime);Serial.println();state=0;break;}//timeout ping
int plen=es.ES_enc28j60PacketReceive(BUFFER_SIZE,buf);
dat_p=es.ES_packetloop_icmp_tcp(buf,plen);
if(dat_p==0) {
// we are idle here
if (es.ES_client_waiting_gw() ){
continue;
}
}
// if (!es.ES_client_waiting_gw() ){Serial.println("Got ARP Reply");state=1;break;}
// report whenever a reply to our outgoing ping comes back
if (plen > 0 && es.ES_packetloop_icmp_checkreply(buf,remIP)) {
Serial.print(" ");
Serial.print((micros() - timer) * 0.001, 3);
Serial.println(" ms");
state=1;break;
}
if (micros() - timer >= 1000000) { //ping every 1 second
Serial.print(count);
Serial.print(". Pinging: ");
printIP(remIP);
Serial.print("-->");
timer = micros();
es.ES_client_icmp_request(buf,remIP);
count++;
}
}
}
Ishan Karve on December 26, 2011
Simple Python Script To Mimic APC UPS
December 19th, 2011 | No Comments »
Just written this script to simulate a APC SMART UPS for one of my ongoing projects.
#! /usr/bin/env python
"""\
The purpose of this program is to simulate an APC UPS
It will respond to only 'basic' UPS commands
Presently only following commands are supported
YBFLOPR
"""
import datetime
import serial
import glob
import sys
name="/dev/ttyUSB0" # change to suit your needs
ser = serial.Serial(name, 2400, timeout=1)
print "Trying to open" + name
if (ser.isOpen()) :
print "Sucessfully opened port " + name
print "Waiting for command on serial port"
#loop start
while 1 :
timenow= datetime.datetime.now().strftime("%I:%M:%S")
cmd=ser.read() #read a single byte
if len(cmd) != 0 :
print timenow + "--> Received command-> " + cmd
#change output based on received command
if cmd == 'Y':
print timenow + "--> Sending Reply SM"
ser.write("SM\r\n")
elif cmd == 'B':
print timenow + "--> Sending Reply 27.87"
ser.write("27.87\r\n")
elif cmd == 'F':
print timenow + "--> Sending Reply 50.00"
ser.write("50.00\r\n")
elif cmd == 'L':
print timenow + "--> Sending Reply 118.3"
ser.write("118.3\r\n")
elif cmd == 'O':
print timenow + "--> Sending Reply 118.3"
ser.write("118.3\r\n")
elif cmd == 'P':
print timenow + "--> Sending Reply 023.5"
ser.write("023.5\r\n")
elif cmd == 'f':
print timenow + "--> Sending Reply 099.0"
ser.write("099.0\r\n")
elif cmd == 'j':
print timenow + "--> Sending Reply 00327:"
ser.write("0327:")
elif cmd == 'R':
print timenow + "--> Sending Reply BYE"
ser.write("BYE\r\n")
print "-----------------------------------------------------"
ser.close()
Ishan Karve on December 19, 2011
IR Remote Controlled 4 Channel Relay
November 25th, 2011 | No Comments »
Code to read IR Signal from a Sony Remote and trigger a relay.
Code adapted from
Compiler : CCS C Compiler
Target MCU: Microchip PIC 16F628A
#include
#fuses INTRC_IO, NOWDT, BROWNOUT, NOLVP
#use delay(internal=4MHz)
#define LED PIN_B4
#define IR PIN_B3
#define RELAY1 PIN_A2
#define RELAY2 PIN_A3
#define RELAY3 PIN_B1
#define RELAY4 PIN_B2
/* TIMER0 configuration */
#define TIMER0_CONFIG RTCC_INTERNAL | RTCC_DIV_1
/* Interrupt rate: */
/* 4/4000000*65536*1 = 0.256 ms */
/* */
/* Start: 3.0 ms (ignored) */
/* "1": 1.8 ms (225) */
/* "0": 1.2 ms (150) */
/*
*/
#define ONE_MIN 190
#define ONE_MAX 400
#define ZERO_MIN 10
#define ZERO_MAX 185
short rly1,rly2,rly3,rly4,state;
/* irframes[0] (start) will be garbage, ignore it... */
int16 irframes[13];
int8 ircount = 0;
int1 irdone = FALSE;
#int_ccp1
void ext_ccp1() {
if (irdone) return;
irframes[ircount++] = get_timer0();
if (ircount >= 13)
irdone = TRUE;
set_timer0(0);
enable_interrupts(INT_TIMER0);
//#output_bit(LED,1);delay_ms(1);output_bit(LED,0);delay_ms(1);
}
#int_timer0
void timer0_isr() {
disable_interrupts(INT_TIMER0);
}
#separate
int1 decode_ir(int8 &addr, int8 &cmd) {
int8 i;
int8 mask;
int8 bits[13];
addr = 0;
cmd = 0;
for (i=1; i
Ishan Karve on November 25, 2011














