Karve Family Trees

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 Launchpad: Read Pulse Widths Using Interrupts

April 26th, 2012 | 3 Comments »

Print Friendly

Small code to read pulse widths of square waves using interrupts.Uses hardware UART module for serial communication and interrupt on P2.0. Connect incoming signal on P2.0  Serial functions are borrowed from Nathan Zimmerman hardware UART example.

Hardware :TI MSP430G2 Launchpad

Software :Code Composer Studio Version: 4.2.3.00004

 aewFile1 realterm screen cap

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
/* ----------------------------------------------------------------------
 * 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 <msp430g2553.h>
#include <stdbool.h>
////////////////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_2 + SCS + CCIS_0 + CAP + CCIE; // falling 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 == 30)
    {
        P1OUT^=LED0;//toggle led
        //turnoff timer 1
        TA1CTL = MC_0;
        //dump samples
        for (j=0;j<=i;j++) //exclude bit0 as it is mots likey erroneous
        {
            itoa(j, charbuffer, 10);
            TX(charbuffer);
            TX("-->");
            itoa(capture_array[j], charbuffer, 10);
            TX(charbuffer);
            TX("\r\n");
        }
        TX("------------------\r\n");
        first_pulse=0;
        i=0;
        //start timer
        TA1CCTL0 |= CM_1;
        TA1CTL = TASSEL_2 + MC_2;                  // SMCLK + Continuous Mode
    }
    pre_cap = tick;                       // store this capture value
    here:
    P1OUT^=LED1;
}
Ishan Karve on April 26, 2012

Dynamic Water Pump Controller

April 4th, 2012 | 1 Comment »

Print Friendly

This current project of mine which I am documenting here has been selected for the TI MCU Contest. The project idea itself was half cooked and I aborted a similar project last year due to lack of motivation. You can see the remnants here
This post will be progressive and will document the project as it achieves fruition. Since the project has been selected for the contest and TI has been gracious enough to supply me with their Launchpad development tool and samples of their instrumentation amplifier, TI based components would feature and used primarily for this project. Another significant thing that I intend to do is explain the theory and the rational behind component selection, this is something that I rarely see online.

The project aims to build a dynamic water pump controller, that monitors a tank of water and supply line continuously and keeps the tank topped up.

Block Diagram of Dynamic Water Level Controller

Selection of Pressure Sensor: The sensor I have selected is a differential pressure sensor. A differential pressure sensor has generally two ports and are used to measure pressure differences. In this case one port will be left open to atmosphere, while the other port would be connected to the drain/supply line. The advantage is that such an arrangement eliminates variation in pressure due to changes in atmospheric pressure.
Now lets delve into the mathematics of pressure .
Static Pressure exerted by a still column of water is given by the equation

Pressure(P)= fluid density (?) x gravitational acceleration(g) x height of fluid column(h).

Hence pressure exerted by a water tank of 2 meters height =
P=1,000 kg/m3 x 9.81 m/s2 x 2 = 19.62 kPa (kiloPascals)

Now above is a very basic equation valid only for a sensor placed at the bottom of a tank. In real world scenario it is quite likely that the sensor will be connected at the end of a pipe. Lets consider the following case.

A 2 meter high filled water tank is placed on the roof of a building 30 ft high. The sensor is placed at the bottom of the tank on a 1in dia pipe.

In such a case above equation are no longer valid as the head changes. I am still lingering in the realm of static pressure and have not catered for dynamic pressure.
The new water head= 2 mtr + 30 ft =11.14 mtr.
Hence pressure exerted = 1,000 kg/m3 x 9.81 m/s2 x11.14 = 109.2834 kPa (kiloPascals)
Hence I have selected MPX2100 from Freescale Semi for the sensing pressure from the main tank.
Selection of Micro-controller: This is a no-brainer. Since the contest is sponsored by guys at Texas Instruments and they have shipped me a MSP430 development board ‘Launchpad’. I already have 3 launchpad boards and this one adds one more to their company.

Amplifier Design: The MPX2xxx series differential pressure sensors can be approximated to a resistor (Wheatstone) bridge. Hence the output is a differential voltage. The span of the differential voltage demands a sensitive instrumentation amplifier. Ti guys have been kind to ship me samples of their instrumentation amplifiers. To keep the power supply or the circuit simple, I would be designing my circuit around a single supply instrumentation amplifier.

Characteristics of MPX2010 and MPX2100:

MPX2100DP 
Excitation Voltage Min Span (mV) Max Span (mV) Min Offset (mV) Max Offset (mV)
10 V 38.5 40 -1 1
5 V (scaled) 19.25 20 -0.5 0.5
9 V (scaled) 34.65 36 -0.9 0.9
12 V (scaled)  45.6  48  -1.2  1.2
15 V (scaled) 57.75 60 -1.5 1.5
MPX2010DP
Excitation Voltage Min Span (mV) Max Span (mV) Min Offset (mV) Max Offset (mV)
10 V 24 26 -1 1
5 V (scaled) 12 13 -0.5 0.5
9 V (scaled) 21.6 23.4 -0.9 0.9
12 V (scaled)  28.8  31.2  -1.2  1.2
15 V (scaled) 36 39 -1.5 1.5

Offset and Span considerations:
What is Span? Span, put simply is the the max output of the sensor minus the min input.
What is Offset?Offset is the output of the sensor without any applied stimulus. For our sensor according to data-sheet Offset is defined as the output voltage at the minimum rated pressure.

Considering the analog input characteristics of MSP430 devices, the desired offset is 0.5V and the desired span is 3V. 3V is the maximum analog input voltage for MSP430G2x53 devices.

Gain Calculation:

Maximum Gain (GMax) = Desired Span(V) ?Sensor’s Minimum Span
Maximum Gain (GMin)= Desired Span(V) ?Sensor’s Minimum Span

Gain Calculations 
Excitation Voltage MPX 2010 GMax MPX2010 GMin MPX 2100 GMax MPX 2100 GMin
10 V 125 115 78 52
5 V (scaled) 250 231 156 104
9 V (scaled) 139 128 87 58
12 V (scaled) 104 96 65 53
15 V (scaled) 83 77 52 35

As I mentioned earlier, to keep the overall circuit simple and costs low, I would be using a single supply instrumentation amplifier from Texas Instruments. I would be using INA122 from TI for the project.
For INA122, Gain (G) = 5 + (200K ?RG). RG is a gain setting resistor.

Now lets calculate values of RG for different gain settings
RG=200k?(G-5)

RG Values 
Excitation Voltage 10V 5V 9V 12V 15V
MPX2010 1.67K? 820? 1.49K? 2.02K? 2.6K?
MPX2100 2.74K? 1.32K? 2.44K? 3.33K? 4.3K?

 

RG Values approximated to nearest std. resistor values 
Excitation Voltage 10V 5V 9V 12V 15V
MPX2010 1.6 K? 820 ? 1.5 K? 2 K? 2.7 K?
MPX2100 2.7 K? 1.3 K? 2.4 K? 3.3 K? 4.3 K?

Schematic Diagram for sensor-amplifier interface (Test Setup)

Final Schematic of The Wireless Tank Sensor

Eagle Schematic of the tank Senor

PCB Board Layout of the tank Senor

 

 

Man Machine Interface I would be using a 20×4 HD44780 LCD display and a detented rotary encoder for making the man machine interface. This interface This I anticipate would take bulk of my coding time. There are two major obstacles that I foresee in front of me. The first is the the number of pins available on microcontroller to interface the LCD. 20x4 HD44780 LCD Display

Rotary EncoderThe LCD in 4 bit mode requires a minimum of 6 pins to interface with a MCU and the rotary encoder will use 3. Thus ,using 9 pins from 16 available is going to cause an acute shortage of pins for any additional use.  However yesterday I successfully interfaced the MCU using a shift register 74HC164. This has had 2 major positive impacts. The code overhead required for LCD routines has decreased significantly and also has caused reduction in number of pins from 6 to 3.

PCB Board Layout of LCD Interface

 

 

 

 

 

 

 

First obstacle ..overcome!

The second obstacle is wring the routine for the menu itself. This is going to be a long and buggy process as I would require the MCU to be ‘state aware’ and thus write a state machine for menu function.

13 Apr 12

Ok.. now that the sensor has been tried and tested , its time to look at the second obstacle ..designing the menu. I used an online mindcharting toold at bubbls.us to come out with the following flowchart

 

 

 

..to be continued

ishan on April 4, 2012

Fun with Interrupts : Decoding Quadrature Encoders on TI Launchpad MSP-EXP430G2

February 14th, 2012 | 2 Comments »

Print Friendly

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.

Sureelectronics  Rotary Encoder Switch With Keyswitch

Sureelectronics Rotary Encoder Switch With Keyswitch

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, 2012

Fun with Timers : Decoding an Sony SIRC protocol on TI Launchpad MSP-EXP430G2

February 13th, 2012 | No Comments »

Print Friendly

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 &gt; 0) a = i2a(i/r,a,r);
*a = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"[i%r];
return a+1;
}

char *itoa(int i, char *a, int r)
{
if ((r &lt; 2) || (r &gt; 36)) r = 10;
if (i &lt; 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&gt;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 &amp;= ~UCSWRST; // Start USCI
////////////////General GPIO Defines////////////////
P1DIR |= (LED0 + LED1+GND+VCC); //define output ports
P1OUT &amp;= ~(LED0 + LED1+GND); //turn ports low
P1OUT|=VCC;
P2IE |= DAT;
P2IFG &amp;= ~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;
}

Pulse Timings

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.

Pulse Timings

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 &gt; 0) a = i2a(i/r,a,r);
*a = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"[i%r];
return a+1;
}

char *itoa(int i, char *a, int r)
{
if ((r &lt; 2) || (r &gt; 36)) r = 10;
if (i &lt; 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&gt;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 &amp;= ~UCSWRST; // Start USCI
////////////////General GPIO Defines////////////////
P1DIR |= (LED0 + LED1+GND+VCC); //define output ports
P1OUT &amp;= ~(LED0 + LED1+GND); //turn ports low
P1OUT|=VCC;
P2IE |= DAT;
P2IFG &amp;= ~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 &gt;=29000) &amp;&amp; (cap =1190) &amp;&amp; (cap =1790) &amp;&amp; (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;
}

 

Timings Interpreted

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 &gt; 0) a = i2a(i/r,a,r);
*a = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"[i%r];
return a+1;
}

char *itoa(int i, char *a, int r)
{
if ((r &lt; 2) || (r &gt; 36)) r = 10;
if (i &lt; 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&gt;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 &amp;= ~UCSWRST; // Start USCI
////////////////General GPIO Defines////////////////
P1DIR |= (LED0 + LED1+GND+VCC); //define output ports
P1OUT &amp;= ~(LED0 + LED1+GND); //turn ports low
P1OUT|=VCC;
P2IE |= DAT;
P2IFG &amp;= ~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 &gt;=29000) &amp;&amp; (cap =1190) &amp;&amp; (cap =1790) &amp;&amp; (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 &lt; }

mask = 0x01;
for (j=7; j if (capture_array[j])
addr = addr | mask;
mask &lt; }
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;
}

Interpreting Bit Stream

Download Project Files

Following Links have provided me valuable insight to help me understand the topic

  1. http://processors.wiki.ti.com/index.php/MSP430_LaunchPad_LED_Timer
  2. http://www.msp430launchpad.com/2010/07/timers-and-clocks-and-pwm-oh-my.html
  3. http://www.ti.com/lit/ml/slap113/slap113.pdf
  4. http://www.ccs.neu.edu/home/noubir/Courses/CSU610/S07/MSP430-Clock-Timers.pdf
  5. http://picprojects.org.uk/projects/sirc/sonysirc.pdf
  6. http://www.43oh.com/forum/viewtopic.php?f=9&t=358
  7. http://www.43oh.com/wp-content/uploads/2010/10/Lanchpad_Maxbotix_Ultrasonic_Rangefinder_MSP430G2211_main.c
  8. http://forum.sparkfun.com/viewtopic.php?t=8443
Ishan Karve on February 13, 2012

Getting started with Cygwin.

February 7th, 2012 | 19 Comments »

Print Friendly

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.

 


Step 9.  Thats all.

 

 

 

 

Ishan Karve on February 7, 2012

Fun with TI Launchpad : Read LM35 sensor using Launchpad MSP430G2 (MSP430G2553)

February 6th, 2012 | 15 Comments »

Print Friendly

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;
}
Ishan Karve on February 6, 2012

Fun with interrupts on MSP-EXP430FR5739 FRAM Experimenter Board

January 23rd, 2012 | No Comments »

Print Friendly

Just a small program coded for learning interrupts. Code is commented and self explanatory.

Hardware :TI MSP-EXP430FR5739 FRAM Experimenter Board

Target 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
    }

 }
Ishan Karve on January 23, 2012

APC UPS Shutdown Manager

January 8th, 2012 | 18 Comments »

Print Friendly

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.

RTC Module

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.

Connection Matrix

 

 

 

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] << 8) | 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;}

}

}
Ishan Karve on January 8, 2012

Arduino NTP Client using EtherShield library

January 4th, 2012 | 63 Comments »

Print Friendly

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.

  1. Static IP being used.
  2. Timestamp offset corrected to 1970 instead of 2000.
  3. 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
  4. 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;}

}

}

John Mark Osin from Philippines went one step ahead and created a beautiful working NTP clock for his college as part of his project. Here are some snaps of his fantastic build

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 »

Print Friendly

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