14
$\begingroup$

I started exploring GPIO operations on the RPi with Mathematica by following this brief tutorial which describes how to use DeviceWrite to cycle an LED on and off. A natural extension is to use DeviceRead to find the current value of the pin, and this seems to work as well.

I then thought I would attempt to translate some of the introductory python scripts that Adafruit uses to demonstrate basic features of the RPi GPIO programming. In particular, I am trying to operate a basic photocell for light monitoring. The meat of the python code is this:

def RCtime (RCpin):
        reading = 0
        GPIO.setup(RCpin, GPIO.OUT)
        GPIO.output(RCpin, GPIO.LOW)
        time.sleep(0.1)

        GPIO.setup(RCpin, GPIO.IN)
        # This takes about 1 millisecond per loop cycle
        while (GPIO.input(RCpin) == GPIO.LOW):
                reading += 1
        return reading

which I first attempted to replicate with:

lightMeasure[] := Module[{r = 0, pin = 23},
    DeviceWrite["GPIO", pin -> 0];
    While[(pin /. DeviceRead["GPIO", pin]) == 0, r++];
    r
]

This function always returns 0. Executing something like:

DeviceWrite["GPIO", 23->0];DeviceRead["GPIO",23]

Gives a value of 1, which I interpret to mean that the DeviceWrite operation is not discharging the capacitor. To test this, I removed the circuit from +3.3V and executed the code above, and the result is still 23 -> 1. I can then physically remove the capacitor, discharge it and return it to the circuit. Finally, I get 23->0.

Looking at the python code, I think that the GPIO.setup/GPIO.output/time.sleep commands force the pin low for 100 ms, allowing the capacitor to discharge. It does not look like the analogous Mathematica command, DeviceWrite is able to do the same thing.

Is there some way I might be able to force a Device object to maintain a set value for a given amount of time?

$\endgroup$
4
  • $\begingroup$ Maybe you need something equivalent to time.sleep(0.1) in your Mathematica code -- Pause @ .1? $\endgroup$
    – m_goldberg
    Commented Nov 28, 2013 at 3:02
  • 2
    $\begingroup$ And now, we need not just Mma syntax highlighting but also an electronics layout renderer in the site. Where is @halirutan when we need him most? $\endgroup$ Commented Nov 28, 2013 at 3:37
  • 2
    $\begingroup$ @belisarius We don't need it. FYI, one such renderer already exists on SE (Example: Why doesn't the relocation of this resistor affect the circuit's total resistance?) via a partnership with Circuit Lab. $\endgroup$
    – rm -rf
    Commented Nov 28, 2013 at 5:02
  • $\begingroup$ @m_goldberg Thanks. It looks like executing Pause after DeviceWrite does not keep the GPIO output low as it does in the python code. $\endgroup$ Commented Nov 28, 2013 at 12:38

2 Answers 2

10
$\begingroup$

Update 12-8-2013

In this update, I demonstrate that not only is the RPi a good platform for introducing and using the MathLink API, but it is likely necessary giving the significant speed limitations on the RPi. I include working c-code since there are presently few examples of Mathematica/MathLink/RPi implementation.

As of now, it appears that there needs to be some back-end development of the DeviceWrite and DeviceRead commands in order to use them in any practical, custom GPIO data acquisition platform on the Raspberry Pi. Fortunately, significant improvements in the read/write cycling can be made through the help of MathLink-enabled c-programming and a GPIO interface for the Raspberry Pi. Using this library, I was able to generate some simple functions of the type:

#include <wiringPi.h>
#include "mathlink.h"
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

int pinread(int pin);
int pinhi(int pin);
int pinlo(int pin);
int pincycle(int pin, int num);
void multiread(int pin, int num);

int main(int argc, char *argv[]){
  wiringPiSetupGpio();
  return MLMain(argc, argv);
}

int pinread(int pin) {
  pinMode(pin, INPUT);
  return digitalRead(pin);
}

int pinhi(int pin) {
  pinMode(pin, OUTPUT);
  digitalWrite(pin,1);
  return 0;
}

int pinlo(int pin) {
  pinMode(pin, OUTPUT);
  digitalWrite(pin,0);
  return 0;
}

int pincycle(int pin, int num) {
  int i;
  for (i=0;i<num;i++) {
    pinhi(pin);
    pinlo(pin);
  }
  return 0;
}

void multiread(int pin, int num) {
  int i;
  int val[num];
 
  pinMode(pin, INPUT);

  for (i=0;i<num;i++) {
    val[i]=digitalRead(pin);
  }

  MLPutInteger32List(stdlink, val, num);

  return ;
}

Calling pinread through a Mathematica structure, e.g. Table[pinRead[pinno],{1000}] results in GPIO cycling on the order of 3 kHz, which is a marked improvement over the tens of hz I obtained with DeviceRead. Still better is to offload the entire cycling process to the c-code, which is what I do with multiread(). This change in GPIO interaction results in cycling between 1 and 3 MHz, which is about what one expects for the RPi.

Previous thoughts

Here's my latest advance, which is a solution of sorts; however I suspect there are better answers to be found. This circuit works by first using a GPIO low signal to discharge the capacitor. The time it takes for the capacitor to recharge depends on the resistance of the photocell and is monitored by continuously reading the GPIO pin until it is high. The assumption here is that the successive read commands happen at fixed and rapid time intervals, which is where I think the problem is.

As frequent visitors to M.SE know, functional programming is almost always faster than procedural programming, so the first step is to replace the While in my original code with something faster. The following is one way to do that:

lightMeasure[] := Module[{pin = 23},DeviceWrite["GPIO", pin -> 0]; 
  (pin /. Table[DeviceRead["GPIO", pin], {50}]) // Mean // N]

I made two changes from the original circuit: first, I changed the capacitor to 33 μF which increases the time constant and helps with the current reality that DeviceWrite and DeviceRead are not all that fast. The second change was to replace my photocell with a fixed resistor to make sure that the fluctuations I was observing were due to the programming and not the electronics. With 22 kOhm and 220 kOhm resistors, I obtained very consistent results with lightMeasure (~ 10% relative error). I could make a simple data viewer with an intentionally spinning Manipulate:

Manipulate[
 If[measure, AppendTo[x, lightMeasure[]]];
 ListLinePlot[x],
 {measure, {True, False}}, Initialization -> (x = {lightMeasure[]};)]

Below is a sample output of the light in my office with me putting my hand or a small cap over the sensor.

enter image description here

There's still much to be done, but this is a decent first step (and as far as I know the first example of home built RPi-Wolfram data acquisition). The next step is to improve the time it takes to make the measurement (about 1 s with this setup).

$\endgroup$
4
  • $\begingroup$ Looking forward for the PrepareDiner[] function $\endgroup$ Commented Nov 29, 2013 at 3:01
  • $\begingroup$ @bobthechemist Have you tried BinaryWrite? DeviceWrite might close the file (handle) after writing. This is pure speculation, but perhaps this causes the pin to relax? Dont have a Pi to test (yet) :). $\endgroup$
    – Ajasja
    Commented Nov 29, 2013 at 11:37
  • 1
    $\begingroup$ @belisaruius we are closer to that feature than you think. $\endgroup$ Commented Nov 29, 2013 at 12:27
  • $\begingroup$ @Ajasja I have not, but that is next on my list. $\endgroup$ Commented Nov 29, 2013 at 12:37
7
$\begingroup$

I think you have a simple collision of variable names, "pin" is pretty overloaded:

lightMeasure[] := Module[{r = 0, pin = 23},
    DeviceWrite["GPIO", pin -> 0];
    While[(pin /. DeviceRead["GPIO", pin]) == 0, r++];
    r
]

What is happening is that your While statement is being interpreted like this:

    While[(23 /. DeviceRead["GPIO", pin]) == 0, r++];

This is false on the first trip through, always, which is why you're getting r==0 returned.

I don't have access to the documentation for DeviceWrite or DeviceRead so I can only guess that it needs an option named "23" etc, and that it returns a replacement rule "pin -> ...", so based on that perhaps you could try:

lightMeasure[] := Module[{r = 0, pinnum = 23},
    DeviceWrite["GPIO", pinnum -> 0];
    While[(pin /. DeviceRead["GPIO", pinnum]) == 0, r++];
    r
]

EDIT: I've trolled the web a bit and even though there's insufficient documentation for 'DeviceWrite on the Wolfram Language reference page at the moment, here's a screenshot from Stephen Wolfram's blog that confirms the command's syntax: enter image description here

P.S. I have to say that having Mathematica on a Raspberry Pi sounds quite exciting...!

$\endgroup$
4
  • 2
    $\begingroup$ You'll find some (incomplete) DeviceWrite documentation here, with a number of examples. $\endgroup$
    – Szabolcs
    Commented Nov 28, 2013 at 20:24
  • $\begingroup$ @Szabolcs that's great find. When I search on "new 2013" I get a huge list of new functions! $\endgroup$
    – Lou
    Commented Nov 28, 2013 at 21:03
  • $\begingroup$ The reason for holding pin 23 (in my case) low is to discharge the capacitor in the circuit. The commands in python appear to set and keep the pin low; however DeviceWrite seems to send a pulse only and lets the pin relax. $\endgroup$ Commented Nov 28, 2013 at 21:26
  • $\begingroup$ updated docs here reference.wolfram.com/language/guide/ConnectingToDevices.html For ConnectingToDevices There is a whole page on reference.wolfram.com/language/guide/RaspberryPi.html RaspberryPi (I never heared of RaspberryPi before the last week, but it must be important) $\endgroup$
    – Nasser
    Commented Nov 29, 2013 at 2:26

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.