--- /dev/null
+void ActuatorHandler(FCGIContext *context, ActuatorId id, const char *set_value) {
+ char *ptr;
+
+ switch(id) { //Add new actuators here
+ case ACT_PRESSURE: //Suppose is pressure regulator. 0-700 input (kPa)
+ //requires PWM control
+ //TBA, currently in a separate file
+ //pwm_convert_duty(value); - convert input to required duty cycle
+ //pwm_set_frequency(PWM_id, freq); - can be done during PWM setup, frequency won't change (50Hz?)
+ //pwm_set_duty_cycle(PWM_id, duty_cycle); - this is the low/high percentage, will vary with input
+ //may also need to set polarity?
+ //if pwm is not setup, run setup functions first
+ {
+ int value = strtol(set_value, &ptr, 10);
+ if (*ptr == '\0' && value >= 0 && value <= 700) {
+ FCGI_BeginJSON(context, STATUS_OK);
+ FCGI_JSONKey("description");
+ FCGI_JSONValue("\"Set pressure to %d kPa!\"", value);
+ FCGI_EndJSON();
+ } else {
+ FCGI_RejectJSONEx(context,
+ STATUS_ERROR, "Invalid pressure specified.");
+ }
+ } break;
+ case ACT_SOLENOID1:
+ //requires digital control
+ {
+ int value = strtol(set_value, &ptr, 10);
+ if (*ptr == '\0') {
+ //code to set pin to value
+ //move this to separate function, will need to be repeated
+ int fd;
+ char buffer[10];
+ if ((fd = open("/sys/class/gpio/gpio50/value", O_WRONLY)) < 0) //randomly chose pin 50 (each pin can be mapped to a certain switch case as required)
+ perror("Failed to open pin");
+ if (value) {
+ strncpy(buffer, "1", ARRAY_SIZE(buffer) - 1); //copy value to buffer
+ } else {
+ strncpy(buffer, "0", ARRAY_SIZE(buffer) - 1);
+ }
+ int write = write(fd, buffer, strlen(buffer)); //write buffer to pin
+ if (write < 0) perror("Failed to write to pin");
+ close(fd);
+ //code for server
+ const char *state = "off";
+ if (value)
+ state = "on";
+ FCGI_BeginJSON(context, STATUS_OK);
+ FCGI_JSONKey("description");
+ FCGI_JSONValue("\"Solenoid 1 turned %s!\"", state);
+ FCGI_EndJSON();
+ } else {
+ FCGI_RejectJSON(context, "Invalid actuator value specified");
+ }
+ } break;
+ default:
+ FCGI_RejectJSONEx(context,
+ STATUS_ERROR, "Invalid actuator id specified.");
+ }
+}
\ No newline at end of file
--- /dev/null
+DataPoint * GetData(int sensor_id, DataPoint * d)
+{
+ //TODO: We should ensure the time is *never* allowed to change on the server if we use gettimeofday
+ // Another way people might think of getting the time is to count CPU cycles with clock()
+ // But this will not work because a) CPU clock speed may change on some devices (RPi?) and b) It counts cycles used by all threads
+ gettimeofday(&(d->time_stamp), NULL);
+
+ switch (sensor_id)
+ {
+ //TODO: test buffer size on actual hardware
+ // maybe map the sensor path to an array/structure that can be looked up via sensor_id?
+ // not sure about the best place to do unit conversions, especially for nonlinear sensors
+ case SENSOR_TEST0:
+ {
+ int sensor = open("/sys/devices/platform/tsc/ain0", O_RDONLY); //need unique path for each sensor ADC, check path in documentation
+ char buffer[128]; //ADCs on Beaglebone are 12 bits?
+ int read = read(sensor, buffer, sizeof(buffer);
+ if (read != -1) {
+ buffer[read] = NULL; //string returned by read is not null terminated
+ int value = atoi(buffer);
+ double convert = (value/4096) * 1800; //sample conversion from ADC input to 'true value'
+ d->value = convert;
+ lseek(sensor, 0, 0); //after read string must be rewound to start of file using lseek
+ }
+ else {
+ perror("Failed to get value from sensor");
+ }
+ close(sensor);
+ break;
+ }
+ case SENSOR_TEST1:
+ int sensor = open("/sys/devices/platform/tsc/ain1", O_RDONLY);
+ char buffer[128];
+ int read = read(sensor, buffer, sizeof(buffer);
+ if (read != -1) {
+ buffer[read] = NULL;
+ int value = atoi(buffer);
+ double convert = (value/4096) * 1800;
+ d->value = convert;
+ lseek(sensor, 0, 0);
+ }
+ else {
+ perror("Failed to get value from sensor");
+ }
+ close(sensor);
+ break;
+ break;
+ }
+ //TODO: think about a better way to address individual pins
+ // i.e. pass an int to a separate function that builds the correct filename
+ // doesn't really matter if the pins we're using are fixed though
+ case DIGITAL_TEST0:
+ int fd = open("/sys/class/gpio/gpio17/value", O_RDONLY) //again need the right addresses for each pin
+ char ch; //just one character for binary info
+ lseek(fd, 0, SEEK_SET);
+ int read = read(fd, &ch, sizeof(ch);
+ if (read != -1) {
+ if (ch != '0') {
+ d->value = 1;
+ }
+ else {
+ d->value = 0;
+ }
+ }
+ else {
+ perror("Failed to get value from pin");
+ }
+ break;
+ case DIGITAL_TEST1:
+ int fd = open("/sys/class/gpio/gpio23/value", O_RDONLY)
+ char ch;
+ lseek(fd, 0, SEEK_SET);
+ int read = read(fd, &ch, sizeof(ch);
+ if (read != -1) {
+ if (ch != '0') {
+ d->value = 1;
+ }
+ else {
+ d->value = 0;
+ }
+ }
+ else {
+ perror("Failed to get value from pin");
+ }
+ break;
+ default:
+ Fatal("Unknown sensor id: %d", sensor_id);
+ break;
+ }
+ usleep(100000); // simulate delay in sensor polling
+
+ return d;
+}
\ No newline at end of file
--- /dev/null
+#include "pwm.h"
+
+/* Initialize PWM :
+1/ mmap /dev/mem to have write access to system clock
+2/ enable system clock (0x0 = disabled, 0x02 = enabled)
+3/ set correct pin MUX
+
+can modify pwm variables through virtual filesystem:
+"/sys/class/pwm/ehrpwm.1:0/..."
+
+pwm drivers reference:
+http://processors.wiki.ti.com/index.php/AM335x_PWM_Driver%27s_Guide */
+
+void pwm_init(void)
+{
+ FILE *pwm_mux;
+ int i;
+ volatile uint32_t *epwmss1;
+
+ int fd = open("/dev/mem", O_RDWR);
+
+ if(fd < 0)
+ {
+ printf("Can't open /dev/mem\n");
+ exit(1);
+ }
+
+ epwmss1 = (volatile uint32_t *) mmap(NULL, LENGTH, PROT_READ|PROT_WRITE, MAP_SHARED, fd, START);
+ if(epwmss1 == NULL)
+ {
+ printf("Can't mmap\n");
+ exit(1);
+ }
+ else
+ {
+ epwmss1[OFFSET_1 / sizeof(uint32_t)] = 0x2;
+ }
+ close(fd);
+
+ pwm_mux = fopen("/sys/kernel/debug/omap_mux/gpmc_a2", "w");
+ fprintf(pwm_mux, "6"); // pwm is mux mode 6
+ fclose(pwm_mux);
+}
+
+//can change filepath of pwm module "/ehrpwm.%d:0/" by passing %d as argument
+//depends how many pwm modules we have to run
+//TODO:
+
+void pwm_start(void)
+{
+ FILE *pwm_run;
+ pwm_run = fopen("/sys/class/pwm/ehrpwm.1:0/run", "w");
+ fprintf(pwm_run, "1");
+ fclose(pwm_run);
+}
+
+void pwm_stop(void)
+{
+ FILE *pwm_run;
+
+ pwm_run = fopen("/sys/class/pwm/ehrpwm.1:0/run", "w");
+ fprintf(pwm_run, "0");
+ fclose(pwm_run);
+}
+
+//duty_percent is just a regular percentage (i.e. 50 = 50%)
+
+void pwm_set_duty(int duty_percent)
+{
+ FILE *pwm_duty;
+
+ pwm_duty = fopen("/sys/class/pwm/ehrpwm.1:0/duty_percent", "w");
+ fprintf(pwm_duty, "%d", duty_percent);
+ fclose(pwm_duty);
+}
+
+//freq is just normal frequency (i.e. 100 = 100Hz)
+
+void pwm_set_period(int freq)
+{
+ FILE *pwm_period;
+
+ pwm_period = fopen("/sys/class/pwm/ehrpwm.1:0/period_freq", "w");
+ fprintf(pwm_period, "%d", freq);
+ fclose(pwm_period);
+}
\ No newline at end of file
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <sys/wait.h>
+#include <sched.h>
+#include <stdint.h>
+
+#define START 0x44e00000 // see datasheet of AM335x
+#define LENGTH 1024
+#define OFFSET_1 0xcc // offset of PWM1 clock (see datasheet of AM335x p.1018)
+
+void pwm_init(void);
+void pwm_start(void);
+void pwm_stop(void);
+void pwm_set_duty(int duty_percent);
+void pwm_set_period(int freq);
\ No newline at end of file
--- /dev/null
+//Code to blink an LED - just to illustrate that it's pretty easy
+//Only important thing is which name to address the LED by
+
+#include <stdio.h>
+#include <unistd.h>
+
+using namespace std;
+
+int main(int argc, char** argv) {
+ FILE *LEDHandle = NULL;
+ char *LEDBrightness = "/sys/class/leds/beaglebone:green:usr0/brightness";
+ printf("\nStarting LED blink program wooo!\n");
+ while(1){
+ if((LEDHandle = fopen(LEDBrightness, "r+")) != NULL){
+ fwrite("1", sizeof(char), 1, LEDHandle);
+ fclose(LEDHandle);
+ }
+ sleep(1);
+ if((LEDHandle = fopen(LEDBrightness, "r+")) != NULL){
+ fwrite("0", sizeof(char), 1, LEDHandle);
+ fclose(LEDHandle);
+ }
+ sleep(1);
+ }
+ return 0;
+}
+
+//Sample code that should read a pressure sensor pin (conversion factors
+//are just random numbers). Again pretty simple.
+
+#include STUFF
+
+double pressure(char *string) {
+ int value = atoi(string);
+ double millivolts = (value / 4096.0) * 1800; //convert input to volts
+ double pressure = (millivolts - 500.0) / 10.0; //convert volts to pressure
+ return pressure;
+}
+
+void main() {
+ int fd = open("/sys/devices/platform/tsc/ain2", O_RDONLY); //open pin signal
+ while (1) {
+ char buffer[1024];
+ int ret = read(fd, buffer, sizeof(buffer)); //get data
+ if (ret != -1) {
+ buffer[ret] = NULL;
+ double kpa = pressure(buffer);
+ printf("digital value: %s kilopascals: %f\n", buffer, kpa);
+ lseek(fd, 0, 0);
+ }
+ sleep(1);
+ }
+ close(fd);
+}
\ No newline at end of file
/*int p[3];
p[0] = CV_IMWRITE_JPEG_QUALITY;
- p[1] = 10;
+ p[1] = 10; //quality value; 0->100
p[2] = 0;*/
frame = cvQueryFrame(capture);
if( frame == NULL)
return 0; //error
cvSaveImage(filepath,frame,0);
-
+ cvReleaseImageHeader(&frame);
return 1;
}
20:49 -!- jtanx [
[email protected]] has quit ["ChatZilla 0.9.90.1 [Firefox 23.0.1/20130814063812]"]
+--- Day changed Thu Sep 05 2013
+09:34 -!- jtanx [
[email protected]] has quit ["ChatZilla 0.9.90.1 [Firefox 23.0.1/20130814063812]"]
+18:51 < jtanx> hm, so to get clock_gettime to work, you need to use std=gnu99 instead of std=c99
+18:52 < jtanx> do you think we should just stick with gettimeofday?
+--- Day changed Fri Sep 06 2013
+12:05 -!- jtanx [
[email protected]] has quit ["ChatZilla 0.9.90.1 [Firefox 23.0.1/20130814063812]"]
+16:05 < jtanx> I was just thinking, what sort of identification information is needed from the server?
+16:05 < jtanx> The gui should have some understanding of what sort of sensors/actuators it needs to display
+16:10 < jtanx> it might be enough to say this is API version x
+16:10 < jtanx> and at version x, it's agreed that these id values correspond to these sensors/actuators etc?
+16:19 < jtanx> anyway, what I've done for now is if asked, the api will list the sensor/actuator ids and the corresponding human readable string
+16:19 < jtanx> api/?sensors&actuators
+18:06 < jtanx> also, what do you think of keeping track of the number of points stored
+18:07 < jtanx> then you can allow the user to request from where they want to retrieve data from and how many to retrieve
+18:08 < jtanx> the only problem is that it wouldn't be time based, but based on the number of data points recorded
+18:08 < jtanx> this method is simpler because you now the offset to use with fseek
+18:08 < jtanx> if it's time based you have to search the data for the correct point
+19:51 < sam_moore> All the above makes good sense
+19:52 < sam_moore> If we can make a working "request X number of points" first instead of "request from time T onwards", we might be able to convert the latter to the former if necessary
+20:04 < jtanx> ok I just submitted a pull request for some stuff
+20:04 < jtanx> mostly the identification stuff and some reordering
+20:04 < jtanx> FCGI_RejectJSON now requires that you give a description explaining why
+20:05 < jtanx> when RejectJSOn is called, it's always logged along with the description, so some of the current log messages within the handler functions may be redundant
+21:11 -!- jtanx [
[email protected]] has quit ["ChatZilla 0.9.90.1 [Firefox 23.0.1/20130814063812]"]
+--- Day changed Sat Sep 07 2013
+18:29 < jtanx> hmm
+18:29 < jtanx> what if you had two file pointers to the same file
+18:29 < jtanx> one read only, one write only
+18:29 < jtanx> then if you also kept track of how many points were written to the file
+18:29 < jtanx> you don't need to have mutexes anymore
+18:30 < jtanx> as long as you write the read/write calls carefully
+18:44 < jtanx> ahp, may have spoken too soon
+18:44 < jtanx> you'd still need a mutex around the read/write from/to the counter
+18:45 < jtanx> I think...
+19:07 < jtanx> but it might still be a good idea
+21:38 < jtanx> I went the simple route
+21:53 < jtanx> what I've got: /api/sensors?id=x&from=y&count=z
+21:54 < jtanx> In dump mode:
+21:54 < jtanx> If from < 0, then return from start, else from the given index (0-indexed)
+21:54 < jtanx> if count < 0, then return all points, else return /at most/ that many points
+21:55 < jtanx> in normal mode:
+21:55 < jtanx> if from < 0, then return from the end (return the most recent points)
+21:55 < jtanx> if count < 0, then return at most the default amount (SENSOR_QUERYBUFSIZ)
+21:56 < jtanx> otherwise return /at most/ that many points
+21:56 < jtanx> oh ya, if from >= 0 then return from that indicated index
+21:57 < jtanx> it's only in my git repository for now because I haven't tested it enough and I'm still not sure how I should go about integrating it with the current code
+21:58 < jtanx> (I basically rewrote Sensor_Handler and it has less error checks on the input; not sure if they're necessary or not)
+21:59 < jtanx> Since requesting a huge range could impact on sensor readings due to the mutex, it may be better to at least restrict dumps to when the experiment is not 'running'
+--- Day changed Sun Sep 08 2013
+18:06 < Callum_> what was the server stuff you said to install? also what packages do i need?
+18:09 -!- Callum_ is now known as Callum
+18:21 < jtanx> if you want to install the server, you need at least the 'nginx' and 'spawn-fcgi' packages
+18:21 < jtanx> one nginx is installed you need to copy the config files from the git repository in
+21:14 -!- jtanx [
[email protected]] has quit ["ChatZilla 0.9.90.1 [Firefox 23.0.1/20130814063812]"]
+22:36 -!- jtanx [
[email protected]] has quit ["ChatZilla 0.9.90.1 [Firefox 23.0.1/20130814063812]"]
+22:38 -!- Callum [
[email protected]] has quit ["ChatZilla 0.9.90.1 [Firefox 23.0.1/20130814063812]"]
+++ /dev/null
-/* Industrialio buffer test code.
- *
- * Copyright (c) 2008 Jonathan Cameron
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published by
- * the Free Software Foundation.
- *
- * This program is primarily intended as an example application.
- * Reads the current buffer setup from sysfs and starts a short capture
- * from the specified device, pretty printing the result after appropriate
- * conversion.
- *
- * Command line parameters
- * generic_buffer -n <device_name> -t <trigger_name>
- * If trigger name is not specified the program assumes you want a dataready
- * trigger associated with the device and goes looking for it.
- *
- */
-
-#define _GNU_SOURCE
-
-#include <unistd.h>
-#include <dirent.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <errno.h>
-#include <sys/stat.h>
-#include <sys/dir.h>
-#include <linux/types.h>
-#include <string.h>
-#include <poll.h>
-#include <endian.h>
-#include <getopt.h>
-#include <inttypes.h>
-#include "iio_utils.h"
-
-/**
- * size_from_channelarray() - calculate the storage size of a scan
- * @channels: the channel info array
- * @num_channels: number of channels
- *
- * Has the side effect of filling the channels[i].location values used
- * in processing the buffer output.
- **/
-int size_from_channelarray(struct iio_channel_info *channels, int num_channels)
-{
- int bytes = 0;
- int i = 0;
- while (i < num_channels) {
- if (bytes % channels[i].bytes == 0)
- channels[i].location = bytes;
- else
- channels[i].location = bytes - bytes%channels[i].bytes
- + channels[i].bytes;
- bytes = channels[i].location + channels[i].bytes;
- i++;
- }
- return bytes;
-}
-
-void print2byte(int input, struct iio_channel_info *info)
-{
- /* First swap if incorrect endian */
- if (info->be)
- input = be16toh((uint16_t)input);
- else
- input = le16toh((uint16_t)input);
-
- /*
- * Shift before conversion to avoid sign extension
- * of left aligned data
- */
- input = input >> info->shift;
- if (info->is_signed) {
- int16_t val = input;
- val &= (1 << info->bits_used) - 1;
- val = (int16_t)(val << (16 - info->bits_used)) >>
- (16 - info->bits_used);
- printf("%05f ", ((float)val + info->offset)*info->scale);
- } else {
- uint16_t val = input;
- val &= (1 << info->bits_used) - 1;
- printf("%05f ", ((float)val + info->offset)*info->scale);
- }
-}
-/**
- * process_scan() - print out the values in SI units
- * @data: pointer to the start of the scan
- * @channels: information about the channels. Note
- * size_from_channelarray must have been called first to fill the
- * location offsets.
- * @num_channels: number of channels
- **/
-void process_scan(char *data,
- struct iio_channel_info *channels,
- int num_channels)
-{
- int k;
- for (k = 0; k < num_channels; k++)
- switch (channels[k].bytes) {
- /* only a few cases implemented so far */
- case 2:
- print2byte(*(uint16_t *)(data + channels[k].location),
- &channels[k]);
- break;
- case 4:
- if (!channels[k].is_signed) {
- uint32_t val = *(uint32_t *)
- (data + channels[k].location);
- printf("%05f ", ((float)val +
- channels[k].offset)*
- channels[k].scale);
-
- }
- break;
- case 8:
- if (channels[k].is_signed) {
- int64_t val = *(int64_t *)
- (data +
- channels[k].location);
- if ((val >> channels[k].bits_used) & 1)
- val = (val & channels[k].mask) |
- ~channels[k].mask;
- /* special case for timestamp */
- if (channels[k].scale == 1.0f &&
- channels[k].offset == 0.0f)
- printf("%" PRId64 " ", val);
- else
- printf("%05f ", ((float)val +
- channels[k].offset)*
- channels[k].scale);
- }
- break;
- default:
- break;
- }
- printf("\n");
-}
-
-int main(int argc, char **argv)
-{
- unsigned long num_loops = 2;
- unsigned long timedelay = 1000000;
- unsigned long buf_len = 128;
-
- int ret, c, i, j, toread;
- int fp;
-
- int num_channels;
- char *trigger_name = NULL, *device_name = NULL;
- char *dev_dir_name, *buf_dir_name;
-
- int datardytrigger = 1;
- char *data;
- ssize_t read_size;
- int dev_num, trig_num;
- char *buffer_access;
- int scan_size;
- int noevents = 0;
- char *dummy;
-
- struct iio_channel_info *channels;
-
- while ((c = getopt(argc, argv, "l:w:c:et:n:")) != -1) {
- switch (c) {
- case 'n':
- device_name = optarg;
- break;
- case 't':
- trigger_name = optarg;
- datardytrigger = 0;
- break;
- case 'e':
- noevents = 1;
- break;
- case 'c':
- num_loops = strtoul(optarg, &dummy, 10);
- break;
- case 'w':
- timedelay = strtoul(optarg, &dummy, 10);
- break;
- case 'l':
- buf_len = strtoul(optarg, &dummy, 10);
- break;
- case '?':
- return -1;
- }
- }
-
- if (device_name == NULL)
- return -1;
-
- /* Find the device requested */
- dev_num = find_type_by_name(device_name, "iio:device");
- if (dev_num < 0) {
- printf("Failed to find the %s\n", device_name);
- ret = -ENODEV;
- goto error_ret;
- }
- printf("iio device number being used is %d\n", dev_num);
-
- asprintf(&dev_dir_name, "%siio:device%d", iio_dir, dev_num);
- if (trigger_name == NULL) {
- /*
- * Build the trigger name. If it is device associated its
- * name is <device_name>_dev[n] where n matches the device
- * number found above
- */
- ret = asprintf(&trigger_name,
- "%s-dev%d", device_name, dev_num);
- if (ret < 0) {
- ret = -ENOMEM;
- goto error_ret;
- }
- }
-
- /* Verify the trigger exists */
- trig_num = find_type_by_name(trigger_name, "trigger");
- if (trig_num < 0) {
- printf("Failed to find the trigger %s\n", trigger_name);
- ret = -ENODEV;
- goto error_free_triggername;
- }
- printf("iio trigger number being used is %d\n", trig_num);
-
- /*
- * Parse the files in scan_elements to identify what channels are
- * present
- */
- ret = build_channel_array(dev_dir_name, &channels, &num_channels);
- if (ret) {
- printf("Problem reading scan element information\n");
- printf("diag %s\n", dev_dir_name);
- goto error_free_triggername;
- }
-
- /*
- * Construct the directory name for the associated buffer.
- * As we know that the lis3l02dq has only one buffer this may
- * be built rather than found.
- */
- ret = asprintf(&buf_dir_name,
- "%siio:device%d/buffer", iio_dir, dev_num);
- if (ret < 0) {
- ret = -ENOMEM;
- goto error_free_triggername;
- }
- printf("%s %s\n", dev_dir_name, trigger_name);
- /* Set the device trigger to be the data ready trigger found above */
- ret = write_sysfs_string_and_verify("trigger/current_trigger",
- dev_dir_name,
- trigger_name);
- if (ret < 0) {
- printf("Failed to write current_trigger file\n");
- goto error_free_buf_dir_name;
- }
-
- /* Setup ring buffer parameters */
- ret = write_sysfs_int("length", buf_dir_name, buf_len);
- if (ret < 0)
- goto error_free_buf_dir_name;
-
- /* Enable the buffer */
- ret = write_sysfs_int("enable", buf_dir_name, 1);
- if (ret < 0)
- goto error_free_buf_dir_name;
- scan_size = size_from_channelarray(channels, num_channels);
- data = malloc(scan_size*buf_len);
- if (!data) {
- ret = -ENOMEM;
- goto error_free_buf_dir_name;
- }
-
- ret = asprintf(&buffer_access, "/dev/iio:device%d", dev_num);
- if (ret < 0) {
- ret = -ENOMEM;
- goto error_free_data;
- }
-
- /* Attempt to open non blocking the access dev */
- fp = open(buffer_access, O_RDONLY | O_NONBLOCK);
- if (fp == -1) { /* If it isn't there make the node */
- printf("Failed to open %s\n", buffer_access);
- ret = -errno;
- goto error_free_buffer_access;
- }
-
- /* Wait for events 10 times */
- for (j = 0; j < num_loops; j++) {
- if (!noevents) {
- struct pollfd pfd = {
- .fd = fp,
- .events = POLLIN,
- };
-
- poll(&pfd, 1, -1);
- toread = buf_len;
-
- } else {
- usleep(timedelay);
- toread = 64;
- }
-
- read_size = read(fp,
- data,
- toread*scan_size);
- if (read_size == -EAGAIN) {
- printf("nothing available\n");
- continue;
- }
- for (i = 0; i < read_size/scan_size; i++)
- process_scan(data + scan_size*i,
- channels,
- num_channels);
- }
-
- /* Stop the buffer */
- ret = write_sysfs_int("enable", buf_dir_name, 0);
- if (ret < 0)
- goto error_close_buffer_access;
-
- /* Disconnect the trigger - just write a dummy name. */
- write_sysfs_string("trigger/current_trigger",
- dev_dir_name, "NULL");
-
-error_close_buffer_access:
- close(fp);
-error_free_data:
- free(data);
-error_free_buffer_access:
- free(buffer_access);
-error_free_buf_dir_name:
- free(buf_dir_name);
-error_free_triggername:
- if (datardytrigger)
- free(trigger_name);
-error_ret:
- return ret;
-}
+++ /dev/null
-/* IIO - useful set of util functionality
- *
- * Copyright (c) 2008 Jonathan Cameron
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published by
- * the Free Software Foundation.
- */
-
-#include <string.h>
-#include <stdlib.h>
-#include <ctype.h>
-#include <stdio.h>
-#include <stdint.h>
-#include <dirent.h>
-#include <errno.h>
-
-/* Made up value to limit allocation sizes */
-#define IIO_MAX_NAME_LENGTH 30
-
-#define FORMAT_SCAN_ELEMENTS_DIR "%s/scan_elements"
-#define FORMAT_TYPE_FILE "%s_type"
-
-const char *iio_dir = "/sys/bus/iio/devices/";
-
-/**
- * iioutils_break_up_name() - extract generic name from full channel name
- * @full_name: the full channel name
- * @generic_name: the output generic channel name
- **/
-inline int iioutils_break_up_name(const char *full_name,
- char **generic_name)
-{
- char *current;
- char *w, *r;
- char *working;
- current = strdup(full_name);
- working = strtok(current, "_\0");
- w = working;
- r = working;
-
- while (*r != '\0') {
- if (!isdigit(*r)) {
- *w = *r;
- w++;
- }
- r++;
- }
- *w = '\0';
- *generic_name = strdup(working);
- free(current);
-
- return 0;
-}
-
-/**
- * struct iio_channel_info - information about a given channel
- * @name: channel name
- * @generic_name: general name for channel type
- * @scale: scale factor to be applied for conversion to si units
- * @offset: offset to be applied for conversion to si units
- * @index: the channel index in the buffer output
- * @bytes: number of bytes occupied in buffer output
- * @mask: a bit mask for the raw output
- * @is_signed: is the raw value stored signed
- * @enabled: is this channel enabled
- **/
-struct iio_channel_info {
- char *name;
- char *generic_name;
- float scale;
- float offset;
- unsigned index;
- unsigned bytes;
- unsigned bits_used;
- unsigned shift;
- uint64_t mask;
- unsigned be;
- unsigned is_signed;
- unsigned enabled;
- unsigned location;
-};
-
-/**
- * iioutils_get_type() - find and process _type attribute data
- * @is_signed: output whether channel is signed
- * @bytes: output how many bytes the channel storage occupies
- * @mask: output a bit mask for the raw data
- * @be: big endian
- * @device_dir: the iio device directory
- * @name: the channel name
- * @generic_name: the channel type name
- **/
-inline int iioutils_get_type(unsigned *is_signed,
- unsigned *bytes,
- unsigned *bits_used,
- unsigned *shift,
- uint64_t *mask,
- unsigned *be,
- const char *device_dir,
- const char *name,
- const char *generic_name)
-{
- FILE *sysfsfp;
- int ret;
- DIR *dp;
- char *scan_el_dir, *builtname, *builtname_generic, *filename = 0;
- char signchar, endianchar;
- unsigned padint;
- const struct dirent *ent;
-
- ret = asprintf(&scan_el_dir, FORMAT_SCAN_ELEMENTS_DIR, device_dir);
- if (ret < 0) {
- ret = -ENOMEM;
- goto error_ret;
- }
- ret = asprintf(&builtname, FORMAT_TYPE_FILE, name);
- if (ret < 0) {
- ret = -ENOMEM;
- goto error_free_scan_el_dir;
- }
- ret = asprintf(&builtname_generic, FORMAT_TYPE_FILE, generic_name);
- if (ret < 0) {
- ret = -ENOMEM;
- goto error_free_builtname;
- }
-
- dp = opendir(scan_el_dir);
- if (dp == NULL) {
- ret = -errno;
- goto error_free_builtname_generic;
- }
- while (ent = readdir(dp), ent != NULL)
- /*
- * Do we allow devices to override a generic name with
- * a specific one?
- */
- if ((strcmp(builtname, ent->d_name) == 0) ||
- (strcmp(builtname_generic, ent->d_name) == 0)) {
- ret = asprintf(&filename,
- "%s/%s", scan_el_dir, ent->d_name);
- if (ret < 0) {
- ret = -ENOMEM;
- goto error_closedir;
- }
- sysfsfp = fopen(filename, "r");
- if (sysfsfp == NULL) {
- printf("failed to open %s\n", filename);
- ret = -errno;
- goto error_free_filename;
- }
-
- ret = fscanf(sysfsfp,
- "%ce:%c%u/%u>>%u",
- &endianchar,
- &signchar,
- bits_used,
- &padint, shift);
- if (ret < 0) {
- printf("failed to pass scan type description\n");
- ret = -errno;
- goto error_close_sysfsfp;
- }
- *be = (endianchar == 'b');
- *bytes = padint / 8;
- if (*bits_used == 64)
- *mask = ~0;
- else
- *mask = (1 << *bits_used) - 1;
- if (signchar == 's')
- *is_signed = 1;
- else
- *is_signed = 0;
- fclose(sysfsfp);
- free(filename);
-
- filename = 0;
- sysfsfp = 0;
- }
-error_close_sysfsfp:
- if (sysfsfp)
- fclose(sysfsfp);
-error_free_filename:
- if (filename)
- free(filename);
-error_closedir:
- closedir(dp);
-error_free_builtname_generic:
- free(builtname_generic);
-error_free_builtname:
- free(builtname);
-error_free_scan_el_dir:
- free(scan_el_dir);
-error_ret:
- return ret;
-}
-
-inline int iioutils_get_param_float(float *output,
- const char *param_name,
- const char *device_dir,
- const char *name,
- const char *generic_name)
-{
- FILE *sysfsfp;
- int ret;
- DIR *dp;
- char *builtname, *builtname_generic;
- char *filename = NULL;
- const struct dirent *ent;
-
- ret = asprintf(&builtname, "%s_%s", name, param_name);
- if (ret < 0) {
- ret = -ENOMEM;
- goto error_ret;
- }
- ret = asprintf(&builtname_generic,
- "%s_%s", generic_name, param_name);
- if (ret < 0) {
- ret = -ENOMEM;
- goto error_free_builtname;
- }
- dp = opendir(device_dir);
- if (dp == NULL) {
- ret = -errno;
- goto error_free_builtname_generic;
- }
- while (ent = readdir(dp), ent != NULL)
- if ((strcmp(builtname, ent->d_name) == 0) ||
- (strcmp(builtname_generic, ent->d_name) == 0)) {
- ret = asprintf(&filename,
- "%s/%s", device_dir, ent->d_name);
- if (ret < 0) {
- ret = -ENOMEM;
- goto error_closedir;
- }
- sysfsfp = fopen(filename, "r");
- if (!sysfsfp) {
- ret = -errno;
- goto error_free_filename;
- }
- fscanf(sysfsfp, "%f", output);
- break;
- }
-error_free_filename:
- if (filename)
- free(filename);
-error_closedir:
- closedir(dp);
-error_free_builtname_generic:
- free(builtname_generic);
-error_free_builtname:
- free(builtname);
-error_ret:
- return ret;
-}
-
-/**
- * bsort_channel_array_by_index() - reorder so that the array is in index order
- *
- **/
-
-inline void bsort_channel_array_by_index(struct iio_channel_info **ci_array,
- int cnt)
-{
-
- struct iio_channel_info temp;
- int x, y;
-
- for (x = 0; x < cnt; x++)
- for (y = 0; y < (cnt - 1); y++)
- if ((*ci_array)[y].index > (*ci_array)[y+1].index) {
- temp = (*ci_array)[y + 1];
- (*ci_array)[y + 1] = (*ci_array)[y];
- (*ci_array)[y] = temp;
- }
-}
-
-/**
- * build_channel_array() - function to figure out what channels are present
- * @device_dir: the IIO device directory in sysfs
- * @
- **/
-inline int build_channel_array(const char *device_dir,
- struct iio_channel_info **ci_array,
- int *counter)
-{
- DIR *dp;
- FILE *sysfsfp;
- int count, i;
- struct iio_channel_info *current;
- int ret;
- const struct dirent *ent;
- char *scan_el_dir;
- char *filename;
-
- *counter = 0;
- ret = asprintf(&scan_el_dir, FORMAT_SCAN_ELEMENTS_DIR, device_dir);
- if (ret < 0) {
- ret = -ENOMEM;
- goto error_ret;
- }
- dp = opendir(scan_el_dir);
- if (dp == NULL) {
- ret = -errno;
- goto error_free_name;
- }
- while (ent = readdir(dp), ent != NULL)
- if (strcmp(ent->d_name + strlen(ent->d_name) - strlen("_en"),
- "_en") == 0) {
- ret = asprintf(&filename,
- "%s/%s", scan_el_dir, ent->d_name);
- if (ret < 0) {
- ret = -ENOMEM;
- goto error_close_dir;
- }
- sysfsfp = fopen(filename, "r");
- if (sysfsfp == NULL) {
- ret = -errno;
- free(filename);
- goto error_close_dir;
- }
- fscanf(sysfsfp, "%u", &ret);
- if (ret == 1)
- (*counter)++;
- fclose(sysfsfp);
- free(filename);
- }
- *ci_array = malloc(sizeof(**ci_array) * (*counter));
- if (*ci_array == NULL) {
- ret = -ENOMEM;
- goto error_close_dir;
- }
- seekdir(dp, 0);
- count = 0;
- while (ent = readdir(dp), ent != NULL) {
- if (strcmp(ent->d_name + strlen(ent->d_name) - strlen("_en"),
- "_en") == 0) {
- current = &(*ci_array)[count++];
- ret = asprintf(&filename,
- "%s/%s", scan_el_dir, ent->d_name);
- if (ret < 0) {
- ret = -ENOMEM;
- /* decrement count to avoid freeing name */
- count--;
- goto error_cleanup_array;
- }
- sysfsfp = fopen(filename, "r");
- if (sysfsfp == NULL) {
- free(filename);
- ret = -errno;
- goto error_cleanup_array;
- }
- fscanf(sysfsfp, "%u", ¤t->enabled);
- fclose(sysfsfp);
-
- if (!current->enabled) {
- free(filename);
- count--;
- continue;
- }
-
- current->scale = 1.0;
- current->offset = 0;
- current->name = strndup(ent->d_name,
- strlen(ent->d_name) -
- strlen("_en"));
- if (current->name == NULL) {
- free(filename);
- ret = -ENOMEM;
- goto error_cleanup_array;
- }
- /* Get the generic and specific name elements */
- ret = iioutils_break_up_name(current->name,
- ¤t->generic_name);
- if (ret) {
- free(filename);
- goto error_cleanup_array;
- }
- ret = asprintf(&filename,
- "%s/%s_index",
- scan_el_dir,
- current->name);
- if (ret < 0) {
- free(filename);
- ret = -ENOMEM;
- goto error_cleanup_array;
- }
- sysfsfp = fopen(filename, "r");
- fscanf(sysfsfp, "%u", ¤t->index);
- fclose(sysfsfp);
- free(filename);
- /* Find the scale */
- ret = iioutils_get_param_float(¤t->scale,
- "scale",
- device_dir,
- current->name,
- current->generic_name);
- if (ret < 0)
- goto error_cleanup_array;
- ret = iioutils_get_param_float(¤t->offset,
- "offset",
- device_dir,
- current->name,
- current->generic_name);
- if (ret < 0)
- goto error_cleanup_array;
- ret = iioutils_get_type(¤t->is_signed,
- ¤t->bytes,
- ¤t->bits_used,
- ¤t->shift,
- ¤t->mask,
- ¤t->be,
- device_dir,
- current->name,
- current->generic_name);
- }
- }
-
- closedir(dp);
- /* reorder so that the array is in index order */
- bsort_channel_array_by_index(ci_array, *counter);
-
- return 0;
-
-error_cleanup_array:
- for (i = count - 1; i >= 0; i--)
- free((*ci_array)[i].name);
- free(*ci_array);
-error_close_dir:
- closedir(dp);
-error_free_name:
- free(scan_el_dir);
-error_ret:
- return ret;
-}
-
-/**
- * find_type_by_name() - function to match top level types by name
- * @name: top level type instance name
- * @type: the type of top level instance being sort
- *
- * Typical types this is used for are device and trigger.
- **/
-inline int find_type_by_name(const char *name, const char *type)
-{
- const struct dirent *ent;
- int number, numstrlen;
-
- FILE *nameFile;
- DIR *dp;
- char thisname[IIO_MAX_NAME_LENGTH];
- char *filename;
-
- dp = opendir(iio_dir);
- if (dp == NULL) {
- printf("No industrialio devices available\n");
- return -ENODEV;
- }
-
- while (ent = readdir(dp), ent != NULL) {
- if (strcmp(ent->d_name, ".") != 0 &&
- strcmp(ent->d_name, "..") != 0 &&
- strlen(ent->d_name) > strlen(type) &&
- strncmp(ent->d_name, type, strlen(type)) == 0) {
- numstrlen = sscanf(ent->d_name + strlen(type),
- "%d",
- &number);
- /* verify the next character is not a colon */
- if (strncmp(ent->d_name + strlen(type) + numstrlen,
- ":",
- 1) != 0) {
- filename = malloc(strlen(iio_dir)
- + strlen(type)
- + numstrlen
- + 6);
- if (filename == NULL) {
- closedir(dp);
- return -ENOMEM;
- }
- sprintf(filename, "%s%s%d/name",
- iio_dir,
- type,
- number);
- nameFile = fopen(filename, "r");
- if (!nameFile) {
- free(filename);
- continue;
- }
- free(filename);
- fscanf(nameFile, "%s", thisname);
- fclose(nameFile);
- if (strcmp(name, thisname) == 0) {
- closedir(dp);
- return number;
- }
- }
- }
- }
- closedir(dp);
- return -ENODEV;
-}
-
-inline int _write_sysfs_int(char *filename, char *basedir, int val, int verify)
-{
- int ret;
- FILE *sysfsfp;
- int test;
- char *temp = malloc(strlen(basedir) + strlen(filename) + 2);
- if (temp == NULL)
- return -ENOMEM;
- sprintf(temp, "%s/%s", basedir, filename);
- sysfsfp = fopen(temp, "w");
- if (sysfsfp == NULL) {
- printf("failed to open %s\n", temp);
- ret = -errno;
- goto error_free;
- }
- fprintf(sysfsfp, "%d", val);
- fclose(sysfsfp);
- if (verify) {
- sysfsfp = fopen(temp, "r");
- if (sysfsfp == NULL) {
- printf("failed to open %s\n", temp);
- ret = -errno;
- goto error_free;
- }
- fscanf(sysfsfp, "%d", &test);
- fclose(sysfsfp);
- if (test != val) {
- printf("Possible failure in int write %d to %s%s\n",
- val,
- basedir,
- filename);
- ret = -1;
- }
- }
-error_free:
- free(temp);
- return ret;
-}
-
-int write_sysfs_int(char *filename, char *basedir, int val)
-{
- return _write_sysfs_int(filename, basedir, val, 0);
-}
-
-int write_sysfs_int_and_verify(char *filename, char *basedir, int val)
-{
- return _write_sysfs_int(filename, basedir, val, 1);
-}
-
-int _write_sysfs_string(char *filename, char *basedir, char *val, int verify)
-{
- int ret = 0;
- FILE *sysfsfp;
- char *temp = malloc(strlen(basedir) + strlen(filename) + 2);
- if (temp == NULL) {
- printf("Memory allocation failed\n");
- return -ENOMEM;
- }
- sprintf(temp, "%s/%s", basedir, filename);
- sysfsfp = fopen(temp, "w");
- if (sysfsfp == NULL) {
- printf("Could not open %s\n", temp);
- ret = -errno;
- goto error_free;
- }
- fprintf(sysfsfp, "%s", val);
- fclose(sysfsfp);
- if (verify) {
- sysfsfp = fopen(temp, "r");
- if (sysfsfp == NULL) {
- printf("could not open file to verify\n");
- ret = -errno;
- goto error_free;
- }
- fscanf(sysfsfp, "%s", temp);
- fclose(sysfsfp);
- if (strcmp(temp, val) != 0) {
- printf("Possible failure in string write of %s "
- "Should be %s "
- "written to %s\%s\n",
- temp,
- val,
- basedir,
- filename);
- ret = -1;
- }
- }
-error_free:
- free(temp);
-
- return ret;
-}
-
-/**
- * write_sysfs_string_and_verify() - string write, readback and verify
- * @filename: name of file to write to
- * @basedir: the sysfs directory in which the file is to be found
- * @val: the string to write
- **/
-int write_sysfs_string_and_verify(char *filename, char *basedir, char *val)
-{
- return _write_sysfs_string(filename, basedir, val, 1);
-}
-
-int write_sysfs_string(char *filename, char *basedir, char *val)
-{
- return _write_sysfs_string(filename, basedir, val, 0);
-}
-
-int read_sysfs_posint(char *filename, char *basedir)
-{
- int ret;
- FILE *sysfsfp;
- char *temp = malloc(strlen(basedir) + strlen(filename) + 2);
- if (temp == NULL) {
- printf("Memory allocation failed");
- return -ENOMEM;
- }
- sprintf(temp, "%s/%s", basedir, filename);
- sysfsfp = fopen(temp, "r");
- if (sysfsfp == NULL) {
- ret = -errno;
- goto error_free;
- }
- fscanf(sysfsfp, "%d\n", &ret);
- fclose(sysfsfp);
-error_free:
- free(temp);
- return ret;
-}
-
-int read_sysfs_float(char *filename, char *basedir, float *val)
-{
- float ret = 0;
- FILE *sysfsfp;
- char *temp = malloc(strlen(basedir) + strlen(filename) + 2);
- if (temp == NULL) {
- printf("Memory allocation failed");
- return -ENOMEM;
- }
- sprintf(temp, "%s/%s", basedir, filename);
- sysfsfp = fopen(temp, "r");
- if (sysfsfp == NULL) {
- ret = -errno;
- goto error_free;
- }
- fscanf(sysfsfp, "%f\n", val);
- fclose(sysfsfp);
-error_free:
- free(temp);
- return ret;
-}
+++ /dev/null
-This is a userspace application which accesses the adc via /dev/iio in continuous sampling mode.
-
-The application scans the scan_elements folder in /dev/iio/devices/iio:deviceX/scan_elements for enabled channels.
-
-Creates a data structure.
-
-Sets the buffer size. Enables the buffer. And reads from the dev file for the driver.
-
-The source code is located under kernel sources "drivers/staging/iio/Documentation/generic_buffer.c".
-
-How to compile:
-
-arm-arago-linux-gnueabi-gcc --static generic_buffer.c -o generic_buffer
-
-or
-
--gcc --static generic_buffer.c -o generic_buffer
-
-
-SOURCE: https://github.com/ZubairLK/adc-iio-continuous-sampling-userspace
\ No newline at end of file
+++ /dev/null
-echo 1 > /sys/bus/iio/devices/iio_sysfs_trigger/add_trigger
-echo 1 > /sys/bus/iio/devices/iio\:device0/scan_elements/in_voltage7_en
-echo 1 > /sys/bus/iio/devices/iio\:device0/scan_elements/in_voltage5_en
-generic_buffer -n TI-am335x-adc -t sysfstrig1 -l 128
-
+++ /dev/null
-echo 1 > /sys/bus/iio/devices/trigger0/trigger_now
+++ /dev/null
-//Code to blink an LED - just to illustrate that it's pretty easy
-//Only important thing is which name to address the LED by
-
-#include <stdio.h>
-#include <unistd.h>
-
-using namespace std;
-
-int main(int argc, char** argv) {
- FILE *LEDHandle = NULL;
- char *LEDBrightness = "/sys/class/leds/beaglebone:green:usr0/brightness";
- printf("\nStarting LED blink program wooo!\n");
- while(1){
- if((LEDHandle = fopen(LEDBrightness, "r+")) != NULL){
- fwrite("1", sizeof(char), 1, LEDHandle);
- fclose(LEDHandle);
- }
- sleep(1);
- if((LEDHandle = fopen(LEDBrightness, "r+")) != NULL){
- fwrite("0", sizeof(char), 1, LEDHandle);
- fclose(LEDHandle);
- }
- sleep(1);
- }
- return 0;
-}
-
-//Sample code that should read a pressure sensor pin (conversion factors
-//are just random numbers). Again pretty simple.
-
-#include STUFF
-
-double pressure(char *string) {
- int value = atoi(string);
- double millivolts = (value / 4096.0) * 1800; //convert input to volts
- double pressure = (millivolts - 500.0) / 10.0; //convert volts to pressure
- return pressure;
-}
-
-void main() {
- int fd = open("/sys/devices/platform/tsc/ain2", O_RDONLY); //open pin signal
- while (1) {
- char buffer[1024];
- int ret = read(fd, buffer, sizeof(buffer)); //get data
- if (ret != -1) {
- buffer[ret] = NULL;
- double kpa = pressure(buffer);
- printf("digital value: %s kilopascals: %f\n", buffer, kpa);
- lseek(fd, 0, 0);
- }
- sleep(1);
- }
- close(fd);
-}
\ No newline at end of file
+++ /dev/null
-/*
- * SimpleGPIO.cpp
- *
- * Modifications by Derek Molloy, School of Electronic Engineering, DCU
- * www.eeng.dcu.ie/~molloyd/
- * Almost entirely based on Software by RidgeRun:
- *
- * Copyright (c) 2011, RidgeRun
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the RidgeRun.
- * 4. Neither the name of the RidgeRun nor the
- * names of its contributors may be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY RIDGERUN ''AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL RIDGERUN BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "SimpleGPIO.h"
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <poll.h>
-
-/****************************************************************
- * gpio_export
- ****************************************************************/
-int gpio_export(unsigned int gpio)
-{
- int fd, len;
- char buf[MAX_BUF];
-
- fd = open(SYSFS_GPIO_DIR "/export", O_WRONLY);
- if (fd < 0) {
- perror("gpio/export");
- return fd;
- }
-
- len = snprintf(buf, sizeof(buf), "%d", gpio);
- write(fd, buf, len);
- close(fd);
-
- return 0;
-}
-
-/****************************************************************
- * gpio_unexport
- ****************************************************************/
-int gpio_unexport(unsigned int gpio)
-{
- int fd, len;
- char buf[MAX_BUF];
-
- fd = open(SYSFS_GPIO_DIR "/unexport", O_WRONLY);
- if (fd < 0) {
- perror("gpio/export");
- return fd;
- }
-
- len = snprintf(buf, sizeof(buf), "%d", gpio);
- write(fd, buf, len);
- close(fd);
- return 0;
-}
-
-/****************************************************************
- * gpio_set_dir
- ****************************************************************/
-int gpio_set_dir(unsigned int gpio, PIN_DIRECTION out_flag)
-{
- int fd;
- char buf[MAX_BUF];
-
- snprintf(buf, sizeof(buf), SYSFS_GPIO_DIR "/gpio%d/direction", gpio);
-
- fd = open(buf, O_WRONLY);
- if (fd < 0) {
- perror("gpio/direction");
- return fd;
- }
-
- if (out_flag == OUTPUT_PIN)
- write(fd, "out", 4);
- else
- write(fd, "in", 3);
-
- close(fd);
- return 0;
-}
-
-/****************************************************************
- * gpio_set_value
- ****************************************************************/
-int gpio_set_value(unsigned int gpio, PIN_VALUE value)
-{
- int fd;
- char buf[MAX_BUF];
-
- snprintf(buf, sizeof(buf), SYSFS_GPIO_DIR "/gpio%d/value", gpio);
-
- fd = open(buf, O_WRONLY);
- if (fd < 0) {
- perror("gpio/set-value");
- return fd;
- }
-
- if (value==LOW)
- write(fd, "0", 2);
- else
- write(fd, "1", 2);
-
- close(fd);
- return 0;
-}
-
-/****************************************************************
- * gpio_get_value
- ****************************************************************/
-int gpio_get_value(unsigned int gpio, unsigned int *value)
-{
- int fd;
- char buf[MAX_BUF];
- char ch;
-
- snprintf(buf, sizeof(buf), SYSFS_GPIO_DIR "/gpio%d/value", gpio);
-
- fd = open(buf, O_RDONLY);
- if (fd < 0) {
- perror("gpio/get-value");
- return fd;
- }
-
- read(fd, &ch, 1);
-
- if (ch != '0') {
- *value = 1;
- } else {
- *value = 0;
- }
-
- close(fd);
- return 0;
-}
-
-
-/****************************************************************
- * gpio_set_edge
- ****************************************************************/
-
-int gpio_set_edge(unsigned int gpio, char *edge)
-{
- int fd;
- char buf[MAX_BUF];
-
- snprintf(buf, sizeof(buf), SYSFS_GPIO_DIR "/gpio%d/edge", gpio);
-
- fd = open(buf, O_WRONLY);
- if (fd < 0) {
- perror("gpio/set-edge");
- return fd;
- }
-
- write(fd, edge, strlen(edge) + 1);
- close(fd);
- return 0;
-}
-
-/****************************************************************
- * gpio_fd_open
- ****************************************************************/
-
-int gpio_fd_open(unsigned int gpio)
-{
- int fd;
- char buf[MAX_BUF];
-
- snprintf(buf, sizeof(buf), SYSFS_GPIO_DIR "/gpio%d/value", gpio);
-
- fd = open(buf, O_RDONLY | O_NONBLOCK );
- if (fd < 0) {
- perror("gpio/fd_open");
- }
- return fd;
-}
-
-/****************************************************************
- * gpio_fd_close
- ****************************************************************/
-
-int gpio_fd_close(int fd)
-{
- return close(fd);
-}
-
-
-/****************************************************************
- * gpio_omap_mux_setup - Allow us to setup the omap mux mode for a pin
- ****************************************************************/
-int gpio_omap_mux_setup(const char *omap_pin0_name, const char *mode)
-{
- int fd;
- char buf[MAX_BUF];
- snprintf(buf, sizeof(buf), SYSFS_OMAP_MUX_DIR "%s", omap_pin0_name);
- fd = open(buf, O_WRONLY);
- if (fd < 0) {
- perror("failed to open OMAP_MUX");
- return fd;
- }
- write(fd, mode, strlen(mode) + 1);
- close(fd);
- return 0;
-}
\ No newline at end of file
+++ /dev/null
-/*
- * SimpleGPIO.h
- *
- * Copyright Derek Molloy, School of Electronic Engineering, Dublin City University
- * www.eeng.dcu.ie/~molloyd/
- *
- * Based on Software by RidgeRun
- * Copyright (c) 2011, RidgeRun
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the RidgeRun.
- * 4. Neither the name of the RidgeRun nor the
- * names of its contributors may be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY RIDGERUN ''AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL RIDGERUN BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef SIMPLEGPIO_H_
-#define SIMPLEGPIO_H_
-
- /****************************************************************
- * Constants
- ****************************************************************/
-
-#define SYSFS_GPIO_DIR "/sys/class/gpio"
-#define POLL_TIMEOUT (3 * 1000) /* 3 seconds */
-#define MAX_BUF 64
-#define SYSFS_OMAP_MUX_DIR "/sys/kernel/debug/omap_mux/"
-
-enum PIN_DIRECTION{
- INPUT_PIN=0,
- OUTPUT_PIN=1
-};
-
-enum PIN_VALUE{
- LOW=0,
- HIGH=1
-};
-
-/****************************************************************
- * gpio_export
- ****************************************************************/
-int gpio_export(unsigned int gpio);
-int gpio_unexport(unsigned int gpio);
-int gpio_set_dir(unsigned int gpio, PIN_DIRECTION out_flag);
-int gpio_set_value(unsigned int gpio, PIN_VALUE value);
-int gpio_get_value(unsigned int gpio, unsigned int *value);
-int gpio_set_edge(unsigned int gpio, char *edge);
-int gpio_fd_open(unsigned int gpio);
-int gpio_fd_close(int fd);
-int gpio_omap_mux_setup(const char *omap_pin0_name, const char *mode);
-
-#endif /* SIMPLEGPIO_H_ */
\ No newline at end of file
{
case ANALOG_TEST0:
{
+ //CheckSensor( sensor_id, *sensor value*);
+
static int count = 0;
d->value = count++;
break;
return d;
}
+/**
+ * Checks the sensor data for unsafe or unexpected results
+ * @param sensor_id - The ID of the sensor
+ *
+*
+void CheckSensor( SensorId sensor_id)
+{
+ switch (sensor_id)
+ {
+ case ANALOG_TEST0:
+ {
+ if( *sensor value* > ANALOG_TEST0_SAFETY)
+ {
+ LogEx(LOGERR, GetData, Sensor analog_test0 is above the safe value);
+ //new log function that stops actuators?
+ }
+ //Also include a warning level?
+ else if( *sensor value* > ANALOG_TEST0_WARN)
+ {
+ LogEx(LOGWARN, GetData, Sensor analog_test0);
+ }
+ }
+ }
+
+
+*/
+
/**
* Destroy a sensor
{
s->write_index = 0;
s->id = id;
+ s->points_written = 0;
+ s->points_read = 0;
#define FILENAMESIZE 3
char filename[FILENAMESIZE];
Fatal("Error collecting data");
}
s->write_index += 1;
- //TODO: s->points_read not used?
}
//Log(LOGDEBUG, "Filled buffer");
{
Fatal("Wrote %d data points and expected to write %d to \"%s\" - %s", amount_written, SENSOR_DATABUFSIZ, strerror(errno));
}
- s->points_stored += amount_written;
+ s->points_written += amount_written;
//Log(LOGDEBUG, "Wrote %d data points for sensor %d", amount_written, s->id);
pthread_mutex_unlock(&(s->mutex));
// End of critical section
return NULL;
}
+/**
+ * Get position in a binary sensor file with a timestamp using a binary search
+ * @param s - Sensor to use
+ * @param time_stamp - Timestamp
+ * @param count - If not NULL, used to provide number of searches required
+ * @param found - If not NULL, set to the closest DataPoint
+ * @returns Integer giving the *closest* index in the file
+ * TODO: Refactor or replace?
+ */
+int FindTime(Sensor * s, double time_stamp, int * count, DataPoint * found)
+{
+ DataPoint d;
+
+ int lower = 0;
+ int upper = s->points_written - 1;
+ int index = 0;
+ if (count != NULL)
+ *count = 0;
+
+ while (upper - lower > 1)
+ {
+ index = lower + ((upper - lower)/2);
+
+ // Basically anything with fseek is critical; if we don't make it critical the sensor thread may alter data at a random point in the file!
+ // CRITICAL SECTION (May need to rethink how this is done, but I can't see how to do it without fseek :S)
+ // Regarding the suggestion that we have 2 file pointers; one for reading and one for writing:
+ // That seems like it will work... but we will have to be very careful and test it first
+ pthread_mutex_lock(&s->mutex);
+ fseek(s->file, index*sizeof(DataPoint), SEEK_SET);
+ int amount_read = fread(&d, sizeof(DataPoint), 1, s->file);
+ pthread_mutex_unlock(&s->mutex);
+
+ if (amount_read != 1)
+ {
+ Fatal("Couldn't read single data point from sensor %d", s->id);
+ }
+
+ if (d.time_stamp > time_stamp)
+ {
+ upper = index;
+ }
+ else if (d.time_stamp < time_stamp)
+ {
+ lower = index;
+ }
+ if (count != NULL)
+ *count += 1;
+ }
+
+ if (found != NULL)
+ *found = d;
+
+ return index;
+
+}
+
+/**
+ * Print sensor data between two indexes in the file, using a given format
+ * @param s - Sensor to use
+ * @param start - Start index
+ * @param end - End index
+ * @param output_type - JSON, CSV or TSV output format
+ */
+void PrintData(Sensor * s, int start, int end, OutputType output_type)
+{
+ DataPoint buffer[SENSOR_QUERYBUFSIZ];
+ int index = start;
+
+ if (output_type == JSON)
+ {
+ FCGI_JSONValue("[");
+ }
+
+
+ while (index < end)
+ {
+ int to_read = end - index;
+ if (to_read > SENSOR_QUERYBUFSIZ)
+ {
+ to_read = SENSOR_QUERYBUFSIZ;
+ }
+
+ int amount_read = 0;
+ // CRITICAL SECTION
+ pthread_mutex_lock(&(s->mutex));
+
+ fseek(s->file, index*sizeof(DataPoint), SEEK_SET);
+ amount_read = fread(buffer, sizeof(DataPoint), to_read, s->file);
+
+ pthread_mutex_unlock(&(s->mutex));
+ // End critical section
+
+ if (amount_read != to_read)
+ {
+ Fatal("Failed to read %d DataPoints from sensor %d; read %d instead", to_read, s->id, amount_read);
+ }
+
+ // Print the data
+ for (int i = 0; i < amount_read; ++i)
+ {
+ //TODO: Reformat?
+ switch (output_type)
+ {
+ case JSON:
+ FCGI_JSONValue("[%f, %f]", buffer[i].time_stamp, buffer[i].value);
+ if (i+1 < amount_read)
+ FCGI_JSONValue(",");
+ break;
+ case CSV:
+ FCGI_PrintRaw("%f,%f\n", buffer[i].time_stamp, buffer[i].value);
+ break;
+ case TSV:
+ default:
+ FCGI_PrintRaw("%f\t%f\n", buffer[i].time_stamp, buffer[i].value);
+ break;
+ }
+ }
+ index += amount_read;
+ }
+
+ if (output_type == JSON)
+ {
+ FCGI_JSONValue("]");
+ }
+}
+
/**
* Fill buffer with most recent sensor data
+ * TODO: This may be obselete; remove?
* @param s - Sensor to use
* @param buffer - Buffer to fill
* @param bufsiz - Size of buffer to fill
return g_sensors+id;
}
-/*
- * Behaviour:
- * Dump true:
- * - from < 0: From beginning of file, else from that point onwards (0-indexed)
- * - count < 0: All points available, else *at most* that many points
- * Dump false:
- * - from < 0: From the end of file (last available points), else from that point onwards (0-indexed)
- * - count < 0: Default buffer size (SENSOR_QUERYBUFSIZ), else *at most* that many points
- */
-void Sensor_Handler2(FCGIContext *context, char *params)
-{
- const char *key, *value;
- int id = -1, from = -1, count = -1;
- bool dump = false;
-
- //Lazy checking
- while ((params = FCGI_KeyPair(params, &key, &value))) {
- if (!strcmp(key, "id") && *value) {
- char *end;
- id = strtol(value, &end, 10);
- if (*end != '\0')
- id = -1;
- } else if (!strcmp(key, "dump")) {
- dump = !dump;
- } else if (!strcmp(key, "from") && *value) {
- from = strtol(value, NULL, 10);
- } else if (!strcmp(key, "count") && *value) {
- count = strtol(value, NULL, 10);
- }
- }
-
- if (id < 0 || id >= NUMSENSORS) {
- FCGI_RejectJSON(context, "Invalid sensor id specified.");
- return;
- }
-
- Sensor *sensor = &g_sensors[id];
- DataPoint buffer[SENSOR_QUERYBUFSIZ];
- int amount_read = 0, total = 0;
-
- //Critical section
- pthread_mutex_lock(&(sensor->mutex));
- if (from >= sensor->points_stored) {
- FCGI_RejectJSONEx(context, STATUS_OUTOFRANGE, "Invalid range specified.");
- } else if (dump) {
- from = (from < 0) ? 0 : from;
- count = (count < 0) ? sensor->points_stored : count;
-
- FCGI_PrintRaw("Content-type: text/plain\r\n"
- "Content-disposition: attachment;filename=%d.csv\r\n\r\n", id);
-
- fseek(sensor->file, sizeof(DataPoint) * from, SEEK_SET);
- //Force download with content-disposition
- do {
- amount_read = fread(buffer, sizeof(DataPoint), SENSOR_QUERYBUFSIZ, sensor->file);
- for (int i = 0; i < amount_read && total < count; i++, total++) {
- FCGI_PrintRaw("%f\t%f\n", buffer[i].time_stamp, buffer[i].value);
- }
- } while (amount_read > 0 && total < count);
- } else {
- count = (count < 0) ? SENSOR_QUERYBUFSIZ : count;
- if (from < 0) {
- from = sensor->points_stored - count;
- if (from < 0)
- from = 0;
- }
- fseek(sensor->file, sizeof(DataPoint) * from, SEEK_SET);
-
- FCGI_BeginJSON(context, STATUS_OK);
- FCGI_JSONLong("id", id);
- FCGI_JSONKey("data");
- FCGI_JSONValue("[");
- while (total < count) {
- amount_read = fread(buffer, sizeof(DataPoint), SENSOR_QUERYBUFSIZ, sensor->file);
- if (amount_read > 0) {
- FCGI_JSONValue("[%f, %f]", buffer[0].time_stamp, buffer[0].value);
- total++;
- for (int i = 1; i < amount_read && total < count; i++, total++)
- FCGI_JSONValue(", [%f, %f]", buffer[i].time_stamp, buffer[i].value);
- } else {
- break;
- }
- }
- FCGI_JSONValue("]");
-
- FCGI_JSONLong("total_points", sensor->points_stored);
- FCGI_JSONLong("next_point", from + total);
- FCGI_EndJSON();
- }
- pthread_mutex_unlock(&(sensor->mutex));
- //End critical section
-}
-
/**
* Handle a request to the sensor module
* @param context - The context to work in
* @param params - Parameters passed
+ * TODO: Seriously need to write more helper functions and decrease the size of this function!
*/
void Sensor_Handler(FCGIContext *context, char * params)
{
- DataPoint buffer[SENSOR_QUERYBUFSIZ];
StatusCodes status = STATUS_OK;
- enum {DEFAULT, DUMP} operation = DEFAULT;
+ OutputType output_type = JSON;
+
+
const char * key; const char * value;
Sensor * sensor = NULL;
+ struct timeval now;
+ gettimeofday(&now, NULL);
+
+ double start_time = -1;
+ double end_time = -1;
+ double current_time = (now.tv_sec - g_options.start_time.tv_sec) + 1e-6*(now.tv_usec - g_options.start_time.tv_usec);
+ bool seek_time = false;
+ bool points_specified = false;
+ int query_size = SENSOR_QUERYBUFSIZ;
+ int start_index = -1;
+ int end_index = -1;
+
+
while ((params = FCGI_KeyPair(params, &key, &value)) != NULL)
{
Log(LOGDEBUG, "Got key=%s and value=%s", key, value);
break;
}
}
- else if (strcmp(key, "dump") == 0)
+ else if (strcmp(key, "format") == 0)
+ {
+ if (strcmp(value, "json") == 0)
+ output_type = JSON;
+ else if (strcmp(value, "csv") == 0)
+ output_type = CSV;
+ else if (strcmp(value, "tsv") == 0)
+ output_type = TSV;
+ }
+ else if (strcmp(key, "points") == 0)
{
- if (operation != DEFAULT)
+ points_specified = true;
+ if (strcmp(value, "all") == 0)
{
- Log(LOGERR, "Operation already specified!");
+ query_size = sensor->points_written;
+ }
+ else
+ {
+ char * end;
+ query_size = strtol(value, &end, 10);
+ if (*end != '\0')
+ {
+ Log(LOGERR, "Require \"all\" or an integer value: %s = %s", key, value);
+ status = STATUS_ERROR;
+ break;
+ }
+ }
+
+ }
+ else if (strcmp(key, "start_time") == 0)
+ {
+ seek_time = true;
+ char * end;
+ start_time = strtod(value, &end);
+ if (*end != '\0')
+ {
+ Log(LOGERR, "Require a double: %s = %s", key, value);
status = STATUS_ERROR;
break;
+ }
+
+ // Treat negative values as being relative to the current time
+ if (start_time < 0)
+ {
+ start_time = current_time + start_time;
}
- operation = DUMP;
+ start_time = floor(start_time);
+ }
+ else if (strcmp(key, "end_time") == 0)
+ {
+ seek_time = true;
+ char * end;
+ end_time = strtod(value, &end);
+ if (*end != '\0')
+ {
+ Log(LOGERR, "Require a double: %s = %s", key, value);
+ status = STATUS_ERROR;
+ break;
+ }
+
+ // Treat negative values as being relative to the current time
+ if (end_time < 0)
+ {
+ end_time = current_time + end_time;
+ }
+ end_time = ceil(end_time);
+ }
+ // For backward compatability:
+ else if (strcmp(key, "dump") == 0)
+ {
+ output_type = TSV;
+ query_size = sensor->points_written+1;
}
else
{
FCGI_RejectJSON(context, "Invalid input parameters");
return;
}
-
- switch (operation)
+
+
+ if (seek_time)
{
- case DUMP:
+ if (end_time < 0 && !points_specified)
+ end_index = sensor->points_written;
+ else
{
- //Force download with content-disposition
- FCGI_PrintRaw("Content-type: text/plain\r\n"
- "Content-disposition: attachment;filename=%d.csv\r\n\r\n",
- sensor->id);
- //CRITICAL SECTION
- pthread_mutex_lock(&(sensor->mutex));
- fseek(sensor->file, 0, SEEK_SET);
- int amount_read = 0;
- do
- {
- amount_read = fread(buffer, sizeof(DataPoint), SENSOR_QUERYBUFSIZ, sensor->file);
- for (int i = 0; i < amount_read; ++i)
- {
- FCGI_PrintRaw("%f\t%f\n", buffer[i].time_stamp, buffer[i].value);
- }
-
- }
- while (amount_read == SENSOR_QUERYBUFSIZ);
- pthread_mutex_unlock(&(sensor->mutex));
- // end critical section
- break;
+ int count = 0; DataPoint d;
+ end_index = FindTime(sensor, end_time, &count, &d);
+ Log(LOGDEBUG, "FindTime - Looked for %f; found [%f,%f] after %d iterations; sensor %d, position %d", end_time, d.time_stamp, d.value, count, sensor->id, end_index);
}
- default:
+ if (start_time < 0)
+ start_time = 0;
+ else
{
- FCGI_BeginJSON(context, status);
- FCGI_JSONPair(key, value); // should spit back sensor ID
- //Log(LOGDEBUG, "Call Sensor_Query...");
- int amount_read = Sensor_Query(sensor, buffer, SENSOR_QUERYBUFSIZ);
- //Log(LOGDEBUG, "Read %d DataPoints", amount_read);
- //Log(LOGDEBUG, "Produce JSON response");
+ int count = 0; DataPoint d;
+ start_index = FindTime(sensor, start_time, &count, &d);
+ Log(LOGDEBUG, "FindTime - Looked for %f; found [%f,%f] after %d iterations; sensor %d, position %d", start_time, d.time_stamp, d.value, count, sensor->id, start_index);
+ }
+
+ if (points_specified)
+ end_index = start_index + query_size;
+ }
+ else
+ {
+ start_index = sensor->points_written - query_size;
+
+ end_index = sensor->points_written;
+ }
+
+ if (start_index < 0)
+ {
+ Log(LOGNOTE, "start_index = %d => Clamped to 0", start_index);
+ start_index = 0;
+ }
+ if (end_index > sensor->points_written)
+ {
+ Log(LOGNOTE, "end_index = %d => Clamped to %d", end_index, sensor->points_written);
+ end_index = sensor->points_written;
+ }
+
+ switch (output_type)
+ {
+ case JSON:
+ FCGI_BeginJSON(context, status);
+ FCGI_JSONLong("id", sensor->id);
FCGI_JSONKey("data");
- FCGI_JSONValue("[");
- for (int i = 0; i < amount_read; ++i)
- {
- //TODO: Consider; is it better to give both tv_sec and tv_usec to the client seperately, instead of combining here?
-
- FCGI_JSONValue("[%f, %f]", buffer[i].time_stamp, buffer[i].value);
- if (i+1 < amount_read)
- FCGI_JSONValue(",");
- }
- FCGI_JSONValue("]");
- //Log(LOGDEBUG, "Done producing JSON response");
- FCGI_EndJSON();
+ PrintData(sensor, start_index, end_index, output_type);
+ FCGI_EndJSON();
+ break;
+ default:
+ FCGI_PrintRaw("Content-type: text/plain\r\n\r\n");
+ PrintData(sensor, start_index, end_index, output_type);
+ //Force download with content-disposition
+ // Sam: This is cool, but I don't think we should do it
+ // - letting the user view it in the browser and then save with their own filename is more flexible
+ //"Content-disposition: attachment;filename=%d.csv\r\n\r\n", sensor->id);
break;
- }
}
+
}
/**
/** Number of sensors **/
#define NUMSENSORS 4
+/** Safety Values for sensors **/
+#define ANALOG_TEST0_SAFETY 1000
+#define ANALOG_TEST1_SAFETY 1000
+#define DIGITAL_TEST0_SAFETY 1
+#define DIGITAL_TEST1_SAFETY 1
+
+
typedef enum SensorId {
ANALOG_TEST0,
ANALOG_TEST1,
DIGITAL_TEST1
} SensorId;
+typedef enum
+{
+ JSON, // JSON data
+ CSV, // Comma seperated vector
+ TSV // Tab seperated vector
+} OutputType;
+
/** Human readable names for the sensors **/
extern const char * g_sensor_names[NUMSENSORS];
int write_index;
/** Number of points read **/
long points_read;
+ /** Number of points written to file **/
+ long points_written;
/** Binary file to write data into when buffer is full **/
FILE * file;
/** Number of data points stored in file **/
int p[3];
p[0] = CV_IMWRITE_JPEG_QUALITY;
- p[1] = 10; //quality value. 0-100
+ p[1] = 50; //quality value. 0-100
p[2] = 0;
frame = cvQueryFrame(capture);
if( frame == NULL)
return 0; //error
cvSaveImage("../web/images/test.JPG",frame,p);
+ cvReleaseImageHeader(&frame);
return 1;
}