Create iPhone App and Pass Data Using WiFi to the Chipkit Uno32
http://www.box.com/s/89d848f369d9fb09e762Part 2 will use the iPhone app we created in Part 1, to pass data using UDP to the Digilent PmodWiFi module plugged into there new ChipKit Pmod Shield-Uno.
I will also show how to make some mods to the iPhone app that can turn leds on and off and display a analog input from the Uno32.
Well the ChipKit Pmod Shield-Uno board and the Wireless PmodWiFi module has arrived.
Digilent Pmod Shield-Uno Board
http://digilentinc.com/Products/Detail.cfm?NavPath=2,892,993&Prod=CHIPKIT-PMOD-SHIELD-UNO
Digilent PmodWiFi Module
http://digilentinc.com/Products/Detail.cfm?NavPath=2,401,884&Prod=PMOD-WIFI
One thing to note with Digilent Products, at the bottom of the page they provide download links for Schematics, Reference Manuals and Libraries.
The Pmod Shield-Uno board has the same form factor as the Uno32. After reviewing the reference manual, the Pmod Shield has five 2x6 Digilent Pmod connections, one 6 pin header for SPI and one I2C daisy chain connector.
Both of the I2C interfaces are available using the Pmod Shield. They even provide Jumpers JP10 and JP11 to disable the on-board pull-ups if they are not needed or some other I2C device is providing the pull-ups. Later on we are going to use the I2C to interface with the Pmod 3-axis Gyro.
The other interface we are interested in, is the SPI interface. We are going to interface to the Pmod WiFi module using SPI. The Pmod WiFi module uses Microchip's MRF24WB0MA Wi-Fi radio transceiver module. The Pmod WiFi module provides IEEE 802.11b/g/n support at 1 and 2 Mbps. The connector J1 on the Pmod WiFi modules provides all the signals you will need.
The Digilent Pmod WiFi modules also comes with 2 12-pin cables and 3 headers for remotely locating the Pmod WiFi module if desired. This is a nice feature knowing the cables and headers come with the module. I chose to plug the Pmod WiFi module directly into the Pmod Shield. Digilent also provides the Digilent ChipKit WiFi Network Library. The Library includes examples for using the Pmod WiFi. This takes all the guess work out of connecting and using the Pmod WiFi module.
Lets get started by mating the Pmod Shield to the Uno32.
Be sure to POWER OFF the Uno32 until all the connections have been made.
On the Uno32 board remove the Jumpers JP6/JP8. Place the Pmod Shield board on the Uno 32. Replace the Jumpers previously removed and place them on JP6/JP8 in positions RG3/RG2. This configurations supports I2C communication instead of Pins A4/A5 as analog in on connector J7. The Uno32 reference manual has detailed images and great documentation for jumper selections.
Next, Plug the PmodWiFi module into the JC connector with the top of the PC board facing up. I determined this was the best connector with the required signals, after reading the reference manuals for both the Pmod Shield and the PmodWiFi module. The reference manual for the Pmod Shield has some nice tables listing all the pinouts. The tables show the Uno32 Pins, PIC32 Pins and JC connector pins. Its a nice cross reference that takes away any guess work.
The Pmod WiFi requires 3.3v so make sure jumper JPC is in the VCC 3V3 position. You will find that jumper close to the JC connector. Every port has a jumper to select VCC 3V3 or 5V0. It would have been nice to have a layout picture for the Pmod Shield showing all the positions of connectors and jumpers like they provide for the Uno32 and Max32.
At this point we need to download the Digilent ChipKit WiFi Network Library.
http://digilentinc.com/Products/Detail.cfm?NavPath=2,401,884&Prod=PMOD-WIFI
At the bottom of the page you will find the download link for the Library in Support Documents:
After downloading and unzipping the file you should see the following:
At this point copy the "DWIFIck" folder and its contents. Remember I'm on a Mac so this will be somewhat different if your on a PC. Go to your "MPIDE" folder, open it. You should find a folder called "Libraries". Paste the "DWIFIck" into the "Libraries" folder. Open the "libraries" folder and you should see the "DWIFlck" folder and the other Networking folders we installed in Part 1.
Now close your MPIDE app and reopen MPIDE. You should be able to see the DWIFlck Library.
Now that the wireless library is installed, we need to set the Pmod WiFi module into adhoc mode. We can do this by modifying the WF_Config.h file. The file is located on my mac computer at: /Users/<username>/documents/mpide/libraries/DWIFlck/utility/WF_Config.h
Open the file and search for:
MY_DEFAULT_NETWORK_TYPE
Change from WF_INFRASTRUCTURE to WF_ADHOC. Save and exit the file.
Lets open the example 'UDPEchoServer.pde'.
You can find it here:
Once you have the file open, do a 'save as...' , name the file UDPEchoServerAdhoc.pde.
Scroll down in the file until you see 'IPv4 ipServer = {192,168,1,190}'.
Change the ip address to 169,254,0,1
I also changed the SSID to 'Pmod WiFi'.
We also need to change the security to 'None'. Do this by commenting out any of the security #defines.
Now we should have everything changed and ready to test it.
Be sure to save the sketch
.
Upload this file to the Uno32. When it done uploading, bring up the serial monitor and you should see the following:
On the iPhone, go to 'settings', 'Wi-Fi'. Look under 'Choose a Network' for the 'Pmod WiFi' network. Select the Pmod WiFi network.
Now return to the iPhone application. Make sure the Address is 169.254.0.1 and the port is 44400. Enter some words into the Results field, press 'Send'. The results field should display Results - <plus what you typed>.
At this point the iPhone is sending data using the Wireless Network 'Pmod WiFi' to the Uno32 using the Pmod WiFi module.
Lets make some changes to the iPhone app. Lets add 8 switches to control the 8 Leds on the Digilent Basic I/O shield. We can also add a meter interface on the iPhone to display the analog input AN0 which has a pot connected to it on the Digilent Basic I/O shield.
I'll make the mods the easy way by just modifying the existing iPhone app. You might want to save a copy of the original app before you start modifying.
Go ahead and delete the labels and text fields for the IP address and the port Number. Add 8 switches and 8 labels. Lets also add a label and a text field to display the data from AN0. I also moved the results field, label and send button to the bottom of the screen for now. We really wont be using the 'Send' button but maybe later. I also changed the background color to a light blue color.
Make sure all your switches are in the 'OFF' position. In the sketch we are going to be turning all the Leds 'OFF' in setup(). In Xcode you will find a property called 'State' in the attributes inspector to set the switches in the 'OFF' state.
Now we need to connect our 8 switches. The easiest way to do this is bring up the .xib file in xcode, click on the assistant editor button. This brings up the .xib file along with the interface file(JLCViewController.h). Now holding down the ctrl key, click on Led 1's switch and hold down the mouse button and drag over to the JLCViewController.h file. It should bring up a message that displays the following: "insert outlet, action, outlet connection". Release the mouse button and the following should be displayed:
Change connection to 'Action', input 'led1' for the name. I had already done led1 so mine displays 'led2'. Do this same thing for all 8 Led switches.
When you are finished you should have the following:
Now lets go to the implementation file(JLCViewController.m) and you should see the following:
Lets create a simple protocol msg that each switch will send that will turn on or turn off an led.
'L' - LED
'N' - Replace 'N' with 1 thru 8 for the 8 switches.
'1' - On, '0' - Off
So 'L10' would turn off Led 1. 'L11' would turn on Led 1.
and 'L41' would turn on Led 4.
In each action function for the switches 1 - 8 we will want the following code:
- (IBAction)led1:(id)sender
{
NSString *msg;
if ( [sender isOn])
{
msg = @"L11";
}
else
{
msg = @"L10";
}
[self sendLedData:msg];
}
The code is creating a string(msg) for the Led message, then checking to see if the switch is 'ON' or 'OFF', then it loads 'msg' with our short protocol message. Then we call the 'sendLedData' function passing the 'msg' message. The led message will then be sent over our wireless network.
Be sure to change the statement 'msg = @"L11" and 'msg = @"L10" for each Led 1 - 8.
Now we need to create the function 'sendLedData'.
- (void)sendLedData:msg
{
NSLog(@"Sending led Data%@",msg);
//Setup the host ip address
NSString *host = @"169.254.0.1";
//Setup the port number
int port = [@"44400" intValue];
//Check for a valid message
if ([msg length] == 0)
{
return;
}
NSData *data = [msg dataUsingEncoding:NSUTF8StringEncoding];
[udpSocket sendData:data toHost:host port:port withTimeout:-1 tag:tag];
}
This completes the app for turning Leds off and on. Let modify the receiving sketch and test the code we have created. Then we will proceed with receiving some data from the analog port AN0 on the Uno32.
Lets go back to MPIDE and the sketch.
One thing I would like to mention at this point. There are many ways to write code. I tried to write this code so it was very easy to understand. You could optimize this code by rewriting it, but that usually makes it harder for people to understand when they are learning something new.
If you look in the documentation for the Basic I/O shield, you will see the LED's are on Pins 26 - 33. So we need to set those pins to be an output. The statement for that is 'pinMode(26, OUTPUT)'. Lets create some define's for the Led's pin number. Its lots easier to remember LED1 than Pin 26.
#define LED1 26
#define LED2 27
#define LED3 28
#define LED4 29
#define LED5 30
#define LED6 31
#define LED7 32
#define LED8 33
Also add the following to set the outputs for the Leds in setup().
pinMode(LED1,OUTPUT);
pinMode(LED2,OUTPUT);
pinMode(LED3,OUTPUT);
pinMode(LED4,OUTPUT);
pinMode(LED5,OUTPUT);
pinMode(LED6,OUTPUT);
pinMode(LED7,OUTPUT);
pinMode(LED8,OUTPUT);
We should also set all the Leds off in setup().
digitalWrite(LED1,LOW);
digitalWrite(LED2,LOW);
digitalWrite(LED3,LOW);
digitalWrite(LED4,LOW);
digitalWrite(LED5,LOW);
digitalWrite(LED6,LOW);
digitalWrite(LED7,LOW);
digitalWrite(LED8,LOW);
That takes care of the basics for the Led's. Now lets see how main loop of the sketch actually works.
The main loop is simply a 'state' machine. What does this mean? It means that everytime the code completes a section of code, it loads the next state, exits that section, and goes to the next state that was loaded. The 'state' machine is just a big 'switch' statement. The variable 'state' is always loaded with the next state.
You will notice in the 'setup()' section of the code, the 'state' variable is loaded with 'INITIALIZE'.
state = INITIALIZE;
So when the loop() is entered, the 'state' variable is tested with 'case INITIALIZE'.
void loop() {
switch(state)
{
// say to listen on the port
case INITIALIZE:
if(DNETcK::isInitialzied(&status))
{
Serial.println("IP Stack Initialized");
state = LISTEN;
}
else if(DNETcK::isStatusAnError(status))
{
Serial.print("Error in initializing, status: ");
Serial.println(status, DEC);
state = EXIT;
}
break;
The program then initializes the IP Stack. If successful the next state is loaded which is 'LISTEN'. If an error occurs, the next state is loaded with 'EXIT'.
A state machine is very easy to follow. If you look thru the code you will see that we eventually end up in the READ section of code. This is where I have added some code to watch for the commands coming from the iPhone to turn the leds On and Off.
case READ:
// see if we got anything to read
// we need at least 3 bytes
if((cbRead = udpClient.available()) > 0)
{
cbRead = cbRead < sizeof(rgbRead) ? cbRead : sizeof(rgbRead);
cbRead = udpClient.readDatagram(rgbRead, cbRead);
Serial.print("Got ");
Serial.print(cbRead, DEC);
Serial.println(" bytes");
// String stringOne = String(rgbRead[0], BYTE);
// Serial.println(stringOne);
char ledChar = char(rgbRead[0]);
char ledNum = char(rgbRead[1]);
char ledState = char(rgbRead[2]);
Serial.println(testChar);
switch (ledChar)
{
case 'L':
switch (ledNum )
{
case '1':
Serial.println("LED1");
if (ledState == '1')
{
Serial.println("HIGH");
digitalWrite(LED1,HIGH);
}
else
{
Serial.println("LOW");
digitalWrite(LED1,LOW);
}
break;
case '2':
In the code I have added I'm doing the following.
I also commented out 2 lines of code that we no longer need. I added 3 lines of code to get the first 3 bytes that we will be testing for. ledChar contains the letter 'L' for led, ledNum contains a number for which led it is, and ledState contains a 1 or a 0 for indicating the state of the led.
char ledChar = char(rgbRead[0]);
char ledNum = char(rgbRead[1]);
char ledState = char(rgbRead[2]);
Then we come to the Switch statement that is testing ledChar.
switch (ledChar)
If it finds the 'L' for led, it then hits the next switch statement testing for which led it is.
switch (ledNum )
Then I use a if statement to test 'ledState' to see if we need to turn the led On or Off.
if (ledState == '1')
{
Serial.println("HIGH");
digitalWrite(LED1,HIGH);
}
else
{
Serial.println("LOW");
digitalWrite(LED1,LOW);
}
At this point you should be able to finish up the 'READ' state by adding the rest of the Leds 2 - 8. If not download the file to see how I did it.
Lets get ready to test the code we have written. It should be noted that there are plenty of Serial.print statements to make debugging easy or just to watch what is going on. I would recommend that you run the Serial Monitor at 115200 baud.
Install the Basic I/O Shield on top of the Pmod Shield-Uno.
Go ahead and upload the sketch to the Uno32. Start the serial monitor once it is down uploading. You should see the following when the wireless is ready.
Lets go back to MPIDE and the sketch.
One thing I would like to mention at this point. There are many ways to write code. I tried to write this code so it was very easy to understand. You could optimize this code by rewriting it, but that usually makes it harder for people to understand when they are learning something new.
If you look in the documentation for the Basic I/O shield, you will see the LED's are on Pins 26 - 33. So we need to set those pins to be an output. The statement for that is 'pinMode(26, OUTPUT)'. Lets create some define's for the Led's pin number. Its lots easier to remember LED1 than Pin 26.
#define LED1 26
#define LED2 27
#define LED3 28
#define LED4 29
#define LED5 30
#define LED6 31
#define LED7 32
#define LED8 33
Also add the following to set the outputs for the Leds in setup().
pinMode(LED1,OUTPUT);
pinMode(LED2,OUTPUT);
pinMode(LED3,OUTPUT);
pinMode(LED4,OUTPUT);
pinMode(LED5,OUTPUT);
pinMode(LED6,OUTPUT);
pinMode(LED7,OUTPUT);
pinMode(LED8,OUTPUT);
We should also set all the Leds off in setup().
digitalWrite(LED1,LOW);
digitalWrite(LED2,LOW);
digitalWrite(LED3,LOW);
digitalWrite(LED4,LOW);
digitalWrite(LED5,LOW);
digitalWrite(LED6,LOW);
digitalWrite(LED7,LOW);
digitalWrite(LED8,LOW);
That takes care of the basics for the Led's. Now lets see how main loop of the sketch actually works.
The main loop is simply a 'state' machine. What does this mean? It means that everytime the code completes a section of code, it loads the next state, exits that section, and goes to the next state that was loaded. The 'state' machine is just a big 'switch' statement. The variable 'state' is always loaded with the next state.
You will notice in the 'setup()' section of the code, the 'state' variable is loaded with 'INITIALIZE'.
state = INITIALIZE;
So when the loop() is entered, the 'state' variable is tested with 'case INITIALIZE'.
void loop() {
switch(state)
{
// say to listen on the port
case INITIALIZE:
if(DNETcK::isInitialzied(&status))
{
Serial.println("IP Stack Initialized");
state = LISTEN;
}
else if(DNETcK::isStatusAnError(status))
{
Serial.print("Error in initializing, status: ");
Serial.println(status, DEC);
state = EXIT;
}
break;
The program then initializes the IP Stack. If successful the next state is loaded which is 'LISTEN'. If an error occurs, the next state is loaded with 'EXIT'.
A state machine is very easy to follow. If you look thru the code you will see that we eventually end up in the READ section of code. This is where I have added some code to watch for the commands coming from the iPhone to turn the leds On and Off.
case READ:
// see if we got anything to read
// we need at least 3 bytes
if((cbRead = udpClient.available()) > 0)
{
cbRead = cbRead < sizeof(rgbRead) ? cbRead : sizeof(rgbRead);
cbRead = udpClient.readDatagram(rgbRead, cbRead);
Serial.print("Got ");
Serial.print(cbRead, DEC);
Serial.println(" bytes");
// String stringOne = String(rgbRead[0], BYTE);
// Serial.println(stringOne);
char ledChar = char(rgbRead[0]);
char ledNum = char(rgbRead[1]);
char ledState = char(rgbRead[2]);
Serial.println(testChar);
switch (ledChar)
{
case 'L':
switch (ledNum )
{
case '1':
Serial.println("LED1");
if (ledState == '1')
{
Serial.println("HIGH");
digitalWrite(LED1,HIGH);
}
else
{
Serial.println("LOW");
digitalWrite(LED1,LOW);
}
break;
case '2':
In the code I have added I'm doing the following.
I also commented out 2 lines of code that we no longer need. I added 3 lines of code to get the first 3 bytes that we will be testing for. ledChar contains the letter 'L' for led, ledNum contains a number for which led it is, and ledState contains a 1 or a 0 for indicating the state of the led.
char ledChar = char(rgbRead[0]);
char ledNum = char(rgbRead[1]);
char ledState = char(rgbRead[2]);
Then we come to the Switch statement that is testing ledChar.
switch (ledChar)
If it finds the 'L' for led, it then hits the next switch statement testing for which led it is.
switch (ledNum )
Then I use a if statement to test 'ledState' to see if we need to turn the led On or Off.
if (ledState == '1')
{
Serial.println("HIGH");
digitalWrite(LED1,HIGH);
}
else
{
Serial.println("LOW");
digitalWrite(LED1,LOW);
}
At this point you should be able to finish up the 'READ' state by adding the rest of the Leds 2 - 8. If not download the file to see how I did it.
Lets get ready to test the code we have written. It should be noted that there are plenty of Serial.print statements to make debugging easy or just to watch what is going on. I would recommend that you run the Serial Monitor at 115200 baud.
Install the Basic I/O Shield on top of the Pmod Shield-Uno.
Go ahead and upload the sketch to the Uno32. Start the serial monitor once it is down uploading. You should see the following when the wireless is ready.
Now lets get data from AN0 on the ChipKit Uno32. First we have to modify the sketch some more. You will notice in the sketch that the only way we ever get to the 'WRITE' state is we have to received some characters first. Once we read characters it sets the state to 'WRITE', we get the read characters and go to the 'WRITE' state. Lets modify the state machine for a couple of conditions.
Lets leave it where we have to receive one set of characters before we start writing. We dont want to send any characters to the iPhone until the Wireless is up and running. After the first read lets start writing the A/D converter count about 10 times a second to the iPhone.
In the sketch we check if any chars are available, if not then we check the millis counter to see if its been longer than the time in tWait. If its past the time in tWait then it sets the next state to 'CLOSE'
// If too much time elapsed between reads, close the connection
else if( (((unsigned) millis()) - tStart) > tWait )
{
state = CLOSE;
}
Add the following after "unsigned tWait = 25000;"
unsigned tAnalog = 100;
Lets modify the if else just for sending the analog steps up to the iPhone. This also means that the code will never get to the 'CLOSE' state. I would give you the challenge to issue a command from the iPhone that will cause the code to go to the 'CLOSE' state. Should be easy to implement and a good learning experience.
You will notice that in the if else statement I added our tAnalog and changed the next state to 'WRITE' instead of 'CLOSE'. Thats all that needs to be changed.
// If a 100ms has elapsed then send the analog steps to the iPhone
else if( (((unsigned) millis()) - tStart) > tAnalog )
{
state = WRITE;
}
Now, in order to read an analog pin we will use analogRead(), a variable to hold the steps, and assign a pin.
Add the following statements before setup():
int analogIn = 14;
int analogSteps;
Add the following in the 'WRITE' state:
char buff[8];
Serial.println("Entering Write");
analogSteps = analogRead(analogIn);
Serial.println(analogSteps);
This will tell us that we entered the 'WRITE' state and it will display the A/D step count. I also added a char array so when we convert the analogSteps to ascii characters or chars. So here is an example of what you should see.
3.3volts should read '1023', then we convert that to ascii 31,30,32,33.
We are using the itoa function to do the conversion to ascii. Add the following code.
itoa(analogSteps, buff, 10);
//Load the converted characters into the rbgRead array
for (int i = 0; i < 4; i++)
{
rgbRead[i] = buff[i];
}
cbRead = 4;
Lets finish this up with making the final changes to the iPhone app that will receive the analog steps from the ChipKit Uno32. The analog steps will be converted to a voltage and displayed on the iPhone display.
Make the following changes in Xcode and upload to the iPhone.
- (void)udpSocket:(GCDAsyncUdpSocket *)sock didReceiveData:(NSData *)data fromAddress:(NSData *)address withFilterContext:(id)filterContext
{
//Log that we are receiving data
NSLog(@"Receiving Data");
NSString *testMsg = @"Results - ";
NSLog(@"testMsg - %@",testMsg);
//Convert the incoming data into a string
NSString *msg = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
//Convert the data string into a int
int analog = [msg intValue];
//if you take the max input voltage of 3.3v and divide it
//by 10 bits(1023) you get how many millivolts per step
//So multiply the steps by 0.0032258 and get the voltage reading
//to display
float voltage = analog * 0.0032258;
NSLog(@"Analog = %.2f", voltage);
//Add some formatting to display volts
NSMutableString *analogString = [NSMutableString stringWithFormat:@"%.2fv", voltage];
NSLog(@"analogString = %@", analogString);
//Send it out to the display
an0Voltage.text = analogString;
//This will display the analogSteps in the Results field
NSLog(@"msg - %@",msg);
msg = [testMsg stringByAppendingString:msg];
NSLog(@"msg - %@",msg);
if (msg)
{
NSLog(@"Returned-%@", msg);
messageField.text = msg;
}
else
{
NSString *host = nil;
uint16_t port = 0;
[GCDAsyncUdpSocket getHost:&host port:&port fromAddress:address];
}
}
You can download the files for this blog at:
iPhone app at:
iPHONE_BASIC_IO_CONTROLLER
MPIDE Sketch at:
UDP_BASIC_IO_CONTROLLER_SKETCH
No comments:
Post a Comment