Group Abstract Group Abstract

Message Boards Message Boards

Programming the World with Arduino and Mathematica

GROUPS:

Hello all, today I am going to demonstrate the use of Arduino Drivers I recently developed using the new functions in Mathematica 10. This demonstrations uses the following:

All of the hardware components can be found in the Official Arduino Starter Kit and almost all similar Arduino kits.

The functions I developed should in theory be system independent, i.e. run on Linux, Mac OS X, Windows, etc., but I used Windows to develop the functions, so I can only confirm that it works on Windows. I would love for others to try this out on Linux and Mac OS X and post their results.

The first step will be to upload the firmware I developed for the Arduino to your Arduino. You can do this using the Arduino software.

You can download the firmware from here.

Now select your Arduino board (I developed this for the Uno, so can only confirm that the Uno works, but again in theory should work on any Arduino), and open the .ino file you downloaded and upload it to the board.

After uploading the firmware to the Arduino, connect your Arduino and determine which Serial port your Arduino is attached on. One easy way to do this is to open the Arduino software and go under Tools -> Serial Port. On Windows machines this is a COMXX, where XX is some non-negative integer. On Macintosh machines, it is generally "/dev/tty.usbmodemXXXXXX" or "/dev/tty.usbserialXXXXXX", where X is some string of letters or numbers. In Linux, it is generally "/dev/ttyXXXX",where XXXX is some string of letters and numbers. Keep this port in mind as we will need it in a second.

Now the last setup step is to install the Driver package. It can be downloaded from here. Download it and open up Mathematica 10 and run

<< "(path_to_file)/ArduinoDriver.m"

Where (pathtofile) is wherever you downloaded the file to, for instance on Windows this might be C:UsersIanDownloadsArduinoDriver.m

Now, to open a connection to the device we use

arduinoObject = DeviceOpen["Arduino", "COM4"]

Where "COM4" is my serial port, replace that with whatever port your Arduino is attached on.

Now for some basic device control, we can use

DeviceWrite[arduinoObject, <|pinNumber -> 1|>]

To control the digital state of pinNumber, which should be an integer between 2 and 13, or a string corresponding to one of the analog pins, i.e. "A0" through "A5". Note that the strings must be capitalized.

Now attach an LED as shown in the Fritzing diagram below.

Fritzing diagram of LED attached to Arduino

We can now blink the LED with the following code.

Do[(
DeviceWrite[arduinoObject, <|pinNumber -> 1|>];
Pause[1];
DeviceWrite[arduinoObject, <|pinNumber -> 0|>];
Pause[1]),
{5}]

This will blink an LED attached to pinNumber 5 times, for one second each time. Note that pinNumber which must be defined elsewhere. For the circuit above it should be equal to 11.

We can also use the "analog" write functionality of the Arduino with DeviceWrite, using the following syntax

DeviceWrite[arduinoObject, {pinNumber, "analogOutput", state}]

Where pinNumber is a valid pin value, and state is an integer between 0 and 255, corresponding to the 8 bit resolution of the PWM pins.

If your LED was attached as in the picture above, using the following will allow you to adjust the brightness of the attached LED. Note that as the value is dynamically changed, the brightness jumps around, this is a known bug.

Manipulate[
DeviceWrite[arduinoObject, {11, "analogOutput", analogWriteValue}],
{{analogWriteValue, 0, "Value"}, 0, 255, 1}]

We can also read values from pin's states from the Arduino using DeviceRead. Before we can read values though, we need to configure the values using the function DeviceConfigure. To configure pinNumber as a digital input, we can use

DeviceConfigure[arduinoObject, <|pinNumber -> "digitalInput"|>]

This also works with analog pins, with the same syntax, except with "analogInput" instead of "digitalInput". Note that this is expandable to multiple pins, as it uses an association. In that case, we would just add another key-value pair to the association. This also works to configure a pin to be an output with "digitalOutput". Now that the pin is configured correctly, the Arduino will begin outputting values over serial, and the Serial TX light should light up on your device.

Let's try reading a pin with the following simple circuit, using a button and a resistor. Fritzing diagram of button attached to Arduino

With the circuit built, to read values, we use

DeviceRead[arduinoObject, pinNumber]

This returns the value of the pin as either a 0, corresponding to a low voltage, or a 1, corresponding to a high voltage.

Evaluating the previous command while holding down the button should return a 1, while without holding down the button should return a 0, if the circuit is built as described. The Serial read buffer may take two evaluations to be updated the very first time this is evaluated, but after that it should change properly.

If pinNumber was an analog input, this would return a value between 0 and 1023, corresponding to the 10 bit resolution of the analog to digital converter inside the Arduino. This function is also compatible with lists of pin numbers, where an association of pins to values is returned. Note that pinNumber can be either a non-negative integer between 2 and 13, or a string of the type "AX", where X is a non-negative integer between 0 and 5. This is done as follows

DeviceRead[arduinoObject, {pinNumber1, pinNumber2, ...}]

This returns an association of the type <|pinNumber1->state1, pinNumber2->state2, ... |>.

Let's also set up another circuit to demonstrate the analog input functionality. This would be ideally done with a joystick, but it can be done just as easily with two potentiometers (which is basically all a joystick is).

Fritzing diagram of two Potentiometers attached to an Arduino

The following code will display a circle at the coordinates, which are derived from the two potentiometers. The first thing we need to do is setup the pins to be analog inputs. We can get both values from DeviceRead as an association, which we then get the values from using Values, then divide the coordinates by 1023 to map the circle's coordinates to between 0 and 1. We also subtract the values from 0.5 to center the circle at the origin.

DeviceConfigure[arduinoObject, <|"A0" -> "analogInput", "A1" -> "analogInput"|>];
Dynamic[Graphics[{Blue,
Circle[ (Values[DeviceRead[arduinoObject, {{"A0", "A1"}}]])/1023 - .5, 0.1]},
Axes -> True, ImageSize -> {800, 800},
PlotRange -> {{-.75, .75}, {-.75, .75}}],
UpdateInterval -> .5]

Joystick demonstration example

Thanks!

Attachments:
POSTED BY: Ian Johnson
Answer
1 month ago

Ian, this is an excellent post - thanks so much for sharing! Wolfram Community allows file uploads. I think you should attach whatever complimentary to your project files you have - to the actual post. Just click edit on your post and than attach the files. Again, thank you for great material and detailed project description.

POSTED BY: Sam Carrettie
Answer
1 month ago

Thanks for the suggestion, I attached a notebook, which is just basically the original post in notebook form.

POSTED BY: Ian Johnson
Answer
1 month ago

This is great - it is nice to have this all in one notebook. But I actually meant ArduinoDriver.m file that is currently linked to webpage with an expiration date. That file won't be accessible after some time. If you wish to attach it - your original post can take many attachments.

POSTED BY: Sam Carrettie
Answer
1 month ago

Very nice! I'd like to be able to create a GUI with mathematica to control our microscope lasers via the Arduino. This seems like a very good start. Just tested and it works on my system (Windows). However, any thoughts on how fast this can pulse the LED? I tried setting the time delay to 1 ms and it seemed a bit off. How fast can we pulse?

POSTED BY: Timothy Stasevich
Answer
1 month ago

Well, when I built the drivers I used 19200 as the baud rate, which is quite slow for serious applications. It is definitely possible to increase the speed (in both the Mathematica driver file and the Arduino firmware) to 115200, which is the fastest I have seen with an Arduino. However, I would be surprised if it would ever be possible to have something akin to

Do[(DeviceWrite[aruidnoObject,<|pinNumber->1|>];Pause[1/10000];DeviceWrite[arduinoObject,<|pinNumber->0|>;Pause[1/10000]),{5}]

work faster than about 1 KHz, because the front end combined with all of the necessary Mathematica functions to write over serial, etc. add up pretty quickly and the timing becomes problematic.

Instead, I am currently developing a new functionality (using DeviceExecute), which would basically send one command over Serial to the Arduino, which would then cause the Arduino to run some pre-uploaded code (in C/C++), at which point there would be absolutely no problem doing things on the order of microseconds.

This is not yet available because what I plan to do is be able to write whatever commands you want the Arduino to be able to execute in Mathemetica using Symbolic C, then have Mathematica automatically update the firmware file, and then automatically upload the firmware to the chip, all without ever leaving Mathematica. I haven't quite gotten that far, but the most difficult task to me would seem to be the Symbolic C portion of it and the updating of the firmware, as I have accomplished just about all the other tasks.

Also, if you would like to change the baud rate on your drivers, you can do so by opening up the package in Mathematica, and right underneath Begin["Private"] is the definition for open[], which opens the Serial Port. Here, you can change the option from "BaudRate"->19200 to "BaudRate"->115200.

Changing the Baudrate in the Arduino firmware file, you can open up the file, and down a ways is the void Setup() loop. The second to last line here is Serial.begin(19200). Just change it to Serial.begin(115200), and re-upload it and you should be good to go. I tried this, and it seemed to have about 1 KHz frequency, but stopped flashing after a second or two and stayed on, so it's possible that the Arduino runs it fine, but after it finishes Mathematica takes a couple of seconds to catch up.

POSTED BY: Ian Johnson
Answer
1 month ago

Hi,

this might be interesting in terms of Arduino's speed:

http://www.dustynrobots.com/robotics/arduino-data-logging-and-speed-test/

http://arduino.cc/en/Reference/AnalogRead

This one is the fastest I could find:

http://forums.adafruit.com/viewtopic.php?f=31&t=30557

which claims about 100kHz sampling rate. And in

http://community.wolfram.com/groups/-/m/t/250923

I had trouble getting even close to that. The decay can happen very fast and I need a sampling rate that is as high as possible.

Cheers,

Marco

POSTED BY: Marco Thiel
Answer
1 month ago

While I'm not familiar with how the Gieger counter mentioned in your post works, it looks like it is doing one of two things. Either one, you send it a command over serial, it changes something, then waits for another command; or two you send it a command over serial, then it executes some type of small set of commands on its own processor, then returns the result.

As I mentioned above, the first method of having Mathematica and your computer do the legwork in determining what command to run when is very ineffective at high frequencies. Instead, having the Atmel chip on the Arduino execute things is much more effective, as there is no other processes to slow it down, i.e. serial communication, OS processes, etc. If one needs high frequencies I would say that having the Atmel chip on the Arduino execute those commands is the way to go. I would also say that any faster than the theoretical limit mentioned on the documentation page for analogRead would probably require special hardware, and then another driver developed for it, but it is certainly possible to have the Arduino execute some command at that frequency, then pump out the result (or an array of the results) over serial would be the best way to move forward.

In short, serial communication and Mathematica are definitely the bottlenecks for doing things quickly.

POSTED BY: Ian Johnson
Answer
1 month ago

Hi Ian,

well, the Geiger counter is actually rather simple. Like the Arduino it has an AMTEL chip, which in principle can be programmed. Whenever it detects a decay it sends a zero or a one to the serial, depending on whether the last inter-decay interval was shorter or longer than the previous one. It can send the 0/1 rather fast.

Mathematica listens via the serial. Sometimes, the inter decay event time is longer then 10 s and Mathematica times out; that's easy to detect and can be mended in post-processing. The problem is when two events happen in very quick succession.

In some projects, I actually do what you say and

Either one, you send it a command over serial, it changes something, then waits for another command; or two you send it a command over serial, then it executes some type of small set of commands on its own processor, then returns the result.

but in this case I just listen with Mathematica. There is no two way communication, I believe. When I used SerialIO I got to an update interval of 0.002 which was slightly better than the one I choose in MMA10, at 0.01 - everything else being the same. Mathematica is used to determine the times at which the event has (approximately) happened, up to a precision of the sampling interval.

With a couple of students, we also did try to read a simple Potentiometer as fast as we could - one way communication from the Arduino to MMA10. Do you know what the shortest UpdateInterval/highest ScheduledTask frequency is that MMA10 can handle?

Cheers,

Marco

POSTED BY: Marco Thiel
Answer
1 month ago

I am not familiar enough with the inner workings of Mathematica to know exactly what the upper limit is, but I do know that operations handling files, like OpenWrite and Write work much faster than higher level functions. You can see Arnoud's response here for some information on how to do that with a Raspberry Pi, but I'm not sure that would help you if you plan on using a normal computer (i.e. Mac or PC).

What you could try is to have an Arduino connected to your Geiger counter, then that Arduino would be connected to Mathematica. This would require a library known as Software Serial to have the Arduino be able to communicate with two UART (serial devices) simultaneously. Although, if as you said the Geiger counter only sends information, you could have just the receive pin of the Arduino connected to the Geiger Counter, then the transmit pin going to the Computer. Obviously this would require some hardware modification such that you take care of the UART to USB handling without using the onboard ATmega32u4 as the serial to USB converter.

Anyways, with all of that, you can have code on the Arduino listening for the 1's and 0's from the Geiger counter and be able to handle those at just about whatever frequency you like, then once the Arduino determines, hey something changed that is important, it can then send off a command to the computer and you can either send the raw data trough the Arduino to the computer or have the Arduino do some processing on it (as I said I am not familiar with this device, so I don't know what exactly that data looks like or if the Arduino has the capabilities to process it properly).

POSTED BY: Ian Johnson
Answer
1 month ago

Hi Ian,

I am not sure where you are currently sitting but Arnoud has such a counter. Anyway, your post is brilliant and I will try to put your code to use in some of my projects as well.

In one of your earlier replies you said that you could go up to 1kHz. Did you actually get there? If you manage a bidirectional communication at that speed that would be quite good.

Cheers, Marco

PS: By the way

arduinoObject = DeviceOpen["Serial", {Quiet[FileNames["tty.usb*", {"/dev"}, Infinity]][[1]], "BaudRate" -> 9600}]

seems to work fine on a Mac and you don't have to actually look anything up in the /dev directory. I found that useful.

POSTED BY: Marco Thiel
Answer
1 month ago

I work remotely from Minnesota, but anyways I did somewhat manage to get to 1KHz, but I can't confirm that it was 1KHz, as I don't have an oscilloscope handy, but it appeared to be flickering so fast that it almost seemed steady. I accomplished that by just using

Do[(
DeviceWrite[arduinoObject,<|pinNumber->1|;
Pause[1/2000];
DeviceWrite[arduinoObject,<|pinNumber->0];
Pause[1/2000]),
{1000}]

However when I wrapped that in Timing, it took about seven seconds to evaluate, and the light would flicker as I mentioned for about twoish seconds (again, don't have an oscilloscope handy, so can't confirm the time), then it would flash on and off for about two more seconds, then after that it would stay steady on either on or off, depending on which time I ran it, i.e. sometimes it would end with being on, sometimes it would end with being off. So that leads me to suspect that Mathematica is either doing stuff after sending the commands that takes seven seconds, or that it is getting "confused" with all the serial output such that it doesn't output correctly, on top of being slower than suspected.

Also, thanks for the tip on Mac serial discovery, I may have to integrate that into the FindDevice functionality!

Ian

POSTED BY: Ian Johnson
Answer
1 month ago