10
$\begingroup$

Mathematica can upload code to the Arduino and read/write values to pins, execute commands, etc. Im trying to use this to do Direct Data Acquisition with a pressure sensor.

Here is the link to the documentation:

https://reference.wolfram.com/language/ref/device/Arduino.html

When it comes to executing commands, there is a specific configuration that needs to be uploaded to work with the devices attached to the Arduino.

Does anyone know how to write these configurations for pressure sensors? (BP180, Adafruit link) is the pressure sensors I am using

$\endgroup$
12
$\begingroup$

I wrote code for data acquisition with the Adafruit 10-DOF sensor a while back. I will share that code, with explanation, so that you can adapt it yourself.

Mathematica + Adafruit == TRUE

It's awesome and easy to connect to an Arduino with Mathematica:

Open a connection to Arduino and specify the paths to the Adafruit libraries (I put the library folders in the same directory as my notebook):

(* Connect to the Arduino. *)
arduino = DeviceOpen["Arduino", "COM6"]
(* Required libraries. *)
DOF = NotebookDirectory[] <> "Adafruit_10DOF";
LSM = NotebookDirectory[] <> "Adafruit_LSM303DLHC";
SEN = NotebookDirectory[] <> "Adafruit_Sensor";
BMP = NotebookDirectory[] <> "Adafruit_BMP085_Unified";
OTH = NotebookDirectory[] <> "Adafruit_L3GD20_U";

Define the initialization code, which is prepended to the Arduino sketch by Mathematica (initializes the sensors):

(* Initialization code. *)
initCode = "
  /* Assign a unique ID to the sensors */
  Adafruit_10DOF dof = Adafruit_10DOF();
  Adafruit_LSM303_Accel_Unified accel = Adafruit_LSM303_Accel_Unified(30301);
  Adafruit_LSM303_Mag_Unified mag   = Adafruit_LSM303_Mag_Unified(30302);
  Adafruit_BMP085_Unified bmp = Adafruit_BMP085_Unified(18001);

  byte initQ = 0;
  void initSensors() {
    accel.begin();
    mag.begin();
    bmp.begin();
  }";

You can tell Mathematica to include any Arduino function in the sketch. In my case I wanted a function for retrieving the Euler Angles (using the 10-DOF library from Adafruit). You define an Arduino-function like this (note that initSensors() from the initialization code is called the first time):

(* Function for retrieving euler angles. *)
getEuler = ArduinoCode[<|
    "ArgumentTypes" -> {Integer},
    "ReturnType" -> Real,
    "Code" -> "
     double getAngles(int t)
     {
        if (!initQ) 
         {
            initQ = 1;
            initSensors();
        }
         sensors_event_t accel_event;
         sensors_event_t mag_event;
         sensors_event_t bmp_event;
         sensors_vec_t   orientation;

         accel.getEvent(&accel_event);
         dof.accelGetOrientation(&accel_event, &orientation);
         mag.getEvent(&mag_event);
        dof.magGetOrientation(SENSOR_AXIS_Z, &mag_event, &orientation);
        double currentReading[] = {orientation.roll, orientation.pitch, orientation.heading};

        return currentReading[t];
     }"|>];

The function takes an integer argument, which specifies whether it should return the roll, pitch, or heading. There's room for improvement here if you want to return all three values at once - but to me it didn't matter (and it won't to you since you're looking to acquire a single value).

Next, configure the Arduino, upload libraries plus sketch:

(* Configure the Arduino. *)
DeviceConfigure[arduino, "Upload" -> {
    "Libraries" -> {"Wire", DOF, LSM, SEN, BMP, OTH},
    Initialization -> initCode,
    "Functions" -> <|
      "GetEulerAngles" ->  getEuler
      |>
    }];

That's it! You're ready to acquire data. I wrote a function to collect all angles, and created a table of values over 5 seconds:

(* Define function to get values. *)
allAngles := 
  DeviceExecute[arduino, "GetEulerAngles", #] & /@ {0, 1, 2};
data = Table[Pause[0.1]; allAngles, {i, 50}];

Once done, don't forget to close the connection to the Arduino (or the COM port will stay busy):

(* Close connection. *)
DeviceClose[arduino];

Here's an example of my output (I was doing loops with the Arduino, like a flying ace):

ListPlot[Transpose @ data, Joined -> True]

enter image description here

You wanted live data acquisition which is equally easy, just call the function using dynamic or manipulate or whatever suits your need. If precision is important (e.g. for comparing the pressure at two different times) I recommend RunScheduledTask.

| improve this answer | |
$\endgroup$
  • $\begingroup$ Have you tested performance i.e. how many samples you could capture per second? $\endgroup$ – Sascha Feb 1 '16 at 18:49
  • $\begingroup$ @Sascha No, I have not. If it is important to you I could. $\endgroup$ – V.E. Feb 9 '16 at 17:47
  • $\begingroup$ If it is not too much trouble I would greatly appreciate it. I plan to play around with Arduino and some sensors in the near future too so if you don't find the time to do some testing I will do it eventually and post what I found as edit to your answer if you like. $\endgroup$ – Sascha Feb 9 '16 at 17:53
  • $\begingroup$ @Sascha I'm sorry to say I never did any. Though I had such intentions. But feel free to post any findings if you'd like. $\endgroup$ – V.E. Feb 22 '16 at 21:27
  • 2
    $\begingroup$ I just tested performance briefly: DeviceExecute can be called with approx. 30Hz (which is a bit disappointing) while both DeviceWrite and DeviceReadBuffer can be called with approx. 700Hz at a baud rate of 115200 (higher rates such as 256000 didn't work) when listening on the serial port. $\endgroup$ – Sascha Mar 4 '16 at 14:46

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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