diff --git a/l2ork_addons/raspberry_pi/disis_gpio/chown_gpio b/l2ork_addons/raspberry_pi/disis_gpio/chown_gpio deleted file mode 100755 index 5ceef3a74250d3b6f4d1dbb3e967438ec45a5a54..0000000000000000000000000000000000000000 --- a/l2ork_addons/raspberry_pi/disis_gpio/chown_gpio +++ /dev/null @@ -1,3 +0,0 @@ -sudo chown -R $USER:$USER /sys/class/gpio/* -sudo chown $USER:$USER /sys/class/gpio/gpio*/* -exit 0 diff --git a/l2ork_addons/raspberry_pi/disis_gpio/disis_gpio-help.pd b/l2ork_addons/raspberry_pi/disis_gpio/disis_gpio-help.pd index ba0e841226243a45c5233987dd314fff0f867bb5..c6c22517a48ec6e012280cd87a0c4c5ea48b3d29 100644 --- a/l2ork_addons/raspberry_pi/disis_gpio/disis_gpio-help.pd +++ b/l2ork_addons/raspberry_pi/disis_gpio/disis_gpio-help.pd @@ -1,44 +1,50 @@ -#N canvas 423 183 777 495 10; -#X obj 27 312 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0 +#N canvas 423 183 801 505 10; +#X obj 27 349 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0 1; -#X floatatom 424 428 5 0 0 0 - - -, f 5; -#X obj 424 449 bng 15 250 50 0 empty empty empty 17 7 0 10 -262144 +#X floatatom 424 448 5 0 0 0 - - -, f 5; +#X obj 424 469 bng 15 250 50 0 empty empty empty 17 7 0 10 -262144 -1 -1; -#X obj 27 333 metro 50; +#X obj 27 370 metro 50; #X msg 210 126 open; #X msg 251 126 close; #X msg 26 68 export 27; #X msg 215 68 unexport; #X msg 26 126 direction in; #X msg 115 126 direction out; -#X obj 424 407 disis_gpio 27; +#X obj 424 427 disis_gpio 27; #X msg 164 68 export; -#X msg 26 211 togglepwm \$1; -#X obj 26 189 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0 +#X msg 26 248 togglepwm \$1; +#X obj 26 226 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0 1; -#X msg 89 333 pwm \$1; -#X obj 89 312 nbx 5 14 0 1023 0 0 empty empty empty 0 -8 0 10 -262144 --1 -1 0 256 0; -#X text 150 312 pwm range is 0-1023; +#X msg 89 370 pwm \$1; +#X obj 89 349 nbx 5 14 0 1023 0 0 empty empty empty 0 -8 0 10 -262144 +-1 -1 426 256 0; +#X text 150 349 pwm range is 0-1023; #X msg 95 68 export 18; -#X floatatom 499 428 5 0 0 0 - - -, f 5; +#X floatatom 499 448 5 0 0 0 - - -, f 5; #X text 24 27 1st export the desired pin or use "export" without an argument if you have pin already specified via previous export call or as a creation argument; #X text 24 107 2nd set the direction \, and open the pin; -#X text 24 170 3rd (optional) enable hardware pwm (works only on pin -18); -#X text 24 250 4th either start sending data by sending float 0 or +#X text 24 287 4th either start sending data by sending float 0 or 1 to a regular pin or a 0-1023 range to a pwm (18) pin. If receiving data \, start the metro to get updates through the first outlet. Metro is not needed if only sending data.; -#X text 536 428 status (0=closed \, 1 open); -#X text 336 427 incoming data; -#X obj 424 312 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0 +#X text 336 447 incoming data; +#X obj 424 332 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0 1; -#X text 444 311 if communicating to a pin without pwm (accepts 0 or +#X text 444 331 if communicating to a pin without pwm (accepts 0 or 1); -#X text 512 406 optional arg sets the pin number; +#X text 512 426 optional arg sets the pin number; +#X text 536 448 status (0=closed \, 1=open); +#X obj 112 225 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0 +1; +#X msg 112 247 togglesoftpwm \$1; +#X text 24 160 3rd (optional) enable hardware pwm (works only on pin +18) OR software pwm (works on all pins \, including pin 18). The two +are mutually exclusive and will toggle each other off automatically +and reported in the console (this will not be reflected by toggles +below \, however).; #X connect 0 0 3 0; #X connect 1 0 2 0; #X connect 3 0 10 0; @@ -56,4 +62,6 @@ is not needed if only sending data.; #X connect 14 0 10 0; #X connect 15 0 14 0; #X connect 17 0 10 0; -#X connect 25 0 10 0; +#X connect 23 0 10 0; +#X connect 27 0 28 0; +#X connect 28 0 10 0; diff --git a/l2ork_addons/raspberry_pi/disis_gpio/disis_gpio.c b/l2ork_addons/raspberry_pi/disis_gpio/disis_gpio.c index c98a2aeae8340c735d72b4c8eaf2373e2102df9b..432daf50d009f891c289cc71cb1aa857aaf19368 100644 --- a/l2ork_addons/raspberry_pi/disis_gpio/disis_gpio.c +++ b/l2ork_addons/raspberry_pi/disis_gpio/disis_gpio.c @@ -14,14 +14,92 @@ #include <errno.h> #include <string.h> #include <unistd.h> +#include <pthread.h> #include "g_canvas.h" #include "wiringPi/wiringPi/wiringPi.h" +#include "wiringPi/wiringPi/softPwm.h" static t_class *disis_gpio_class; //#define FILE_PREFIX "/sys/class/gpio/" //#define FILE_EXPORT FILE_PREFIX "export" +// ============================== HELPER FUNCTIONS ================================= // + +typedef struct _params +{ + int *p_pin; + int *p_val; + int *p_thread; +} t_params; + +/* + * softPwmThread: + * Thread to do the actual PWM output + *************************************** + */ + +// The softPWM Frequency is derived from the "pulse time" below. Essentially, +// the frequency is a function of the range and this pulse time. +// The total period will be range * pulse time in uS, so a pulse time +// of 100 and a range of 100 gives a period of 100 * 100 = 10,000 uS +// which is a frequency of 100Hz. +// +// It's possible to get a higher frequency by lowering the pulse time, +// however CPU uage will skyrocket as wiringPi uses a hard-loop to time +// periods under 100uS - this is because the Linux timer calls are just +// accurate at all, and have an overhead. +// +// Another way to increase the frequency is to reduce the range - however +// that reduces the overall output accuracy... + +static void *disisSoftPwmThread(void *val) +{ + t_params *p = (t_params *)val; + + //printf("thread %d\n", *(p->p_thread)); + + piHiPri (50); + + while (!*(p->p_thread)) + { + if (*(p->p_val) != 0) + digitalWrite (*(p->p_pin), HIGH); + delayMicroseconds (*(p->p_val) * 10); + + if ((1000 - *(p->p_val)) != 0) + digitalWrite (*(p->p_pin), LOW) ; + delayMicroseconds ((1000 - *(p->p_val)) * 10); + } + //printf("thread exit %d\n", *(p->p_thread)); + *(p->p_thread) = 2; + return NULL ; +} + +/* + * softPwmCreate: + * Create a new PWM thread. + *************************************** + */ + +int disisSoftPwmCreate (t_params *p) +{ + int res; + pthread_t myThread; + + pinMode (*p->p_pin, OUTPUT); + digitalWrite (*p->p_pin, LOW); + + res = pthread_create (&myThread, NULL, (void *) &disisSoftPwmThread, (void *)p); + + //while (disisNewPin != -1) + // delay (1) ; + + return res; +} + +// ============================ END HELPER FUNCTIONS =============================== // + typedef struct _disis_gpio { t_object x_obj; @@ -32,11 +110,16 @@ typedef struct _disis_gpio int x_fdvalue; int x_dir; int x_pwm; - //int x_pwmrange; - t_symbol *x_chown; + int x_softpwm; + int x_softpwmval; + int x_softpwm_thread; // -1 = not necessary, 0 = start when necessary, 1 = close thread, 2 = thread has been closed + t_params x_params; + //t_symbol *x_chown; } t_disis_gpio; static void disis_gpio_close(t_disis_gpio *x); +static void disis_gpio_togglesoftpwm(t_disis_gpio *x, t_float on); +static void disis_gpio_togglepwm(t_disis_gpio *x, t_float on); static void disis_gpio_reflectstatus(t_disis_gpio *x) { if (x->x_fdvalue >= 0) { @@ -50,6 +133,8 @@ static int disis_gpio_isvalidpin(int pin) { int ret; switch (pin) { case 0: + ret = 1; + break; case 4: case 7: case 8: @@ -60,7 +145,7 @@ static int disis_gpio_isvalidpin(int pin) { case 24: case 25: case 27: - ret = 1; + ret = 2; break; default: ret = 0; @@ -81,11 +166,7 @@ static void disis_gpio_export(t_disis_gpio *x, t_float f) if ((int)f != 0) x->x_pin = (int)f; x->x_export = 1; - post("disis_gpio: exporting pin %d\n", x->x_pin); - if (system(x->x_chown->s_name) < 0) { // first to adjust permissions for /sys/class/gpio so that we can export - post("disis_gpio: failed setting permissions to /sys/class/gpio folder"); - x->x_export = 0; - } + post("disis_gpio: exporting pin %d", x->x_pin); char buf[1024]; //sprintf(buf, "gpio export %d %s\n", x->x_pin, dir->s_name); sprintf(buf, "echo %d > /sys/class/gpio/export\n", x->x_pin); @@ -93,10 +174,10 @@ static void disis_gpio_export(t_disis_gpio *x, t_float f) post("disis_gpio: failed to export requested pin"); x->x_export = 0; } - if (system(x->x_chown->s_name) < 0) { // to adjust permissions within the exported gpio pin - post("disis_gpio: failed setting permissions to /sys/class/gpio/gpio* subfolders"); - x->x_export = 0; - } + //if (system(x->x_chown->s_name) < 0) { // to adjust permissions within the exported gpio pin + // post("disis_gpio: failed setting permissions to /sys/class/gpio/gpio* subfolders"); + // x->x_export = 0; + //} } } else { post("disis_gpio: invalid pin number (0)"); @@ -159,8 +240,15 @@ static void disis_gpio_close(t_disis_gpio *x) post("disis_gpio: already closed"); else { + if (x->x_softpwm_thread == 0) { + x->x_softpwm_thread = 1; + while (x->x_softpwm_thread != 2) + usleep(100); + x->x_softpwm_thread = -1; + } close(x->x_fdvalue); x->x_fdvalue = -1; + } disis_gpio_reflectstatus(x); } @@ -180,30 +268,79 @@ static void disis_gpio_float(t_disis_gpio *x, t_float f) static void disis_gpio_pwm(t_disis_gpio *x, t_float val) { - if (x->x_fdvalue != -1 && x->x_pwm && x->x_pin == 18) { - pwmWrite (x->x_pin, (int)val); + int out; + if (x->x_fdvalue != -1 && (x->x_pwm || x->x_softpwm)) { + if ((int)val < 0) + out = 0 ; + else if ((int)val > 1023) + out = 1023 ; + else out = (int)val; + if (x->x_pwm) + pwmWrite (x->x_pin, out); + else { + x->x_softpwmval = (int)((t_float)out/1.023+0.5); + } } else { - post("disis_gpio: pwm messages can be only sent to opened pin 18 with togglepwm enabled"); + post("disis_gpio: pwm messages can be only sent to an opened pin with togglepwm enabled"); } } -static void disis_gpio_togglepwm(t_disis_gpio *x, t_float on) +static void disis_gpio_togglesoftpwm(t_disis_gpio *x, t_float state) +{ + if (x->x_fdvalue != -1) { + if (x->x_softpwm == (int)state) { + if (x->x_softpwm) + post("disis_gpio: soft pwm already enabled"); + else + post("disis_gpio: soft pwm already disabled"); + } else { + if (disis_gpio_isvalidpin(x->x_pin) == 2) { + if (state && x->x_pwm) { + disis_gpio_togglepwm(x, 0); + post("disis_gpio: disabling hardware pwm"); + } + x->x_softpwm = (int)state; + if (x->x_softpwm) { + x->x_softpwm_thread = 0; + x->x_softpwmval = 0; + disisSoftPwmCreate (&x->x_params); + } + else { + x->x_softpwm_thread = 1; + while (x->x_softpwm_thread != 2) + usleep(100); + x->x_softpwm_thread = -1; + } + } + } + } +} + +static void disis_gpio_togglepwm(t_disis_gpio *x, t_float state) { if (x->x_fdvalue != -1 && x->x_pin == 18) { - if (x->x_pwm == (int)on) { + if (x->x_pwm == (int)state) { if (x->x_pwm) post("disis_gpio: pwm already enabled"); else post("disis_gpio: pwm already disabled"); } else { - x->x_pwm = (int)on; - if (x->x_pwm) - pinMode(x->x_pin, PWM_OUTPUT); - else - pinMode(x->x_pin, OUTPUT); - } + if (disis_gpio_isvalidpin(x->x_pin) == 2) { + if (state && x->x_softpwm) { + disis_gpio_togglesoftpwm(x, 0); + post("disis_gpio: disabling software pwm"); + } + x->x_pwm = (int)state; + if (x->x_pwm) { + pinMode(18, PWM_OUTPUT); + } + else { + pinMode(x->x_pin, OUTPUT); + } + } + } } else { - post("disis_gpio: you can toggle pwm only on opened pin 18"); + post("disis_gpio: hardware pwm can be only enabled on the exported and opened pin 18"); } } @@ -253,6 +390,17 @@ static void disis_gpio_free(t_disis_gpio *x) { static void *disis_gpio_new(t_floatarg f) { if (!disis_gpio_isvalidpin((int)f)) return(NULL); + if (geteuid () != 0) + { + post("error: disis_gpio external requires pd-l2ork to be run with root privileges. You can achieve this by doing 'sudo pd-l2ork'. Alternately, if running pd-l2ork remotely via ssh use 'sudo -E pd-l2ork' to preserve the enviroment.") ; + return(NULL); + } + //char buf[FILENAME_MAX]; + //canvas_makefilename(glist_getcanvas((t_glist*)canvas_getcurrent()), "@pd_extra/disis_gpio/chown_gpio&", buf, FILENAME_MAX); + //if (system(buf) < 0) { // first to adjust permissions for /sys/class/gpio so that we can export + // post("disis_gpio: failed setting permissions to /sys/class/gpio folder"); + // return(NULL); + //} t_disis_gpio *x = (t_disis_gpio *)pd_new(disis_gpio_class); x->x_out1 = outlet_new(&x->x_obj, gensym("float")); x->x_out2 = outlet_new(&x->x_obj, gensym("float")); @@ -261,10 +409,15 @@ static void *disis_gpio_new(t_floatarg f) x->x_export = 0; x->x_dir = 0; x->x_pwm = 0; + x->x_softpwm_thread = -1; + + x->x_params.p_pin = &(x->x_pin); + x->x_params.p_val = &(x->x_softpwmval); + x->x_params.p_thread = &(x->x_softpwm_thread); + + wiringPiSetupGpio(); //x->x_pwmrange = 0; - char buf[FILENAME_MAX]; - canvas_makefilename(glist_getcanvas((t_glist*)canvas_getcurrent()), "@pd_extra/disis_gpio/chown_gpio&", buf, FILENAME_MAX); - x->x_chown = gensym(buf); + //x->x_chown = gensym(buf); return (x); } @@ -284,6 +437,8 @@ void disis_gpio_setup(void) A_FLOAT, 0); class_addmethod(disis_gpio_class, (t_method)disis_gpio_togglepwm, gensym("togglepwm"), A_FLOAT, 0); + class_addmethod(disis_gpio_class, (t_method)disis_gpio_togglesoftpwm, gensym("togglesoftpwm"), + A_FLOAT, 0); //class_addmethod(disis_gpio_class, (t_method)disis_gpio_pwmrange, gensym("pwmrange"), // A_FLOAT, 0); class_addfloat(disis_gpio_class, disis_gpio_float); diff --git a/l2ork_addons/raspberry_pi/makeall.sh b/l2ork_addons/raspberry_pi/makeall.sh index fb1dff3cfeab2601b595c8ec2724b1d7bb276f3d..ce8b475569667a1ba824e5e3f5dbd74b960e3621 100755 --- a/l2ork_addons/raspberry_pi/makeall.sh +++ b/l2ork_addons/raspberry_pi/makeall.sh @@ -1,6 +1,6 @@ cd disis_gpio ./build.sh -chmod 4755 chown_gpio +#chmod 4755 chown_gpio cd ../ cd disis_spi