#include "pwm.h"
char pin_dir = "/sys/class/gpio/gpio"; //move these
-int pwm_active = 0;
/** Sets a GPIO pin to the desired value
* @param value - the value (1 or 0) to write to the pin
* @param pin_num - the number of the pin (refer to electronics team)
*/
+//also need to export and set pin direction
+
void SetPin(int value, int pin_num) {
int pin;
char buffer[10];
--- /dev/null
+#include "gpio.h"
+#include "pwm.h"
+
+void Actuator_SetValue(Actuator * a, double value)
+{
+ // Set time stamp
+ struct timeval t;
+ gettimeofday(&t, NULL);
+
+ DataPoint d = {TIMEVAL_DIFF(t, g_options.start_time), value};
+ switch (a->id)
+ {
+ case ACTUATOR_TEST0: //LED actuator test code, should blink onboard LED next to Ethernet port
+ FILE *LEDHandle = NULL; //code reference: http://learnbuildshare.wordpress.com/2013/05/19/beaglebone-black-controlling-user-leds-using-c/
+ char *LEDBrightness = "/sys/class/leds/beaglebone\:green\:usr0/brightness";
+ if(value == 1) {
+ if((LEDHandle = fopen(LEDBrightness, "r+")) != NULL) {
+ fwrite("1", sizeof(char), 1, LEDHandle);
+ fclose(LEDHandle);
+ }
+ else if(value == 0) {
+ if((LEDHandle = fopen(LEDBrightness, "r+")) != NULL) {
+ fwrite("0", sizeof(char), 1, LEDHandle);
+ fclose(LEDHandle);
+ }
+ else perror("Pin value should be 1 or 0");
+ break;
+ case ACTUATOR_TEST1:
+ // Quick actuator function for testing pins
+ // GPIOPin can be either passed as an argument, or defined here (as pins won't change)
+ // First way is better and more generalised
+ int GPIOPin = 13;
+ // Modify this to only export on first run, unexport on shutdown
+ pinExport(setValue, GPIOString);
+ pinDirection(GPIODirection, setValue);
+ pinSet(value, GPIOValue, setValue);
+ pinUnexport(setValue, GPIOString);
+ break;
+ case ACTUATOR_TEST2:
+ if (pwminit == 0) { //if inactive, start the pwm module
+ pwm_init();
+ }
+ if (pwmstart == 0) {
+ pwm_start();
+ pwm_set_period(FREQ); //50Hz defined in pwm header file
+ }
+ if(value >= 0 && value <= 1000) {
+ double duty = value/1000 * 100; //convert pressure to duty percentage
+ pwm_set_duty((int)duty); //set duty percentage for actuator
+ }
+ }
+ Log(LOGDEBUG, "Actuator %s set to %f", g_actuator_names[a->id], value);
+ // Record the value
+ Data_Save(&(a->data_file), &d, 1);
+}
\ No newline at end of file
-char adc_dir = "/sys/devices/platform/tsc/ain";
-char pin_dir = "/sys/class/gpio/gpio";
-
-/** Open an ADC and return the voltage value from it
-* @param adc_num - ADC number, ranges from 0 to 7 on the Beaglebone
- @return the converted voltage value if successful
-*/
-
-//TODO: create a function to lookup the ADC or pin number instead of manually
-// specifying it here (so we can keep all the numbers in one place)
-
-int OpenAnalog(int adc_num)
-{
- char adc_path[40];
- snprintf(adc_path, sizeof(adc_path), "%s%d", adc_dir, adc_num); //construct ADC path
- int sensor = open(adc_path, O_RDONLY);
- char buffer[128]; //I think ADCs are only 12 bits (0-4096)
- int read = read(sensor, buffer, sizeof(buffer); //buffer can probably be smaller
- if (read != -1) {
- buffer[read] = NULL;
- int value = atoi(buffer);
- double convert = (value/4096) * 1000; //random conversion factor, will be different for each sensor
- //lseek(sensor, 0, 0);
- close(sensor);
- return convert;
- }
- else {
- perror("Failed to get value from sensor");
- close(sensor);
- return -1;
- }
-}
-
-/** Open a digital pin and return the value from it
-* @param pin_num - pin number, specified by electronics team
- @return 1 or 0 if reading was successful
-*/
-
-int OpenDigital(int pin_num)
-{
- char pin_path[40];
- snprintf(pin_path, sizeof(pin_path), "%s%d%s", pin_dir, pin_num, "/value"); //construct pin path
- int pin = open(pin_path, O_RDONLY);
- char ch;
- lseek(fd, 0, SEEK_SET);
- int read = read(pin, &ch, sizeof(ch);
- if (read != -1) {
- if (ch != '0') {
- close(pin);
- return 1;
- }
- else {
- close(pin);
- return 0;
- }
- else {
- perror("Failed to get value from pin");
- close(pin);
- return -1;
- }
-}
+#include "gpio.h"
/**
* Read a DataPoint from a Sensor; block until value is read
switch (s->id)
{
case ANALOG_TEST0:
- d->value = OpenAnalog(0); //ADC #0 on the Beaglebone
+ d->value = ADCRead(0); //ADC #0 on the Beaglebone
break;
case ANALOG_TEST1:
{
- d->value = OpenAnalog(1);
+ d->value = ADCRead(1);
break;
}
case ANALOG_FAIL0:
//Gives a value between -5 and 5
break;
case DIGITAL_TEST0:
- d->value = openDigital(0); //replace 0 with correct pin number
+ d->value = pinRead(0); //replace 0 with correct pin number
break;
case DIGITAL_TEST1:
- d->value = openDigital(1); //replace 1 with correct pin number
+ d->value = pinRead(1); //replace 1 with correct pin number
break;
case DIGITAL_FAIL0:
if( rand() % 100 > 98)
--- /dev/null
+#include "gpio.h"
+
+void pinExport(int GPIOPin) {
+ FILE *myOutputHandle = NULL;
+ char GPIOString[4];
+ char setValue[4];
+ sprintf(GPIOString, "%d", GPIOPin);
+ if ((myOutputHandle = fopen(exportPath, "ab")) == NULL){
+ Log(LOGERR, "Unable to export GPIO pin %f\n", GPIOPin);
+ }
+ strcpy(setValue, GPIOString);
+ fwrite(&setValue, sizeof(char), 2, myOutputHandle);
+ fclose(myOutputHandle);
+}
+
+void pinDirection(int GPIOPin, int io) {
+ char setValue[4];
+ char GPIODirection[64];
+ FILE *myOutputHandle = NULL;
+ snprintf(GPIODirection, sizeof(GPIODirection), "%s%d%s", directionPath, GPIOPin, "/direction");
+ if ((myOutputHandle = fopen(GPIODirection, "rb+")) == NULL){
+ Log(LOGERR, "Unable to open direction handle for pin %f\n", GPIOPin);
+ }
+ if (io == 1) {
+ strcpy(setValue,"out");
+ fwrite(&setValue, sizeof(char), 3, myOutputHandle);
+ else if (io == 0) {
+ strcpy(setValue,"in");
+ fwrite(&setValue, sizeof(char), 2, myOutputHandle);
+ else Log(LOGERR, "GPIO direction must be 1 or 0\n");
+ fclose(myOutputHandle);
+}
+
+void pinSet(double value, int GPIOPin) {
+ int val = (int)value;
+ char GPIOValue[64];
+ char setValue[4];
+ FILE *myOutputHandle = NULL;
+ snprintf(GPIOValue, sizeof(GPIOValue), "%s%d%s", valuePath, GPIOPin, "/value");
+ if (val == 1) {
+ if ((myOutputHandle = fopen(GPIOValue, "rb+")) == NULL){
+ Log(LOGERR, "Unable to open value handle for pin %f\n", GPIOPin);
+ }
+ strcpy(setValue, "1"); // Set value high
+ fwrite(&setValue, sizeof(char), 1, myOutputHandle);
+ }
+ else if (val == 0){
+ if ((myOutputHandle = fopen(GPIOValue, "rb+")) == NULL){
+ Log(LOGERR, "Unable to open value handle for pin %f\n", GPIOPin);
+ }
+ strcpy(setValue, "0"); // Set value low
+ fwrite(&setValue, sizeof(char), 1, myOutputHandle);
+ }
+ else Log(LOGERR, "GPIO value must be 1 or 0\n");
+ fclose(myOutputHandle);
+}
+
+/** Open an ADC and return the voltage value from it
+* @param adc_num - ADC number, ranges from 0 to 7 on the Beaglebone
+ @return the converted voltage value if successful
+*/
+
+//TODO: create a function to lookup the ADC or pin number instead of manually
+// specifying it here (so we can keep all the numbers in one place)
+
+int ADCRead(int adc_num)
+{
+ char adc_path[64];
+ snprintf(adc_path, sizeof(adc_path), "%s%d", ADCPath, adc_num); // Construct ADC path
+ int sensor = open(adc_path, O_RDONLY);
+ char buffer[64]; // I think ADCs are only 12 bits (0-4096), buffer can probably be smaller
+ int read = read(sensor, buffer, sizeof(buffer);
+ if (read != -1) {
+ buffer[read] = NULL;
+ int value = atoi(buffer);
+ double convert = (value/4096) * 1000; // Random conversion factor, will be different for each sensor (get from datasheets)
+ // lseek(sensor, 0, 0); (I think this is uneeded as we are reopening the file on each sensor read; if sensor is read continously we'll need this
+ close(sensor);
+ return convert;
+ }
+ else {
+ Log(LOGERR, "Failed to get value from ADC %f\n", adc_num);
+ close(sensor);
+ return -1;
+ }
+}
+
+/** Open a digital pin and return the value from it
+* @param pin_num - pin number, specified by electronics team
+ @return 1 or 0 if reading was successful
+*/
+
+int pinRead(int GPIOPin)
+{
+ char GPIOValue[64];
+ snprintf(GPIOValue, sizeof(GPIOValue), "%s%d%s", valuePath, GPIOPin, "/value"); //construct pin path
+ int pin = open(GPIOValue, O_RDONLY);
+ char ch;
+ lseek(fd, 0, SEEK_SET);
+ int read = read(pin, &ch, sizeof(ch);
+ if (read != -1) {
+ if (ch != '0') {
+ close(pin);
+ return 1;
+ }
+ else {
+ close(pin);
+ return 0;
+ }
+ else {
+ Log(LOGERR, "Failed to get value from pin %f\n", GPIOPin);
+ close(pin);
+ return -1;
+ }
+}
+
+void pinUnexport(int GPIOPin) {
+ char setValue[4];
+ char GPIOString[4];
+ sprintf(GPIOString, "%d", GPIOPin);
+ FILE *myOutputHandle = NULL;
+ if ((myOutputHandle = fopen(unexportPath, "ab")) == NULL) {
+ Log(LOGERR, "Couldn't unexport GPIO pin %f\n", GPIOPin);
+ }
+ strcpy(setValue, GPIOString);
+ fwrite(&setValue, sizeof(char), 2, myOutputHandle);
+ fclose(myOutputHandle);
+}
\ 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 exportPath "/sys/class/gpio/export";
+#define unexportPath "/sys/class/gpio/unexport";
+#define valuePath "/sys/class/gpio/gpio";
+#define directionPath "/sys/class/gpio/gpio";
+#define ADCPath "/sys/devices/platform/tsc/ain";
+
+void pinExport(int GPIOPin);
+void pinDirection(int GPIOPin, int io);
+void pinSet(double value, int GPIOPin);
+void pinUnexport(int GPIOPin);
+int pinRead(int GPIOPin);
+int ADCRead(int adc_num);
\ No newline at end of file
pwm drivers reference:
http://processors.wiki.ti.com/index.php/AM335x_PWM_Driver%27s_Guide */
-void pwm_init(void)
-{
+static int pwminit = 0;
+static int pwmstart = 0;
+
+void pwm_init(void) {
FILE *pwm_mux;
int i;
volatile uint32_t *epwmss1;
epwmss1[OFFSET_1 / sizeof(uint32_t)] = 0x2;
}
close(fd);
-
- pwm_mux = fopen("/sys/kernel/debug/omap_mux/gpmc_a2", "w");
+ pwminit = 1;
+ pwm_mux = fopen(PWMMuxPath, "w");
fprintf(pwm_mux, "6"); // pwm is mux mode 6
fclose(pwm_mux);
}
//depends how many pwm modules we have to run
//TODO:
-void pwm_start(void)
-{
+void pwm_start(void) {
FILE *pwm_run;
- pwm_run = fopen("/sys/class/pwm/ehrpwm.1:0/run", "w");
+ pwm_run = fopen(PWMRunPath, "w");
fprintf(pwm_run, "1");
fclose(pwm_run);
+ pwmstart = 1;
}
-void pwm_stop(void)
-{
+void pwm_stop(void) {
FILE *pwm_run;
-
- pwm_run = fopen("/sys/class/pwm/ehrpwm.1:0/run", "w");
+ pwm_run = fopen(PWMRunPath, "w");
fprintf(pwm_run, "0");
fclose(pwm_run);
+ pwmstart = 0;
}
//duty_percent is just a regular percentage (i.e. 50 = 50%)
-void pwm_set_duty(int duty_percent)
-{
+void pwm_set_duty(int duty_percent) {
FILE *pwm_duty;
-
- pwm_duty = fopen("/sys/class/pwm/ehrpwm.1:0/duty_percent", "w");
+ pwm_duty = fopen(PWMDutyPath, "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)
-{
+void pwm_set_period(int freq) {
FILE *pwm_period;
-
- pwm_period = fopen("/sys/class/pwm/ehrpwm.1:0/period_freq", "w");
+ pwm_period = fopen(PWMFreqPath, "w");
fprintf(pwm_period, "%d", freq);
fclose(pwm_period);
}
\ No newline at end of file
#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)
-#define FREQ 50 //50Hz pwm frequency for pressure regulator
+#define START 0x44e00000 // see datasheet of AM335x
+#define LENGTH 1024
+#define OFFSET_1 0xcc // offset of PWM1 clock (see datasheet of AM335x p.1018)
+#define FREQ 50 //50Hz pwm frequency for pressure regulator
+#define PWMMuxPath "/sys/kernel/debug/omap_mux/gpmc_a2"
+#define PWMRunPath "/sys/class/pwm/ehrpwm.1:0/run"
+#define PWMDutyPath "/sys/class/pwm/ehrpwm.1:0/duty_percent"
+#define PWMFreqPath "/sys/class/pwm/ehrpwm.1:0/period_freq"
void pwm_init(void);
void pwm_start(void);
22:58 -!- jtanx [
[email protected]] has quit ["ChatZilla 0.9.90.1 [Firefox 23.0.1/20130814063812]"]
+--- Day changed Tue Sep 17 2013
+09:50 -!- jtanx [
[email protected]] has quit ["ChatZilla 0.9.90.1 [Firefox 23.0.1/20130814063812]"]
+15:34 < jtanx> to run the software not as root, we need to at least add it to the 'video' group so it can access the webcam
+15:34 < jtanx> not sure about the sensors
+15:34 < jtanx> eg sudo usermod -a -G video debian
+15:38 < sam_moore> Ah, cool
+15:39 < sam_moore> I think I worked out how to get around the camera crashing without killing the rest of the program, won't implement it for a while though
+15:57 -!- jtanx_ is now known as jtanx
+16:46 -!- jtanx_ is now known as jtanx
+18:02 -!- jtanx [
[email protected]] has quit ["ChatZilla 0.9.90.1 [Firefox 23.0.1/20130814063812]"]
+23:18 < Callum> hey
+23:23 < sam_moore> Hi
+23:24 < Callum> do you know how to do the second question of that assignment?
+23:24 < Callum> https://docs.google.com/file/d/0B241H5liqVlnbHZETXRZX1Mwd1k/edit?usp=sharing
+23:25 < Callum> rather remember
+23:35 < sam_moore> Well, you can eliminate any epsilon^2 when you substitute those formulae for v into the formula for gamma
+23:35 < sam_moore> Then it has a really simple taylor expansion
+23:35 < Callum> i might have to hunt you down tomorrow to show me. or maybe il be able to think straight tomorrow.
+23:38 < sam_moore> You can express one of the epsilons in terms of the other one after that
+23:40 < sam_moore> I'm pretty busy tomorrow
+23:42 < Callum> mhmm. so no free time at all? coz im fairly free
+23:49 < sam_moore> Not from 8:00am until 5:00pm
+23:49 < Callum> ohwow. you are busy
+23:49 < Callum> im still unsure what im meant to be applyign the taylor expansion to
+23:53 < sam_moore> Substitute in the suggested formulae to gamma, simplify it, then apply a taylor expansion
+23:53 < sam_moore> Anyway, goodnight, good luck
+23:53 < Callum> night
+--- Day changed Wed Sep 18 2013
+09:11 -!- jtanx [
[email protected]] has quit ["ChatZilla 0.9.90.1 [Firefox 23.0.1/20130814063812]"]
+21:03 -!- jtanx [
[email protected]] has quit ["ChatZilla 0.9.90.1 [Firefox 24.0/20130910160258]"]
+--- Day changed Thu Sep 19 2013
+16:08 < jtanx> one thing I forgot to mention - the latest version on git uses syslog
+16:08 < jtanx> if you copy the config file (server-configs/rsyslog.d/* ??) to /etc/
+16:09 < jtanx> it will log to /var/log/mctx[something I can't remember].log
+16:10 < jtanx> i'm pretty sure you can also create a log file specifically for warning level and above (so it logs in three places, stderr, the normal log, and a special 'error' log), but I haven't set this up yet
+18:17 < jtanx_> :0
+18:22 < sam_moore> ?
+18:50 -!- jtanx_ is now known as jtanx
+19:52 -!- MctxBot_ is now known as MctxBot
+20:24 < jtanx> I got the camera image to be updated in javascript instead: http://mctx.us.to:8080/test/
+20:25 < jtanx> right now it's updated around every ~400ms because my webcam is running through a usb1.1 link which seriously limits how fast it can update
+20:36 < jtanx> (its running on a pentium 3 computer)
+21:35 -!- jtanx [
[email protected]] has quit ["ChatZilla 0.9.90.1 [Firefox 24.0/20130910160258]"]
+--- Day changed Fri Sep 20 2013
+18:02 < jtanx> this seems like an interesting option, at least for the cam that just shows the can explode:http://sourceforge.net/apps/mediawiki/mjpg-streamer/index.php?title=Main_Page
+18:51 < sam_moore> Cool
+18:51 < sam_moore> It, ah
+18:52 < sam_moore> Looks like we might have to recompile the kernel to get PWM working
+18:52 < sam_moore> Hooray
+18:52 < sam_moore> Kernel compiling
+18:52 < sam_moore> What could possibly go wrong?
+19:02 < jtanx> really??
+19:02 < jtanx> why....
+19:03 < jtanx> no am33xx_pwm module?
+19:04 < sam_moore> Not that I can tell
+19:05 < jtanx> well crap
+19:06 < sam_moore> Well... surely I can find a precompiled kernel somewhere
+19:06 < jtanx> it seems that stuff for the BBB is at a really premature stage
+19:07 < sam_moore> Yeah, RPi is much nicer
+19:07 < jtanx> the BBB is just too new..
+19:07 < sam_moore> What's the point of having fancy things like ADC and PWM...
+19:07 < sam_moore> If you can't actually use them without jumping through hoops
+19:07 < jtanx> hahaha
+19:07 < jtanx> were you referencing this, when you said we might have to recompile the kernel? https://groups.google.com/d/msg/beagleboard/wjbOVE6ItNg/AGYVRhYbTX8J
+19:08 < sam_moore> Yeah
+19:08 < sam_moore> ...
+19:08 < sam_moore> I wonder if I can take the kernel used by Angstrom and stick it in /boot
+19:08 < sam_moore> on the SD card
+19:09 < jtanx> ._.
+19:10 < jtanx> what about kernels from here
+19:10 < jtanx> http://rcn-ee.net/deb/precise-armhf/
+19:10 < jtanx> sorry
+19:10 < jtanx> http://rcn-ee.net/deb/wheezy-armhf/
+19:11 < jtanx> what's it currently running
+19:12 < sam_moore> 3.8.13-bone20
+19:13 < sam_moore> Try 3.8.13-bone28 ?
+19:15 < jtanx> what about v3.8.9-bone15
+19:15 < jtanx> oh wait
+19:15 < jtanx> ~.~
+19:15 < sam_moore> I wonder why the BBB developers didn't go with debian...
+19:15 < jtanx> v3.11.0-bone5
+19:15 < jtanx> yeah
+19:15 < jtanx> why angstrom, of all things
+19:15 < sam_moore> Is angstrom used anywhere else?
+19:16 < jtanx> dunno
+19:16 < sam_moore> Other embedded devices, heh
+19:16 < sam_moore> Everyone just has to use their own distro :P
+19:17 < sam_moore> I didn't see the .11 kernels, I'll try the latest one
+19:17 < jtanx> want to try the rc version? lol
+19:18 < jtanx> oh the rc versions are older than .11
+19:19 < jtanx> how does it work?
+19:19 < jtanx> do you just download from http://rcn-ee.net/deb/wheezy-armhf/v3.11.0-bone5/
+19:19 < jtanx> the .sh script and run it from the BBB?
+19:24 < sam_moore> I think so
+19:25 < sam_moore> Well... here goes nothing
+19:26 < sam_moore> Hopefully between 3.8 and 3.11 they actually enabled PWM by default...
+19:27 < sam_moore> It looks like install-me.sh downloads all the .deb files appropriately
+19:29 < sam_moore> It's creating files like: /lib/firmware/bone_pwm_P8_36-00
+19:29 < sam_moore> Which looks promising
+19:29 < sam_moore> Snoopy is not yet on fire
+19:29 < sam_moore> Which is promising
+19:29 < sam_moore> Rebooting and praying
+19:31 < sam_moore> And... it booted into 3.8.13-bone20 -_-
+19:31 < sam_moore> Tempted to just symlink that filename to the new kernel
+19:34 < sam_moore> The new kernel exists in /boot, but obviously there's some boot config that needs to get set
+19:39 < sam_moore> Ok, screw this, I'm making a symlink just to see if it works
+19:40 < jtanx> lol
+19:41 < jtanx> no grub
+19:43 < jtanx> did update-initramfs run?
+19:46 < jtanx> or maybe we need mkimage
+19:47 < jtanx> yeah probably need mkimage
+19:50 < sam_moore> Um, zImage is a symlink to the kernel
+19:50 < sam_moore> But regardless, the new one won't boot
+19:50 < sam_moore> Such a pain
+19:50 < sam_moore> I wonder if we can just toggle the GPIO pin fast enough to implement PWM
+19:56 < sam_moore> Using a decent oscilloscope, I read a 6us period when switching GPIO1_28 on/off as fast as possible
+19:59 < sam_moore> That
+19:59 < sam_moore> s like 166KHz
+19:59 < sam_moore> Heh
+19:59 < sam_moore> Maybe the sources you found were closing the file and reopening it?
+20:01 < sam_moore> Yeah, wow
+20:02 < sam_moore> Using fopen, fprintf and fclose each time takes the period to 224us
+20:02 < sam_moore> Or 4KHz
+20:03 < sam_moore> Also for future reference, that CRO in G19 is definitely broken; it's always a lovely square wave on the modern oscilloscope I'm testing with now
+20:12 < jtanx> haha ok
+20:13 < jtanx> but without kernel support your pwm signal won't be very accurate
+20:13 < sam_moore> Yeah, I suppose
+20:13 < jtanx> how come the new kernel won't boot?
+20:14 < sam_moore> No idea
+20:14 < jtanx> Hmm
+20:14 < jtanx> this lack of documentation on how you do such procedures is pretty crap
+20:15 < sam_moore> Yeah, kernel developers aren't great on documentation
+20:15 < jtanx> is the mkimage package present?
+20:15 < jtanx> if not, install it and try running the install-me script again
+20:16 < jtanx> from this: http://www.jeremycole.com/blog/2011/03/09/beagleboard-upgrading-from-debian-5-to-debian-6/
+20:16 < jtanx> it should be just running that install script and rebooting
+20:16 < sam_moore> Alright, uboot-mkimage I presume?
+20:16 < jtanx> i guess
+20:17 < jtanx> and update-initramfs?
+20:18 < jtanx> (just looking at what the install-me.sh script uses)
+20:18 < sam_moore> Oh, does the script not fail if packages it needs don't exist...
+20:18 < sam_moore> -_-
+20:19 < jtanx> a quick scan says nup
+20:24 < sam_moore> Already had initramfs-tools but not mkimage, so I'll try again
+20:27 < sam_moore> I should probably have focused on the ADC reading before PWM
+20:27 < sam_moore> We're definitely going to get asked about aliasing again
+20:28 < jtanx> urgh yeah
+20:28 < sam_moore> I don't have a signal generator here though
+20:28 < jtanx> this BBB has definitely not been designed with 'plug n play' in mind
+20:28 < sam_moore> Mmm
+20:29 < sam_moore> The Angstrom image would work for this stuff, but obviously has limitations in terms of the webserver stuff
+20:29 < sam_moore> But even with Angstrom you still have to go through a similar convoluted process to control pins
+20:30 < sam_moore> From what I can tell there are two ways to do it
+20:30 < sam_moore> SYSFS, which I can't find much documentation on
+20:30 < sam_moore> Which is the /sys/class/gpio/ stuff
+20:30 < sam_moore> Which actually seems more intuitive than the other way
+20:31 < sam_moore> That involves echoing a bunch of cruft to /sys/devices/cape-manager/slots/ or something similar
+20:31 < jtanx> hmm
+20:32 < sam_moore> There is a /sys/class/pwm
+20:32 < sam_moore> But unfortunately whatever you export it complains about
+20:32 < sam_moore> Probably because the kernel wasn't compiled with it enabled
+20:32 < jtanx> is this with the old kernel?
+20:32 < sam_moore> Yeah
+20:33 < sam_moore> Hangon, the new one's probably finished building by now
+20:34 < jtanx> we don't know if the new one has been compiled with pwm support though
+20:34 < sam_moore> Yeah
+20:36 < sam_moore> The diff between the config options for the kernels shows that the old one has a comment "CONFIG_EHRPWM_TEST is not set" and the newer one has no reference to it
+20:37 < sam_moore> ...
+20:37 < sam_moore> Someone at some point
+20:37 < sam_moore> Has realised that PWM was not enabled
+20:37 < sam_moore> And commented that it isn't enabled
+20:37 < sam_moore> WHY THE HELL DIDN'T THEY JUST ENABLE IT
+20:37 < jtanx> :P
+20:37 < sam_moore> Anyway it still booted off the old kernel
+20:37 < sam_moore> Dinner time
+20:38 < jtanx> ok
+21:15 < sam_moore> http://www.eewiki.net/display/linuxonarm/BeagleBone+Black#BeagleBoneBlack-LinuxKernel
+21:15 < sam_moore> Looks promising
+21:16 < sam_moore> Hmm...
+21:17 < sam_moore> I'll try using 3.8 but building with the PWM
+21:18 < jtanx> building from source - fun fun :P
+21:19 < sam_moore> Well in theory I just change a config option
+21:19 < jtanx> yeah
+21:19 < sam_moore> When that doesn't work and I have to modify the source, that's when the fun begins
+21:19 < jtanx> let's just hope it works
+21:19 < sam_moore> Yeah, if it doesn't we're (beagle) boned
+21:19 < jtanx> oh while you're at it, figure out how to enable the ethernet-over-usb feature
+21:19 < sam_moore> Haha
+21:20 < jtanx> loljk
+21:25 < sam_moore> Hmmm... compiling a kernel is going to take a while
+21:26 < sam_moore> Ergh, why am I running it on this Pentium D
+21:30 < sam_moore> Hmmm, more troubling... why does a debian wheezy server have OpenSUSE sources in sources.list
+21:30 < sam_moore> Oh well, not my problem
+21:33 < jtanx> heh
+21:33 < sam_moore> How the hell are we going to explain this in the progress report...
+21:34 < sam_moore> Also, I didn't fully understand, why can't you use the same image for multiple BBB?
+21:34 < sam_moore> Are we going to have to do this all again for the other BBB?
+21:34 < sam_moore> Spike I mean
+21:37 < jtanx> no idea
+21:38 < jtanx> without some sort of debugging cable to see what happens when it boots, who knows
+21:38 < sam_moore> :S
+21:39 < sam_moore> I love how git gets to the head of the branch
+21:39 < sam_moore> By starting at the initial commit
+21:39 < sam_moore> And going through every commit and changing the file
+21:42 < sam_moore> It hasn't started building yet
+21:42 < sam_moore> And the way you customise the build...
+21:42 < sam_moore> Is to build it with the defaults, so that the options file exists, then change the options, then build it again -_-
+21:43 < jtanx> ಠ ಠ
+21:43 < sam_moore> Oh well, I have to go home, I'll try finish this tomorrow
+21:43 < sam_moore> Bye
+21:43 < jtanx> ok
+21:43 < jtanx> bye
+23:20 -!- jtanx [
[email protected]] has quit ["ChatZilla 0.9.90.1 [Firefox 24.0/20130910160258]"]
+--- Day changed Sat Sep 21 2013
+11:23 < sam_moore> http://hipstercircuits.com/enable-pwm-on-beaglebone-with-device-tree-overlays/
+11:23 < sam_moore> So... according to this I should add pwm_test as a kernel module
+11:24 < sam_moore> "This is it. It is now three o’clock in the morning and I have not slept or eaten in two days. My neck is sore, my ass has fallen asleep and my organs are screaming “slow down, man”. I no longer see [CC]s, [LD]s and [AS]s, I only see blondes, brunettes and redheads flashing before my eyes. With my last ounce of energy I barely manage to type in “reboot” as my face hits the keyboard. And that is when it happens.."
+11:25 < sam_moore> Ummm
+11:25 < sam_moore> It's awesome that this guy has solved the problem (I think)
+11:26 < sam_moore> But a bit depressing that it still isn't in the official kernel
+11:29 < sam_moore> I think most people just give up and use Angstrom, which is tempting
+11:30 < sam_moore> I still have that HTTP server code... :P
+11:45 < sam_moore> Looks like Robert C Nelson's 3.8 kernel does have a pwm_test module
+11:45 < sam_moore> Maybe the image you used just had an out of date kernel?
+11:45 -!- Irssi: #mctxuwa_softdev: Total of 3 nicks [0 ops, 0 halfops, 0 voices, 3 normal]
+11:51 < jtanx> hmm
+11:51 < jtanx> no idea
+11:51 < jtanx> it was made in july I think and it uses those rcn kernels
+11:51 < jtanx> We could always use lighttp
+11:51 < jtanx> lighttpd on angstrom if necessary
+11:52 < jtanx> lighttpd and install mod_fastcgi
+11:55 < jtanx> ok so the image uses 3.8.13-bone20, so maybe it wasn't enabled in that version, but it now is in 3.8.13-bone28?
+12:02 < sam_moore> I've just built 3.8.13-bone28 and the module pwm_test exists
+12:03 < sam_moore> ... I also copied the code from that guy's blog as pwm_test2 just in case :P
+12:03 < sam_moore> I'll have to test it later, but at least we have the kernel module
+12:08 < jtanx> nice
+12:39 < jtanx> ohhhhh I know why it didn't work from one bbb to the next, using the same image
+12:39 < jtanx> When you boot for the first time, it assigns the ethernet port eth0
+12:39 < jtanx> when you then take it out and boot it on a different BBB
+12:40 < jtanx> because the ethernet device has a different id, it gets assigned to say eth1
+12:40 < jtanx> and because you don't have any config for eth1 in your network config, there's no internet access
+12:40 < jtanx> http://www.eewiki.net/display/linuxonarm/BeagleBone#BeagleBone-Networking:UsingasharedSDcardwithMultipleBeagleBone
+12:41 < jtanx> should fix that
+15:10 < jtanx> Eh, you know what I'll just stop the threads when you want to pause it
+15:10 < jtanx> instead of conditionals
+15:11 < jtanx> it's not like you're pausing the experiment that often
+15:18 < sam_moore> That's fine
+15:19 < sam_moore> The conditional approach is only really necessary if you're constantly pausing the threads
+15:19 < sam_moore> I used it for an nbody simulator, so the computation of force and position was split up between threads
+15:19 < sam_moore> It's really slow if you have to stop and then recreate the threads on every step
+15:22 < sam_moore> Although still actually faster than the single threaded program
+15:22 < sam_moore> Well, depending on how many objects you simulated
+15:23 < sam_moore> Anyway, just stop the threads, it's simpler to code and the performance effect in our case is probably negligable
+15:30 < jtanx> yeah
+15:30 < jtanx> say you had an actuator that was being controlled at that instant when an 'emergency stop' was issued
+15:31 < jtanx> since an 'emergency stop' is the same as just pausing (eg stopping the thread but keep DataFile open)
+15:31 < jtanx> you'd have to wait for that action to be completed before the 'emergency stop' would be propagated
+15:34 < jtanx> welp I guess that's why there's hardware safety interlocks
+15:38 < jtanx> Also, technically wouldn't it be possible to try to set the actuator value before the current value has been set
+15:38 < jtanx> Since there's no queue
+15:39 < jtanx> a->control could be overwritten by a new request as Actuator_SetValue operates
+16:12 < sam_moore> We want that right?
+16:13 < sam_moore> I'll look at it later if I get time
+16:14 < jtanx> I don't know if we want that or not
+16:15 < jtanx> wait want as in the current behaviour or the behaviour with a queue?
+16:16 < sam_moore> The current behaviour
+16:16 < sam_moore> I don't think you need a queue
+16:16 < sam_moore> You can extend the critical section in Actuator_Loop to stop the current control getting overwritten
+16:17 < sam_moore> Move the pthread_mutex_unlock on line 121 to below line 127 (Actuator_SetValue)
+16:17 < sam_moore> That way if Actuator_SetControl is called before the value has been successfully set, it will just wait
+16:17 < sam_moore> Mutexes actually implement a queue
+16:18 < sam_moore> If one thread has a lock on the mutex, subsequent threads that try to access the mutex will queue up; whenever the mutex is unlocked the next thread (if any) which was waiting will get it
+16:18 < jtanx> ok
+16:23 < jtanx> I'll leave it as is for now
+16:23 < sam_moore> Sure
+16:49 < sam_moore> PWM working
+16:49 < jtanx> nice
+16:50 < jtanx> I still don't really understand - did you compile the kernel from scratch
+16:50 < jtanx> or did you figure out how to use the install-me.sh script
+16:50 < sam_moore> I did, but I didn't need to modify it
+16:50 < jtanx> huh
+16:50 < jtanx> ok
+16:51 < sam_moore> http://www.eewiki.net/display/linuxonarm/BeagleBone+Black#BeagleBoneBlack-LinuxKernel
+16:52 < jtanx> so if we do this: http://www.eewiki.net/display/linuxonarm/BeagleBone+Black#BeagleBoneBlack-Networking:UsingasharedSDcardwithMultipleBeagleBone
+16:52 < jtanx> We should be able to just copy our image
+16:52 < jtanx> and stick it on the electronics' BBB
+16:53 < sam_moore> Sounds good
+16:53 < sam_moore> I'm glad that worked
+16:53 < jtanx> yeah
+16:54 < jtanx> I wonder if it also enabled the usb0 stuff (ethernet over usb)
+16:58 < sam_moore> PWM appears to have picosecond resolution? Or at least the arguments are in ps
+17:02 < jtanx> oO
+17:11 < sam_moore> ADC can sample at ~4KHz
+17:11 < sam_moore> But... that's using bash
+17:11 < sam_moore> It will probably be massively fast when done in C
+17:11 < jtanx> um
+17:11 < jtanx> is there any need to have it sample at a consistent rate
+17:11 < jtanx> as in, with threads there's no guarantee
+17:12 < jtanx> of a consistent sampling rate
+17:12 < sam_moore> Yes, you're right
+17:13 < sam_moore> I don't think we can achieve a consistent sampling rate, but I don't think it's critical that we do
+17:14 < sam_moore> As soon as we make our software run in an operating system with a kernel and other processes that can run as well, it gets pretty unfeasable to have a constant sample rate
+17:14 < sam_moore> We can have it constant to within an uncertainty I guess
+17:15 < jtanx> yeah, true
+17:18 < sam_moore> If you wanted a really high constant sample rate (say much faster than 1us which is probably the best case we could get) you'd have to use a more low level embedded device
+17:18 < sam_moore> Well I guess you could compile your own kernel for the BBB
+17:19 < sam_moore> But either way you'd have to physically run the webserver/GUI interface stuff on a seperate device
+17:19 < sam_moore> At this stage my feeling is what we have is good enough given the complexity of all the requirements we were given
+17:23 < jtanx> yeah
+17:25 < sam_moore> Hmm, I can set some GPIO pins to toggle whenever Sensor_Read is called and get an idea of sample rates and to what degree of accuracy we can quote the time stamps
+17:26 < sam_moore> I think I'll write some pin control code
+17:26 < sam_moore> I don't trust any of these custom libraries
+17:29 < jtanx> custom libraries?
+17:36 < sam_moore> Well they aren't really libraries
+17:36 < sam_moore> http://www.avrfreaks.net/wiki/index.php/Documentation:Linux/GPIO#Example_of_GPIO_access_from_within_a_C_program
+17:37 < sam_moore> Eg: That one has an fopen and fclose each time the value is changed
+17:38 < sam_moore> I could google until I find someone that has already written a C library, but chances are it will be slow or broken
+17:38 < sam_moore> Since I've worked out how to control the pins I may as well just write the C code to do it
+17:39 < jtanx> yep
+17:49 < sam_moore> I wonder if I can do this with a macro...
+18:30 < sam_moore> Ergh, screw that
+18:31 < sam_moore> Ok, I'm going to implement things like: GPIO/ADC/PWM_Export/Unexport to initialise or deinitialise all pins
+18:31 < jtanx> Ok
+18:31 < jtanx> too much effort with macros?
+18:31 < sam_moore> Yeah,
+18:32 < sam_moore> I was thinking of having something like "GPIOX_Set()" instead of GPIO_Set(int x)"
+18:32 < sam_moore> But that's probably not as nice as I thought it was
+18:32 < sam_moore> Anyway, there's an enum in the header file that contains the id of all pins used
+18:32 < sam_moore> The implementation defines some structs that wrap around the file descriptors
+18:33 < sam_moore> But to use the module you just give it an ID as defined in the enums
+18:33 < jtanx> Makes sense
+18:33 < jtanx> designing the gui is actually not too bad
+18:33 < sam_moore> That's good
+18:34 < jtanx> looks ok in ie8 too
+18:34 < sam_moore> Nice
+18:35 < jtanx> gotta go, dinner
+18:35 < sam_moore> Ok
+18:35 < sam_moore> Hmm, it would be nice if C had value checking on enums
+18:35 < sam_moore> You can define a function that takes an enum type as an argument
+18:36 < sam_moore> But people can still just pass any old integer
+18:36 < sam_moore> As far as I know
+18:36 < sam_moore> eg: typedef enum {FIRST=1, SECOND=10, THIRD=100} EnumType
+18:36 < sam_moore> void Foo(EnumType e);
+18:37 < sam_moore> If you go Foo(2) it won't complain
+18:38 < sam_moore> Annoying
+18:38 < sam_moore> That seems like something the compiler would be able to pick up
+19:31 < sam_moore> Ergh, I'm getting too obsessive compulsive with this pin thing
+19:35 < sam_moore> It's annoying because ADC, GPIO and PWM are treated completely differently
+19:35 < sam_moore> You write one thing and it enables *all* the ADCs
+19:35 < sam_moore> You have to enable each GPIO pin individually
+19:36 < sam_moore> And to enable PWM pins you give a string (not just an integer)
+19:37 < sam_moore> Also the location of the pin files is not guaranteed (though it probably doesn't change for a given system configuration)
+19:39 < sam_moore> Ah, I found a way to enable pwm with /sys/class/ instead of that cape manager thing
+19:39 < sam_moore> I think I'll use that, since at least it's consistent with the GPIO stuff
+19:41 < sam_moore> Ooh!
+19:41 < sam_moore> http://beagleboard-gsoc13.blogspot.com.au/2013/07/sampling-analogue-signals-using-adc-on.html
+19:41 < sam_moore> Provides a driver for continuously sampling with the ADC
+19:41 < sam_moore> Oh wait
+19:41 < sam_moore> Crap in a bucket
+19:42 < sam_moore> Because we're using those multiplexers we can't do that
+19:42 < sam_moore> We have to set the multiplexer before each sample
+19:42 < sam_moore> Oh well, never mind
+19:44 < sam_moore> I suppose we could write our own kernel module :S
+19:45 < sam_moore> I think I understand this enough to talk to Adam next time he tries to talk about sample rate
+19:46 < sam_moore> 1. It's not actually constant, but we can probably have it constant to within a few us
+19:46 < sam_moore> 2. To make it constant would require writing a kernel module
+19:47 < sam_moore> Unless electronics stops being stingy and gets one amplifier per channel :P
+20:22 < jtanx> hehehe
+20:22 < jtanx> next week's adrian though
+20:22 < sam_moore> Ah
+20:23 < jtanx> grilling time
+20:23 < sam_moore> He'll probably ask us the same things :P
+20:23 < jtanx> yeah
+20:23 < jtanx> but man, so much stuff to go through just to get some readings from a few pins
+20:24 < jtanx> so good job with that :P
+20:54 < sam_moore> Thanks
+22:45 -!- jtanx [
[email protected]] has quit ["ChatZilla 0.9.90.1 [Firefox 24.0/20130910160258]"]
+--- Day changed Sun Sep 22 2013
+00:51 < sam_moore> Hell yes
+00:51 < sam_moore> PWM controlled through web browser
+00:51 < sam_moore> GPIO controlled through web browser
CXX = gcc
FLAGS = -std=c99 -Wall -pedantic -g -I/usr/include/opencv -I/usr/include/opencv2/highgui -L/usr/lib
LIB = -lfcgi -lssl -lcrypto -lpthread -lm -lopencv_highgui -lopencv_core -lopencv_ml -lopencv_imgproc
-OBJ = log.o control.o data.o fastcgi.o main.o sensor.o actuator.o image.o
+OBJ = log.o control.o data.o fastcgi.o main.o sensor.o actuator.o image.o bbb_pin.o
RM = rm -f
BIN = server
all : $(BIN) $(BIN2)
-stream : stream.c
- $(CXX) $(FLAGS) -o stream stream.c $(LIB)
-
-intertest : interferometer.c
- $(CXX) $(FLAGS) -o intertest interferometer.c $(LIB)
-
$(BIN) : $(OBJ)
$(CXX) $(FLAGS) -o $(BIN) $(OBJ) $(LIB)
#include "actuator.h"
#include "options.h"
+// Files containing GPIO and PWM definitions
+#include "bbb_pin.h"
+
/** Array of Actuators (global to this file) initialised by Actuator_Init **/
static Actuator g_actuators[NUMACTUATORS];
/** Human readable names for the Actuators **/
const char * g_actuator_names[NUMACTUATORS] = {
- "actuator_test0", "actuator_test1"
+ "actuator_test0", "gpio1_16", "EHRPWM0A_duty@60Hz"
};
/**
Data_Init(&(g_actuators[i].data_file));
pthread_mutex_init(&(g_actuators[i].mutex), NULL);
}
+
+ // Initialise pins used
+ GPIO_Export(GPIO1_16);
+ PWM_Export(EHRPWM0A);
+
}
/**
{
case ACTUATOR_TEST0:
{
+ // Onboard LEDs test actuator
FILE *led_handle = NULL; //code reference: http://learnbuildshare.wordpress.com/2013/05/19/beaglebone-black-controlling-user-leds-using-c/
const char *led_format = "/sys/class/leds/beaglebone:green:usr%d/brightness";
char buf[50];
}
break;
case ACTUATOR_TEST1:
+ GPIO_Set(GPIO1_16, (bool)(value));
break;
+ case ACTUATOR_TEST2:
+ {
+ // PWM analogue actuator (currently generates one PWM signal with first PWM module)
+ static long freq = 16666666; // This is 60Hz
+ PWM_Set(EHRPWM0A, true, freq, value * freq); // Set the duty cycle
+ break;
+ }
}
Log(LOGDEBUG, "Actuator %s set to %f", g_actuator_names[a->id], value);
case JSON:
FCGI_BeginJSON(context, STATUS_OK);
FCGI_JSONLong("id", id);
+ FCGI_JSONPair("name", g_actuator_names[id]);
break;
default:
FCGI_PrintRaw("Content-type: text/plain\r\n\r\n");
// Might be OK in C++ but not easy in C
/** Number of actuators **/
-#define NUMACTUATORS 2
+#define NUMACTUATORS 3
/** List of actuator ids (should be of size NUMACTUATORS) **/
typedef enum
{
ACTUATOR_TEST0,
- ACTUATOR_TEST1
+ ACTUATOR_TEST1,
+ ACTUATOR_TEST2
} ActuatorId;
/** Human readable names for the Actuators **/
--- /dev/null
+/**
+ * @file bbb_pin.c
+ * @purpose Implementation of BBB pin control functions and structures
+ * THIS CODE IS NOT THREADSAFE
+ */
+
+#include "bbb_pin.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+/**
+ * Structure to represent a GPIO pin
+ * Note: Only accessable to this file; to use the functions pass a GPIOId
+ */
+typedef struct
+{
+ int fd_value;
+ int fd_direction;
+} GPIO_Pin;
+
+/**
+ * Structure to represent an ADC pin
+ * Note: Only accessable to this file; to use the functions pass a ADCId
+ */
+typedef struct
+{
+ FILE * file_value;
+} ADC_Pin;
+
+/**
+ * Structure to represent a PWM pin
+ * Note: Only accessable to this file; to use the functions pass a PWMId
+ */
+typedef struct
+{
+ int fd_run;
+ FILE * file_duty;
+ FILE * file_period;
+ int fd_polarity;
+} PWM_Pin;
+
+/** Array of GPIO pins **/
+static GPIO_Pin g_gpio[GPIO_NUM_PINS] = {{0}};
+/** Array of ADC pins **/
+static ADC_Pin g_adc[ADC_NUM_PINS] = {{0}};
+/** Array of PWM pins **/
+static PWM_Pin g_pwm[PWM_NUM_PINS] = {{0}};
+
+static char g_buffer[BUFSIZ] = "";
+
+
+
+
+/**
+ * Export a GPIO pin and open the file descriptors
+ */
+void GPIO_Export(int pin)
+{
+ if (pin < 0 || pin > GPIO_NUM_PINS)
+ Fatal("Invalid pin number %d", pin);
+
+
+
+ // Export the pin
+ sprintf(g_buffer, "%s/export", GPIO_DEVICE_PATH);
+ FILE * export = fopen(g_buffer, "w");
+ if (export == NULL)
+ Fatal("Couldn't open %s to export GPIO pin %d - %s", g_buffer, pin, strerror(errno));
+
+ fprintf(export, "%d", pin);
+ fclose(export);
+
+ // Setup direction file descriptor
+ sprintf(g_buffer, "%s/gpio%d/direction", GPIO_DEVICE_PATH, pin);
+ g_gpio[pin].fd_direction = open(g_buffer, O_RDWR);
+ if (g_gpio[pin].fd_direction < 0)
+ Fatal("Couldn't open %s for GPIO pin %d - %s", g_buffer, pin, strerror(errno));
+
+
+ // Setup value file descriptor
+ sprintf(g_buffer, "%s/gpio%d/value", GPIO_DEVICE_PATH, pin);
+ g_gpio[pin].fd_value = open(g_buffer, O_RDWR);
+ if (g_gpio[pin].fd_value < 0)
+ Fatal("Couldn't open %s for GPIO pin %d - %s", g_buffer, pin, strerror(errno));
+}
+
+/**
+ * Unexport a GPIO pin and close its' file descriptors
+ */
+void GPIO_Unexport(int pin)
+{
+
+ if (pin < 0 || pin > GPIO_NUM_PINS)
+ Fatal("Invalid pin number %d", pin);
+
+ // Close file descriptors
+ close(g_gpio[pin].fd_value);
+ close(g_gpio[pin].fd_direction);
+
+ // Unexport the pin
+
+ if (g_buffer[0] == '\0')
+ sprintf(g_buffer, "%s/unexport", GPIO_DEVICE_PATH);
+ FILE * export = fopen(g_buffer, "w");
+ if (export == NULL)
+ Fatal("Couldn't open %s to export GPIO pin %d - %s", g_buffer, pin, strerror(errno));
+
+ fprintf(export, "%d", pin);
+ fclose(export);
+}
+
+
+
+
+/**
+ * Export all PWM pins and open file descriptors
+ * @param pin - The pin number
+ */
+void PWM_Export(int pin)
+{
+ if (pin < 0 || pin > PWM_NUM_PINS)
+ Fatal("Invalid pin number %d", pin);
+
+ // Export the pin
+ sprintf(g_buffer, "%s/export", PWM_DEVICE_PATH);
+ FILE * export = fopen(g_buffer, "w");
+ if (export == NULL)
+ Fatal("Couldn't open %s to export PWM pin %d - %s", g_buffer, pin, strerror(errno));
+
+ fprintf(export, "%d", pin);
+ fclose(export);
+
+ // Open file descriptors
+ sprintf(g_buffer, "%s/pwm%d/run", PWM_DEVICE_PATH, pin);
+ g_pwm[pin].fd_run = open(g_buffer, O_WRONLY);
+ if (g_pwm[pin].fd_run < 0)
+ Fatal("Couldn't open %s for PWM pin %d - %s", g_buffer, pin, strerror(errno));
+
+ sprintf(g_buffer, "%s/pwm%d/polarity",PWM_DEVICE_PATH, pin);
+ g_pwm[pin].fd_polarity = open(g_buffer, O_WRONLY);
+ if (g_pwm[pin].fd_polarity < 0)
+ Fatal("Couldn't open %s for PWM pin %d - %s", g_buffer, pin, strerror(errno));
+
+ sprintf(g_buffer, "%s/pwm%d/period_ns",PWM_DEVICE_PATH, pin);
+ g_pwm[pin].file_period = fopen(g_buffer, "w");
+ if (g_pwm[pin].file_period == NULL)
+ Fatal("Couldn't open %s for PWM pin %d - %s", g_buffer, pin, strerror(errno));
+
+ sprintf(g_buffer, "%s/pwm%d/duty_ns",PWM_DEVICE_PATH, pin);
+ g_pwm[pin].file_duty = fopen(g_buffer, "w");
+ if (g_pwm[pin].file_duty == NULL)
+ Fatal("Couldn't open %s for PWM pin %d - %s", g_buffer, pin, strerror(errno));
+
+ // Don't buffer the streams
+ setbuf(g_pwm[pin].file_period, NULL);
+ setbuf(g_pwm[pin].file_duty, NULL);
+
+
+}
+
+/**
+ * Unexport a PWM pin and close its file descriptors
+ * @param pin - The pin number
+ */
+void PWM_Unexport(int pin)
+{
+ if (pin < 0 || pin > PWM_NUM_PINS)
+ Fatal("Invalid pin number %d", pin);
+
+ // Close the file descriptors
+ close(g_pwm[pin].fd_polarity);
+ close(g_pwm[pin].fd_run);
+ fclose(g_pwm[pin].file_period);
+ fclose(g_pwm[pin].file_duty);
+
+ //Unexport the pin
+ sprintf(g_buffer, "%s/unexport", PWM_DEVICE_PATH);
+ FILE * export = fopen(g_buffer, "w");
+ if (export == NULL)
+ Fatal("Couldn't open %s to unexport PWM pin %d - %s", g_buffer, pin, strerror(errno));
+
+ fprintf(export, "%d", pin);
+ fclose(export);
+
+
+}
+
+/**
+ * Export ADC pins; http://beaglebone.cameon.net/home/reading-the-analog-inputs-adc
+ * Can't use sysfs like GPIO or PWM pins
+ * Bloody annoying how inconsistent stuff is on the Beaglebone
+ */
+void ADC_Export()
+{
+
+ FILE * export = fopen(ADC_EXPORT_PATH, "w");
+ if (export == NULL)
+ Fatal("Couldn't open %s to export ADCs - %s", ADC_EXPORT_PATH, strerror(errno));
+
+ fprintf(export, "cape-bone-iio");
+ fclose(export);
+
+ for (int i = 0; i < ADC_NUM_PINS; ++i)
+ {
+ sprintf(g_buffer, "%s/AIN%d", ADC_DEVICE_PATH, i);
+ g_adc[i].file_value = fopen(g_buffer, "r");
+ if (g_adc[i].file_value == NULL)
+ Fatal("Couldn't open ADC %d device file %s - %s", i, g_buffer, strerror(errno));
+
+ setbuf(g_adc[i].file_value, NULL);
+
+ }
+}
+
+/**
+ * Unexport ADC pins
+ */
+void ADC_Unexport()
+{
+ for (int i = 0; i < ADC_NUM_PINS; ++i)
+ fclose(g_adc[i].file_value);
+}
+
+/**
+ * Set a GPIO pin
+ * @param pin - The pin to set. MUST have been exported before calling this function.
+ */
+void GPIO_Set(int pin, bool value)
+{
+ if (pwrite(g_gpio[pin].fd_direction, "out", 3*sizeof(char), 0) != 3)
+ Fatal("Couldn't set GPIO %d direction - %s", pin, strerror(errno));
+
+ char c = '0' + (value);
+ if (pwrite(g_gpio[pin].fd_value, &c, 1*sizeof(char), 0) != 1)
+ Fatal("Couldn't read GPIO %d value - %s", pin, strerror(errno));
+
+}
+
+/**
+ * Read from a GPIO Pin
+ * @param pin - The pin to read
+ */
+bool GPIO_Read(int pin)
+{
+ if (pwrite(g_gpio[pin].fd_direction, "in", 2*sizeof(char), 0) != 2)
+ Fatal("Couldn't set GPIO %d direction - %s", pin, strerror(errno));
+ char c = '0';
+ if (pread(g_gpio[pin].fd_value, &c, 1*sizeof(char), 0) != 1)
+ Fatal("Couldn't read GPIO %d value - %s", pin, strerror(errno));
+
+ return (c == '1');
+
+}
+
+/**
+ * Activate a PWM pin
+ * @param pin - The pin to activate
+ * @param polarity - if true, pin is active high, else active low
+ * @param period - The period in ns
+ * @param duty - The time the pin is active in ns
+ */
+void PWM_Set(int pin, bool polarity, long period, long duty)
+{
+ // Have to stop PWM before changing it
+ if (pwrite(g_pwm[pin].fd_run, "0", 1*sizeof(char), 0) != 1)
+ Fatal("Couldn't stop PWM %d - %s", pin, strerror(errno));
+
+ char c = '0' + polarity;
+ if (pwrite(g_pwm[pin].fd_polarity, &c, 1*sizeof(char), 0) != 1)
+ Fatal("Couldn't set PWM %d polarity - %s", pin, strerror(errno));
+
+
+ rewind(g_pwm[pin].file_period);
+ rewind(g_pwm[pin].file_duty);
+
+ if (fprintf(g_pwm[pin].file_duty, "%lu", duty) == 0)
+ Fatal("Couldn't set duty cycle for PWM %d - %s", pin, strerror(errno));
+
+ if (fprintf(g_pwm[pin].file_period, "%lu", period) == 0)
+ Fatal("Couldn't set period for PWM %d - %s", pin, strerror(errno));
+
+ if (pwrite(g_pwm[pin].fd_run, "1", 1*sizeof(char), 0) != 1)
+ Fatal("Couldn't start PWM %d - %s", pin, strerror(errno));
+
+}
+
+/**
+ * Deactivate a PWM pin
+ * @param pin - The pin to turn off
+ */
+void PWM_Stop(int pin)
+{
+ if (pwrite(g_pwm[pin].fd_run, "0", 1*sizeof(char), 0) != 1)
+ Fatal("Couldn't stop PWM %d - %s", pin, strerror(errno));
+
+}
+
+/**
+ * Read an ADC value
+ * @param id - The ID of the ADC pin to read
+ * @returns - The reading of the ADC channel
+ */
+long ADC_Read(int id)
+{
+
+ //Log(LOGDEBUG, "Called for pin %d", id);
+ char adc_val[ADC_DIGITS] = "";
+ rewind(g_adc[id].file_value);
+ fgets(adc_val, sizeof(adc_val)/sizeof(char), g_adc[id].file_value);
+ for(int i = ADC_DIGITS-1; i > 0; --i)
+ {
+ if (adc_val[i] == '\n')
+ adc_val[i] = '\0';
+ }
+
+ char * end;
+ long val = strtol(adc_val, &end, 10);
+ if (*end != '\0')
+ {
+ Log(LOGERR, "Reading ADC%d gives %s - invalid!", id, adc_val);
+ }
+ //Log(LOGDEBUG, "Returns %lu", val);
+ return val;
+}
--- /dev/null
+/**
+ * @file bbb_pin.h
+ * @brief Definition of functions for controlling pins on the Beaglebone Black
+ */
+
+#ifndef _BBB_PIN_H
+#define _BBB_PIN_H
+
+#include "common.h"
+
+#include "bbb_pin_defines.h"
+
+// Initialise / Deinitialise functions
+extern void GPIO_Export(int pin);
+extern void GPIO_Unexport(int pin);
+
+extern void PWM_Export(int pin);
+extern void PWM_Unexport(int pin);
+
+extern void ADC_Export();
+extern void ADC_Unexport();
+
+// Pin reading/setting functions
+extern bool GPIO_Read(int pin);
+extern void GPIO_Set(int pin, bool value);
+
+extern long ADC_Read(int pin);
+
+extern void PWM_Set(int pin, bool polarity, long period, long duty); // period and duty are in ns
+extern void PWM_Stop(int pin);
+
+
+
+#endif //_BBB_PIN_H
+
+//EOF
--- /dev/null
+/**
+ * @file bbb_pin_defines.h
+ * @brief Defines pins on Beaglebone Black
+ */
+
+#ifndef _BBB_PIN_DEFINES_H
+#define _BBB_PIN_DEFINES_H
+
+/** GPIO0 defines **/
+
+#define GPIO0_1 1
+#define GPIO0_2 2
+//#define GPIO0_3 3 // Used for PWM
+//#define GPIO0_4 4 // Used for PWM
+#define GPIO0_5 5
+#define GPIO0_6 6
+#define GPIO0_7 7
+#define GPIO0_8 8
+#define GPIO0_9 9
+#define GPIO0_10 10
+#define GPIO0_11 11
+#define GPIO0_12 12
+#define GPIO0_13 13
+#define GPIO0_14 14
+#define GPIO0_15 15
+#define GPIO0_16 16
+#define GPIO0_17 17
+#define GPIO0_18 18
+#define GPIO0_19 19
+#define GPIO0_20 20
+#define GPIO0_21 21
+#define GPIO0_22 22
+#define GPIO0_23 23
+#define GPIO0_24 24
+#define GPIO0_25 25
+#define GPIO0_26 26
+#define GPIO0_27 27
+#define GPIO0_28 28
+#define GPIO0_29 29
+#define GPIO0_30 30
+#define GPIO0_31 31
+#define GPIO0_32 32
+
+/** GPIO1 defines **/
+
+#define GPIO1_1 33
+#define GPIO1_2 34
+#define GPIO1_3 35
+#define GPIO1_4 36
+#define GPIO1_5 37
+#define GPIO1_6 38
+#define GPIO1_7 39
+#define GPIO1_8 40
+#define GPIO1_9 41
+#define GPIO1_10 42
+#define GPIO1_11 43
+#define GPIO1_12 44
+#define GPIO1_13 45
+#define GPIO1_14 46
+#define GPIO1_15 47
+#define GPIO1_16 48
+#define GPIO1_17 49
+#define GPIO1_18 50
+#define GPIO1_19 51
+#define GPIO1_20 52
+#define GPIO1_21 53
+#define GPIO1_22 54
+#define GPIO1_23 55
+#define GPIO1_24 56
+#define GPIO1_25 57
+#define GPIO1_26 58
+#define GPIO1_27 59
+#define GPIO1_28 60
+#define GPIO1_29 61
+#define GPIO1_30 62
+#define GPIO1_31 63
+#define GPIO1_32 64
+
+/** GPIO2 defines **/
+
+#define GPIO2_1 65
+#define GPIO2_2 66
+#define GPIO2_3 67
+#define GPIO2_4 68
+#define GPIO2_5 69
+#define GPIO2_6 70
+#define GPIO2_7 71
+#define GPIO2_8 72
+#define GPIO2_9 73
+#define GPIO2_10 74
+#define GPIO2_11 75
+#define GPIO2_12 76
+#define GPIO2_13 77
+#define GPIO2_14 78
+#define GPIO2_15 79
+#define GPIO2_16 80
+#define GPIO2_17 81
+#define GPIO2_18 82
+#define GPIO2_19 83
+#define GPIO2_20 84
+#define GPIO2_21 85
+#define GPIO2_22 86
+#define GPIO2_23 87
+#define GPIO2_24 88
+#define GPIO2_25 89
+#define GPIO2_26 90
+#define GPIO2_27 91
+#define GPIO2_28 92
+#define GPIO2_29 93
+#define GPIO2_30 94
+#define GPIO2_31 95
+#define GPIO2_32 96
+
+/** Number of GPIO pins **/
+#define GPIO_NUM_PINS 97
+
+/** Export path **/
+#define GPIO_DEVICE_PATH "/sys/class/gpio"
+
+#define ADC_BITS 12
+#define ADC_DIGITS 5
+#define ADC0 0
+#define ADC1 1
+#define ADC2 2
+#define ADC3 3
+#define ADC4 4
+#define ADC5 5
+#define ADC6 6
+#define ADC7 7
+
+/** Number of ADC pins **/
+#define ADC_NUM_PINS 8
+
+/** Path to export ADCs with**/
+#define ADC_EXPORT_PATH "/sys/devices/bone_capemgr.9/slots"
+/** Path at which ADCs appear **/
+#define ADC_DEVICE_PATH "/sys/devices/ocp.3/helper.16"
+
+/** PWM defines **/
+#define EHRPWM0A 0
+#define EHRPWM0B 1
+// No other PWM pins work!
+
+/** Number of PWM pins **/
+#define PWM_NUM_PINS 2
+
+/** Path to PWM sysfs **/
+#define PWM_DEVICE_PATH "/sys/class/pwm"
+
+
+
+#endif //_BBB_PIN_DEFINES_H
+
+
{
FCGIContext context = {0};
- Log(LOGDEBUG, "First request...");
+ Log(LOGDEBUG, "Start loop");
while (FCGI_Accept() >= 0) {
- Log(LOGDEBUG, "Got request #%d", context.response_number);
+
ModuleHandler module_handler = NULL;
char module[BUFSIZ], params[BUFSIZ];
//strncpy doesn't zero-truncate properly
snprintf(module, BUFSIZ, "%s", getenv("DOCUMENT_URI_LOCAL"));
snprintf(params, BUFSIZ, "%s", getenv("QUERY_STRING"));
+
+ Log(LOGDEBUG, "Got request #%d - Module %s, params %s", context.response_number, module, params);
//Remove trailing slashes (if present) from module query
size_t lastchar = strlen(module) - 1;
}
context.response_number++;
- Log(LOGDEBUG, "Waiting for request #%d", context.response_number);
+
}
Log(LOGDEBUG, "Thread exiting.");
If level is higher (less urgent) than the program's verbosity (see options.h) no message will be printed
* @param funct - String indicating the function name from which this function was called.
If this is NULL, Log will show the unspecified_funct string instead
+ * @param file - Source file containing the function
+ * @param line - Line in the source file at which Log is called
* @param fmt - A format string
* @param ... - Arguments to be printed according to the format string
*/
-void LogEx(int level, const char * funct, ...)
+void LogEx(int level, const char * funct, const char * file, int line, ...)
{
//Todo: consider setlogmask(3) to filter messages
const char *fmt;
if (level > g_options.verbosity)
return;
- va_start(va, funct);
+ va_start(va, line);
fmt = va_arg(va, const char*);
if (fmt == NULL) // sanity check
break;
}
- syslog(level, "%s: %s - %s", severity, funct, buffer);
+ syslog(level, "%s: %s (%s:%d) - %s", severity, funct, file, line, buffer);
}
/**
* Handle a Fatal error in the program by printing a message and exiting the program
* CALLING THIS FUNCTION WILL CAUSE THE PROGAM TO EXIT
* @param funct - Name of the calling function
+ * @param file - Name of the source file containing the calling function
+ * @param line - Line in the source file at which Fatal is called
* @param fmt - A format string
* @param ... - Arguments to be printed according to the format string
*/
-void FatalEx(const char * funct, ...)
+void FatalEx(const char * funct, const char * file, int line, ...)
{
const char *fmt;
char buffer[BUFSIZ];
va_list va;
- va_start(va, funct);
+ va_start(va, line);
fmt = va_arg(va, const char*);
if (fmt == NULL)
if (funct == NULL)
funct = unspecified_funct;
- syslog(LOG_CRIT, "FATAL: %s - %s", funct, buffer);
+ syslog(LOG_CRIT, "FATAL: %s (%s:%d) - %s", funct, file, line, buffer);
exit(EXIT_FAILURE);
}
#define _LOG_H
//To get around a 'pedantic' C99 rule that you must have at least 1 variadic arg, combine fmt into that.
-#define Log(level, ...) LogEx(level, __func__, __VA_ARGS__)
-#define Fatal(...) FatalEx(__func__, __VA_ARGS__)
+#define Log(level, ...) LogEx(level, __func__, __FILE__, __LINE__, __VA_ARGS__)
+#define Fatal(...) FatalEx(__func__, __FILE__, __LINE__, __VA_ARGS__)
// An enum to make the severity of log messages human readable in code
enum {LOGERR=0, LOGWARN=1, LOGNOTE=2, LOGINFO=3,LOGDEBUG=4};
-extern void LogEx(int level, const char * funct, ...); // General function for printing log messages to stderr
-extern void FatalEx(const char * funct, ...); // Function that deals with a fatal error (prints a message, then exits the program).
+extern void LogEx(int level, const char * funct, const char * file, int line, ...); // General function for printing log messages to stderr
+extern void FatalEx(const char * funct, const char * file, int line, ...); // Function that deals with a fatal error (prints a message, then exits the program).
#endif //_LOG_H
#include "common.h"
#include "sensor.h"
#include "options.h"
+#include "bbb_pin.h"
#include <math.h>
/** Array of sensors, initialised by Sensor_Init **/
//Max Safety, Min safety, Max warning, Min warning
{1,-1,1,-1}, // ANALOG_TEST0
{500,0,499,0}, // ANALOG_TEST1
+ {5000,0,5000,0}, // ANALOG_REALTEST
{5,-5,4,-4}, // ANALOG_FAIL0
{1,0,1,0}, // DIGITAL_TEST0
{1,0,1,0}, // DIGITAL_TEST1
+ {1,0,1,0}, // DIGITAL_REALTEST
{1,0,1,0} // DIGITAL_FAIL0
};
/** Human readable names for the sensors **/
const char * g_sensor_names[NUMSENSORS] = {
"analog_test0", "analog_test1",
- "analog_fail0", "digital_test0",
- "digital_test1", "digital_fail0"
+ "analog_realtest", "analog_fail0",
+ "digital_test0", "digital_test1",
+ "digital_realtest", "digital_fail0"
};
/**
g_sensors[i].id = i;
Data_Init(&(g_sensors[i].data_file));
}
+
+ // Get the ADCs
+ ADC_Export();
+
+ // GPIO1_28 used as a pulse for sampling
+ GPIO_Export(GPIO1_28);
+ // GPIO0_30 toggled during sampling
+ GPIO_Export(GPIO0_30);
}
/**
gettimeofday(&t, NULL);
d->time_stamp = TIMEVAL_DIFF(t, *Control_GetStartTime());
+ static bool result = true;
+
+
// Read value based on Sensor Id
switch (s->id)
{
+ case ANALOG_REALTEST:
+ {
+ static bool set = false;
+
+ GPIO_Set(GPIO0_30, true);
+ d->value = (double)ADC_Read(ADC0); //ADC #0 on the Beaglebone
+ //Log(LOGDEBUG, "Got value %f from ADC0", d->value);
+ GPIO_Set(GPIO0_30, false);
+ set = !set;
+ GPIO_Set(GPIO1_28, set);
+
+ break;
+ }
+
+ default:
+ d->value = rand() % 2;
+ usleep(1000000);
+ break;
+
+
case ANALOG_TEST0:
+ {
d->value = (double)(rand() % 100) / 100;
break;
+ }
case ANALOG_TEST1:
{
static int count = 0;
d->value = count++;
break;
}
+
case ANALOG_FAIL0:
- d->value = (double)(rand() % 6) * -( rand() % 2) / ( rand() % 100 + 1);
+ d->value = 0;
+ //d->value = (double)(rand() % 6) * -( rand() % 2) / ( rand() % 100 + 1);
//Gives a value between -5 and 5
break;
case DIGITAL_TEST0:
d->value = t.tv_sec % 2;
+
break;
case DIGITAL_TEST1:
d->value = (t.tv_sec+1)%2;
break;
+ case DIGITAL_REALTEST:
+ {
+ // Can pass pin as argument, just using 20 as an example here
+ // Although since pins will be fixed, can just define it here if we need to
+ //d->value = pinRead(20); //Pin 20 on the Beaglebone
+ break;
+ }
case DIGITAL_FAIL0:
if( rand() % 100 > 98)
d->value = 2;
d->value = rand() % 2;
//Gives 0 or 1 or a 2 every 1/100 times
break;
- default:
- Fatal("Unknown sensor id: %d", s->id);
- break;
+ //default:
+ // Fatal("Unknown sensor id: %d", s->id);
+ // break;
}
- usleep(100000); // simulate delay in sensor polling
+
// Perform sanity check based on Sensor's ID and the DataPoint
Sensor_CheckData(s->id, d->value);
// Update latest DataPoint if necessary
- bool result = (d->value != s->newest_data.value);
+
if (result)
{
s->newest_data.time_stamp = d->time_stamp;
case JSON:
FCGI_BeginJSON(context, STATUS_OK);
FCGI_JSONLong("id", id);
+ FCGI_JSONPair("name", g_sensor_names[id]);
break;
default:
FCGI_PrintRaw("Content-type: text/plain\r\n\r\n");
#include "data.h"
/** Number of sensors **/
-#define NUMSENSORS 6
+#define NUMSENSORS 8
/** Sensor ids - there should be correspondence with the names in g_sensor_names **/
typedef enum SensorId
{
ANALOG_TEST0,
ANALOG_TEST1,
+ ANALOG_REALTEST,
ANALOG_FAIL0,
DIGITAL_TEST0,
DIGITAL_TEST1,
+ DIGITAL_REALTEST,
DIGITAL_FAIL0
} SensorId;
{
g_sensors = []
- g_numSensors = 2
+ g_numSensors = 1
g_storeTime = []
g_key = null
$.fn.updateSensor = function(json)
{
//console.log(json.data)
- var sensor = g_sensors[json.id]
+ var sensor = g_sensors[0]
var most_recent = null
if (sensor.length > 0)
most_recent = sensor[sensor.length-1][0]
//console.log("Plot:")
//console.log(g_sensors[json.id])
- $.plot("#sensor"+String(json.id)+"_plot", [g_sensors[json.id]])
+ $.plot("#sensor"+String(0)+"_plot", [g_sensors[0]])
$.ajax({url : "/api/sensors", data : {id : json.id}, success : function(data) {$(this).updateSensor(data);}});
//
for (var i = 0; i < g_numSensors; ++i)
{
- // $.ajax({url : "/api/sensors", data : {id : i}, success : function(data) {$(this).updateSensor(data);}})
+ $.ajax({url : "/api/sensors", data : {id : 2}, success : function(data) {$(this).updateSensor(data);}})
}
});