Accessing the RPi GPIO pins from APL

For Raspberry Pi specific issues .. more general issues will appear in UNIX and Linux

Accessing the RPi GPIO pins from APL

Postby ray on Mon Jan 23, 2017 6:40 pm

All Raspberry Pi models have a number of General Purpose Input Output (GPIO) pins.

These allow one to attach a multitude of "devices" to the RPi, allowing the RPi to read and write to them.

"Devices" can range from the simplest, a switch or button or buzzer, thru LED lights, and on to sensors to read distances, magnetic fields (compass), movement (accelerometers and Gyroscopes) temperatures, not to mention controlling motors and servos.

Many libraries already exist for accessing the GPIO pins from Python and C.

I am currently writing many of the ⎕NA calls needed to access the functions in the PIGPIO C library.
http://abyz.co.uk/rpi/pigpio/cif.html

Would any one else be interested, please let me know.

Ray
Ray Cannon
Please excuse any smelling pisstakes.
User avatar
ray
 
Posts: 238
Joined: Wed Feb 24, 2010 12:24 am
Location: Blackwater, Camberley. UK

Re: Accessing the RPi GPIO pins from APL

Postby Morten|Dyalog on Tue Jan 24, 2017 7:37 am

Hi Ray, I am just getting back into the Robot business, got myself a Pi3 and a Zero... Hope to resume blogging any day now. I am very interested in your work, please keep me posted!
User avatar
Morten|Dyalog
 
Posts: 462
Joined: Tue Sep 09, 2008 3:52 pm

Re: Accessing the RPi GPIO pins from APL

Postby ray on Tue Jan 24, 2017 4:39 pm

I am looking forward to reading your next blog. My robot ("PiRat bot") is still running an old model B Pi (without an Arduino), although now I am doing most of my testing on a Pi2.

So far I have written the ⎕NA calls to access the following 39 PIGPIO C programs:
Code: Select all
bbI2CClose           gpioSetWatchdog      gpioWrite            i2cReadWordData     
bbI2COpen            gpioTerminate        i2cBlockProcessCall  i2cSwitchCombined   
bbI2CZip             gpioTick             i2cClose             i2cWriteBlockData   
gpioCfgGetInternals  gpioTrigger          i2cOpen              i2cWriteByte         
gpioDelay            gpioWaveAddGeneric   i2cProcessCall       i2cWriteByteData     
gpioHardwareRevision gpioWaveAddNew       i2cReadBlockData     i2cWriteDevice       
gpioInitialise       gpioWaveClear        i2cReadByte          i2cWriteI2CBlockData
gpioRead             gpioWaveCreate       i2cReadByteData      i2cWriteWordData     
gpioServo            gpioWaveTxSend       i2cReadDevice        i2cZip               
gpioSetMode          gpioWaveTxStop       i2cReadI2CBlockData                       

"gpioInitialise" need to run before (most) of these function can work and then "gpioTerminate" before you can leave APL. (Trying to leave without running "gpioTerminate" appears to cause APL to hang.)

I am using some of the i2c* functions to read a HMC5883L Triple-axis Magnetometer (Compass) Board and the MPU6050 Six-Axis Motion Tracking Devices (Gyro + Accelerometer + thermometer),

My robot has "pan and tilt" servos controlling the "head" movements. I have just replaced calls to "Servoblaster" that I originally used, by calls to gpioServo.

Elsewhere, the gpioWave* functions are being used to produce musical notes via a buzzer connected to one of the GPIO pins.

And to read the distance via a HC-SR04 ultrasonic range finder, I am using gpioSetMode, gpioWrite, gpioRead, gpioTrigger and gpioTick. The HC-SR04 device requires you to measure the time the "echo" pin remains high. From APL I can measure distances to an accuracy of about 1cm, where as, running from C, one can achieve better than 0.5 cm accuracy (but only by using gpioSetAlertFunc).

"gpioSetAlertFunc" set up a call back function to be triggered whenever a specific pin changes state (from 0 to 3.3 volts or back). One of the arguments it passes is the clock tick when the rising or falling edge of the pulse was detected.

However, so far, I have been unable to get gpioSetAlertFunc to successfully execute the APL "call-back" function that I have tried to pass to it.

So the best APL code I have come up with to time a pulse on pin ⍵ is
Code: Select all
time←{                                             ⍝ Time the pulse on GPIO pin ⍵
     read←{
         ⍺≠#.PiGpio.gpioRead ⍵:#.PiGpio.gpioTick   ⍝ Return current tick
         ⍺ ∇ ⍵                                     ⍝ tail recursive call
     }
     (1 read ⍵)-(0 read ⍵)
 }

Suggestion on a way of re-writing this code (in APL) with less "overhead" will be gratefully received. (Would rewriting the above as a single line help?)
Ray Cannon
Please excuse any smelling pisstakes.
User avatar
ray
 
Posts: 238
Joined: Wed Feb 24, 2010 12:24 am
Location: Blackwater, Camberley. UK

Re: Accessing the RPi GPIO pins from APL

Postby romilly on Wed Jan 25, 2017 8:03 am

Hi Ray,

I'm really impressed by the amount of code you've got working with ⎕na - the quad-fn that raises terror in the bravest APler's heart (or mine, at any rate).

I suspect that pin change polling code is always going to be slower than you'd like.

There is a way of getting the Pi's kernel to generate a C call-back on a pin state change. I don't know if APL can be coaxed into detecting that and turning it into an APL callback. Maybe team Dyalog can comment and/or help?

Romilly
romilly
 
Posts: 6
Joined: Tue May 03, 2016 9:51 am

Re: Accessing the RPi GPIO pins from APL

Postby Geoff|Dyalog on Thu Jan 26, 2017 9:35 am

Callback support is for a callback from a ⎕NA'd function that has been invoked. There is no support for just leaving the possibility of calling back into apl whilst it is doing other things.

So you can give a callback function as an argument to a ⎕NA'd function. That function can call the callback. Once the function is finished there is no support for it calling back into apl.

Now if you run an apl thread which uses an ampersanded ⎕NA which only returns when the callback is desired - that is supported and may solve the issue.
Geoff|Dyalog
 
Posts: 43
Joined: Wed May 13, 2009 12:36 pm

Re: Accessing the RPi GPIO pins from APL

Postby ray on Thu Jan 26, 2017 6:45 pm

Thanks for the feedback Geoff.

The PIGPIO "C" function "gpioSetAlertFunc" requires two arguments
Code: Select all
int gpioSetAlertFunc(unsigned user_gpio,gpioAlertFunc_t f)
"user_gpio" is an GPIO pin integer between 1 and 53,
"f" is the call-back function.

"gpioSetAlertFunc" expect the called-back function to be passed in 3 arguments and return nothing
Code: Select all
void aFunction(int gpio,int level,uint32_t tick)
 {
     printf("GPIO%d became%d at%d",gpio,level,tick);
 }

So for the ⎕NA statement setting up gpioSetAlertFunc I have coded:
Code: Select all
⎕NA'U4 '/home/pi/PIGPIO/libpigpio.so|gpioSetAlertFunc U ∇(P P P)'
which appears to match all the requirements from the "C" end.

The APL callback function I have written is
Code: Select all
CallBack(pins level tics)
⍝ gpioSetAlertFunc sets up this function as a callback when the level changes on <pin>
(⊃,/' ',∘⍕¨pins level tics)⎕NPUT ('/home/pi/callback',⍕level) 1
This should just create a text file containing the the parameters passed in. It does not try to return a result or interact with the APL session ("calling back into APL"). It is Namespace and thread independant, and uses no globals.

To call gpioSetAlertFunc, I
Code: Select all
echo←24
callback←⎕OR'CallBack'
rc←#.PiGpio.gpioSetAlertFunc&(echo callback)

(I have tried this last line with and without the &.)
Now this all appears to be fine (APL does not crash or complaint) except that my "CallBack" function does not appear to ever be called. So the expected files are not created.

It would be nice to get this working if we could, but for now I'm stuck.
Ray Cannon
Please excuse any smelling pisstakes.
User avatar
ray
 
Posts: 238
Joined: Wed Feb 24, 2010 12:24 am
Location: Blackwater, Camberley. UK

Re: Accessing the RPi GPIO pins from APL

Postby ray on Fri Jan 27, 2017 12:53 am

I have now written (in "C") a callback function ("afun") and linked it as an ".so" library ("libsonarEcho.so"). Once APL has imported "afun" (via an yet other ⎕NA call), it can be passed to "gpioSetAlertFunc" as a parameter.

"afun" is called twice (once on the rising pulse edge, and once on the falling pulse edge), passing the clock tick it receives as an argument from the first call to the second call via a static integer.

On the second call, "afun" calculates the distance in cm from the difference between the two clock tics, and writes the result out to a text file "sonar.txt".

Back in APL, I can then read (via ⎕MAP) the content of "sonar.txt" file.
Code: Select all
⎕NA'U4 '/home/pi/PIGPIO/libpigpio.so|gpioSetAlertFunc U ∇(P P P)'
⎕NA'/home/pi/libsonarEcho.so|afun U U U4'
...
 callback←⎕OR'afun'
...
 rc←#.PiGpio.gpioSetAlertFunc(echo callback) 
...
cm←⊃(//⎕VFI,80 ¯1 5 ⎕MAP'/home/pi/sonar.txt')

I have now completed a 100 test runs (all at the same distance) to compare my non-callback pure APL against APl calling gpioSetAlertFunc with the new "C" callback.

The pure APL returned 76% "good" results (within about 3% of the average) while the APL with a "C" callback 81% were "good". So it just seems worth the extra effort to use gpioSetAlertFunc with the "C" callback.
Ray Cannon
Please excuse any smelling pisstakes.
User avatar
ray
 
Posts: 238
Joined: Wed Feb 24, 2010 12:24 am
Location: Blackwater, Camberley. UK

Re: Accessing the RPi GPIO pins from APL

Postby Geoff|Dyalog on Fri Jan 27, 2017 8:54 am

The gpioSetAlertFunc is exactly the sort of function I said we cannot support. What this is doing is remembering a function address for later use and then returning.

Sometime later it makes a call to the supplied function address. Unfortunately, on the APL side all of the information and indeed the address only lasts until the gpioSetAlertFunc returns.

So the callbacks are useful for the sort of code that runs through a list and calls back for each element in the list.

What I was reaching for as a solution was to make a C function with the same syntax as gpioSetAlertFunc which called gpioSetAlertFunc and then went into a wait state. Lets call this function C. Each time the callback triggered it would call the APL because the C is still running. If C is invoked as a threaded call in an APL thread I think you get the effect you want.

If this works - tell me - then we might be able to provide a bit of syntax on ⎕NA to say "do not return" and thus avoid writing the C function.

Cleaning up would be an exercise. I presume there is a corresponding "unset" function. Its invocation would need to terminate the thread in which C was running. Tricky, I always dislike trying to terminate a thread from outside of the thread.
Geoff|Dyalog
 
Posts: 43
Joined: Wed May 13, 2009 12:36 pm

Re: Accessing the RPi GPIO pins from APL

Postby ray on Sat Jan 28, 2017 11:53 am

Thanks Geoff, I'v had to read your last reply a second (or third) time before I finally understood exactly what your saying. I will give it another try and let you know the result.

Thanks
Ray Cannon
Please excuse any smelling pisstakes.
User avatar
ray
 
Posts: 238
Joined: Wed Feb 24, 2010 12:24 am
Location: Blackwater, Camberley. UK

Re: Accessing the RPi GPIO pins from APL

Postby Geoff|Dyalog on Mon Jan 30, 2017 2:58 pm

Sorry Ray. There is a reason why I am not responsible for Dyalog's documentation.
Geoff|Dyalog
 
Posts: 43
Joined: Wed May 13, 2009 12:36 pm

Next

Return to Raspberry Pi

Who is online

Users browsing this forum: Google [Bot] and 1 guest