This example shows you how to build a native iPhone application for performing multiple-point data acquisition from a single channel.
1. NI-DAQmx Task - Performs multiple-point data acquisition on a single channel.
2. LabVIEW Web Service - Starts the NI-DAQmx task when called and publishes the data as an HTTP response.
3. iPhone Application - Calls the web service on demand and plots the data from the server.
Single-Channel Multi-Point Data Acquition (LabVIEW Project).zip
Single-Channel Multi-Point Data Acquition (iPhone Application).zip
The NI-DAQmx task and web service are combined into a single VI. In this VI, there are three primary sections. First, there is a Setup section which specifies which device the DAQmx task will read from. Also, this section reads the parameters from the web service's HTTP Request, and pulls them out into variables that can be used to create the DAQmx task. These variables are the following:
Next, we have the DAQmx section. This section takes in the parameters that were read in the Setup section and creates a DAQmx task. Once the task is created, a DAQmx read collects the data from the hardware.
Lastly, once the data from the device is in LabVIEW, the final section prepares and sends the data back to the phone. First, the FFT of the data is computed, then, the arrays of data are reordered so that they can be easily processed on the phone. Next, the Data Interleave VI creates a formatted string representing the binary values of the data. (We are able to cut down on the amount of data that needs to be sent over the network by sending a binary representation of our data, rather than an ASCII representation.) Lastly, once the output has been formatted as a binary string, the HTTP Response is written.
After you create your web service VI, you must deploy it within the LabVIEW project.
Because the web service depends on some additional HTTP Headers to be set before the request is sent, this service cannot be tested through the browser like the more simple examples can be tested.
Getting Started with iPhone
Setting Up the Project
In our sample code, we have used the ASIHTTPRequest classes to simplify our calls to the Web Service. Take a look at Using ASIHTTPRequest in an iOS Project to see how to add these classes to your project and add the necessary frameworks.
We have also used the Core Plot framework to simplify plotting in iOS. Take a look at How to Add Core Plot to an iOS Project.
Understanding the Code
This code snippet contains the core functionality and is a part of the View Controller for the application's primary view. When the user presses the Refresh button to fetch new data from the server, we first clear the previous data, then we fetch new data. The comments in the fetchStream method walk through what is happening. It is worth mentioning that it is not generally a good idea to fetch data synchronously like this. The way this is written, the UI will appear frozen to the user from the time that the button is pressed until the data is returned from the server. This could be improved by fetching the data asynchronously. Take a look at the ASIHTTPRequest Documentation to see how to make this modification.
Not shown here, we use a modal view controller to give the user the ability to edit the parameters of the data acquisition. This view controller is defined in the ModalConfigurationViewController class, and is displayed to the user when the editButtonPressed function is called.
-(IBAction)refreshButtonPressed:(id)sender{
//remove any previous data
[appDelegate.data clearSamplesForVirtualChannel:channelName];
[appDelegate.data clearSamplesForVirtualChannel:channelName2];
//fetch new data from the web service
[self fetchStream];
}
-(void)fetchStream{
NSURL *url;
ASIHTTPRequest *request;
NSError *error;
// Create an HTTP Request to fetch data from the server
NSUserDefaults *stdDefaults = [NSUserDefaults standardUserDefaults];
url = [NSURL URLWithString:[stdDefaults valueForKey:@"networkAddress"]];
request = [ASIHTTPRequest requestWithURL:url];
// Set the request headers with the variables that were populated from the Settings View
// Note, the Physical Channel variable should be set for compatibility with the existing web
// service's API, but it will be ignored by LabVIEW
[request addRequestHeader:@"PHYSICAL_CHANNEL" value:@"0"];
[request addRequestHeader:@"MODE" value:[HelperFunctions numberToString:[stdDefaults valueForKey:@"dataMode"]]];
[request addRequestHeader:@"NUMBER_OF_SAMPLES" value:[HelperFunctions numberToString:[stdDefaults valueForKey:@"numberOfSamples"]]];
[request addRequestHeader:@"SAMPLE_RATE" value:[HelperFunctions numberToString:[stdDefaults valueForKey:@"sampleRate"]]];
[request startSynchronous];
// Print out the web request's info for debugging
NSLog(@"%@", url);
NSLog(@"numberOfSamples: %@", [HelperFunctions numberToString:[stdDefaults valueForKey:@"numberOfSamples"]]);
NSLog(@"sampleRate: %@", [HelperFunctions numberToString:[stdDefaults valueForKey:@"sampleRate"]]);
error = [request error];
if (!error) {
// If there was no error, let's save the data
double num;
NSRange range;
CFSwappedFloat64 tempNum;
NSData *response = [request responseData];
NSInteger numElements = [response length]/sizeof(double);
NSMutableArray *results;
NSMutableArray *results2;
// Depending on the dataMode (time, fft, both) we'll need different storage to handle the data
// (This approach isn't very efficient, but hopefully the concepts are clear)
switch ([[stdDefaults valueForKey:@"dataMode"] intValue]) {
case 0:
//time
results = [[NSMutableArray alloc] initWithCapacity:numElements/2];
break;
case 1:
//FFT
results2 = [[NSMutableArray alloc] initWithCapacity:numElements/2];
break;
case 2:
//time/FFT
results = [[NSMutableArray alloc] initWithCapacity:numElements/2];
results2 = [[NSMutableArray alloc] initWithCapacity:numElements/2];
break;
default:
break;
}
// Let's loop through all the data that we recevied and add it to the correct
// storage array.
for (NSInteger i=0; i<numElements; i++) {
// First, we need to extract the next set of bytes that represent the next data element
range.location = 8*i;
range.length = sizeof(double);
[response getBytes:&tempNum range:range];
// Because LabVIEW output the data using Bin Endian formatting, we need to swap the
// byte order to Little Endian to be compatible with iOS
num = CFConvertDoubleSwappedToHost(tempNum);
// Based on the data mode, add the current number to the correct array
switch ([[stdDefaults valueForKey:@"dataMode"] intValue]) {
case 0:
//time
[results addObject:[NSNumber numberWithDouble:num]];
break;
case 1:
//FFT
[results2 addObject:[NSNumber numberWithDouble:num]];
break;
case 2:
//time/FFT
if (i%2 == 0) {
// Even idexed points will be Time values
[results addObject:[NSNumber numberWithDouble:num]];
}else {
// Odd indexed points will be FFT values
[results2 addObject:[NSNumber numberWithDouble:num]];
}
break;
default:
break;
}
}
// Based on the data mode, add the array(s) of data to the app's storage
switch ([[stdDefaults valueForKey:@"dataMode"] intValue]) {
case 0:
//time
[appDelegate.data setSamples:results forVirtualChannel:channelName];
break;
case 1:
//FFT
[appDelegate.data setSamples:results2 forVirtualChannel:channelName2];
break;
case 2:
//time/FFT
[appDelegate.data setSamples:results forVirtualChannel:channelName];
[appDelegate.data setSamples:results2 forVirtualChannel:channelName2];
break;
default:
break;
}
// Update the UI with the data
[graphView processNewPoint];
}else {
// If there was an error retrieving data, log it to the console.
// In a shipping app, this should certainly be improved to alert the user to the error.
NSLog(@"error");
NSLog(@"%i", [request responseStatusCode]);
NSLog(@"%@", error);
}
}
good Project
This site uses cookies to offer you a better browsing experience. Learn more about our privacy statement and cookie policy.