Message Boards Message Boards

18
|
67141 Views
|
34 Replies
|
28 Total Likes
View groups...
Share
Share this post:

Programming the World with Arduino and Mathematica

Posted 10 years ago

Note : this code is now obsolete, superseded by the built-in Mathematica Arduino driver starting in version 10.1

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 sketch I developed for the Arduino to your Arduino. You can do this using the Arduino software.

You can download the sketch that is attached to this post. The filename was changed from a .ino to a .txt, so you may need to change it back before the Arduino software recognizes it.

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 sketch 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 as an attachment to this post. Download it and open up Mathematica 10 and run

<< "(path_to_file)/ArduinoDriver.m"

Where (path_to_file) is wherever you downloaded the file to, for instance on Windows this might be C:\Users\Ian\Downloads\ArduinoDriver.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
34 Replies

HEllo... Not much esxciting going on between Mathematica and Arduino. Old firmware FIRMATA does not allow to read all the pins from MEGA. Please, reopen and take over the subject or comment .

POSTED BY: Jose Calderon

Yes.. I already posted an issue

For ecample, it does not explain hot to get the Device Properties.

POSTED BY: Jose Calderon

NOW IT WORKED!!!!\ I will check again with your link .. But the Wolfram guide in too confusing.

POSTED BY: Jose Calderon

I'm glad you got it working. And what about the Wolfram guide is too confusing (and I'm assuming by the Wolfram guide you are referring to the documentation page I linked to)?

Ian

POSTED BY: Ian Johnson

sketch to verify everything is ok with the board. At what speed is the Mathematica download running? Does this matter? My Device manager states that arduino receives at 9600 buds.

POSTED BY: Jose Calderon

I think there might have been a problem with the sketch I uploaded. I have modified the sketch, can you try reuploading the sketch?

Additionally, if you have 10.1, you can just use the builtin Arduino functionality (which evolved from this), documented at http://reference.wolfram.com/language/ref/device/Arduino.html

POSTED BY: Ian Johnson

Yes.. I uploaded using the Arduino loader..

POSTED BY: Jose Calderon

And you know that your serial port is "COM3"?

POSTED BY: Ian Johnson

I also use <|11 -> 1> but dod not work either.

enter image description here

enter image description here

POSTED BY: Jose Calderon

Hi again Jose,

So what exactly is not working for you? Have you uploaded the sketch to your Arduino with the Arduino software?

Ian

POSTED BY: Ian Johnson

Programming the World with Arduino and Mathematica

In[2]:= arduinoObject = DeviceOpen["Arduino", "COM3"]

Out[2]= DeviceObject[{"Arduino", 1}]

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

In[15]:= DeviceWrite[arduinoObject, <|11 -> "HIGH"|>]

Out[15]= <|11 -> "HIGH"|>

In[9]:= <|1 -> 1|>

Out[9]= <|1 -> 1|>

Also tested the LED with the insire 3V power supplied to confirm the LED is ok.

enter image description here

POSTED BY: Jose Calderon

This is a great subject. But a bit advance for new users. May you explain what is the "Arduino firmware" and ho w it differs from a "sketch"?

Also , explain what is a Wolfram package and how it is different from running a standard notebook?

Thank you!

POSTED BY: Jose Calderon

Hi Jose,

The Arduino firmware as I called it in the post is the same thing as a sketch. They are the same.

A Wolfram Language package is a file that contains Wolfram Language code and definitions that is meant to be portable. It is intended that packages contain useful definitions that is to be used multiple times. It is similar to a library file in other programming languages. You load packages with either the function Get or the function Needs. In my post, << is a shorthand notation for Get.

Ian

POSTED BY: Ian Johnson
Posted 9 years ago

Hello Ian, unfortunately my ideas are way beyond the skill I reacht up to now. I will try to hook up an arduino micro or nano first, 32u4 / 328P. Then the due...

The temptation is that DUE is not so limited in pin count as are the small arduinos. And the limitation of 10bit DAC, beiing noisy as it is, diminishes together with 4kHz usable sample rate the use as aquisition device. Due would probably stand out there, so I will try.

For you I found a link that mentiones M3 simulation model as open source, so you could swallow it in Mathematica, and it has morphing technology...

Yours

Andi

POSTED BY: Updating Name
Posted 9 years ago

Hello Ian, thank you for this approach!.

Can I use this with my Arduino DUE board, using the 32bit ARM cortex M3 CPU Atmel AtSam3X8E ? In Arduino, you have to let it download the Board via Boards Manager. DUE has a USB to serial bridge with Atmel ARM ATmega16U2 on board, having its own bootloader. The Sam3X8E also has a built-in USB device. And several Hardware Serial Ports, normally the standard one attached to Serial of 32U4 to get the Program in the chip with normal bootloader and bossac programmer.

I am looking forward for this, because combined with coding from mathematica, one could do an oscilloscope with the 12bit 2MHz 2 ADC's it employs. DAC is also there. And 12 PWMs, etc...

Could you use OpenOCD.org's Debugging features to inject code "live"?

Thanks again

Andi

POSTED BY: Andi Hofma

Hi Andi,

You can most certainly use this software with an Arduino Due, but as you said you will need to install the package inside the Arduino software to support the SAM chip on the Due. And you would interact with the Arduino Due via the serial port you mentioned, going through the 32u4 to the computer. You might be able to figure out some way to use the hardware serial ports on the chip itself with something like an FTDI chip on it, but I'm not entirely sure why you'd want to when there's already a dedicated UART/USB chip on the board itself.

Lastly, I am not familiar with the OpenOCD project enough to say whether or not you can inject features to the code "live", but a cursory glance at the project tells me probably not. It is certainly a doable task to rewrite some section of the flash memory to add code, but the problem is that it would get pretty complicated, and to my knowledge almost all of said code would need to be written in assembly, as the code you would load into the flash would have to be from a .hex file. You might look at the optiboot loader code on github, as the bootloader for Arduinos does basically what you are describing, "live" loading of code on the device. Note that the specific project I linked to doesn't support the Due, so you would have to write your own code for the SAM chip. While live loading of code onto a running arduino without resetting is complicated, I can say that the Arduino driver that is currently in Mathematica 10.1.0 (which only supports the Uno at the moment) does support changing a sketch and re-uploading the new sketch completely from within Mathematica. Take a look at the DeviceConfigure section of the documentation. It's not exactly what you were describing, but it is a lot more convenient than reopening the Arduino software every time you need to change the sketch. We do plan on expanding the builtin functionality to other Arduino boards in the future, but for now it is just the Uno.

Ian

POSTED BY: Ian Johnson

Hi there,

I just wonder whether the link to the firmware has been disabled on purpose or whether it's just me who cannot access it.

Cheers,

Marco

POSTED BY: Marco Thiel

I reattached the file as an attachment to the post. The file had a limited amount of time it was hosted on the server, and I had thought that the official driver would be in the software by the time it had expired, but unfortunately not. It will be a little bit longer before the official driver (which was derived from this work) will be integrated into the product.

Ian

POSTED BY: Ian Johnson

Dear Ian,

thanks a lot! That's great. Fantastic work!

Thanks,

Marco

POSTED BY: Marco Thiel

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

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

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

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

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

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

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?

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

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

POSTED BY: Ian Johnson

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

Ian, when using your wonderful Arduino Driver with an Arduino Micro I find that serialBufferRead, at odd times, does not contain the full data array upon which or also unrelated to this occurrence the notebook looses connection to arduinoObject. Is it advisable to include a second If statement in ArduinoRead[pin_] which checks for the Length of serialBufferRead ? I am using OS X 10.9.5 . I include a notebook with results. Thank you in advance, Hanspeter

Attachments:
POSTED BY: Hanspeter Helm

Hi, I have been looking into this, and I think the problem here is that too many requests are being sent to the Arduino, and the Arduino Micro chip doesn't have an additional chip for processing incoming Serial bytes, so I would say that it is possibly the cause of losing the connection, but is still quite wierd. I have an Arduino Micro chip here, and I can't seem to reproduce the disconnection, so it could be unrelated or something in the driver code I'm not seeing.

On the topic of ArduinoRead, I can almost guarantee that the problem is because too many requests are being sent to the Arduino, and Mathematica reads the serial buffer too fast, so it doesn't get a chance to become populated. As you mentioned a check for the length of serialReadBuffer[] would probably solve at least some of the problems, but the other problem is that because so many requests are being sent into the serial buffer on the Arduino, the entirety of the buffer can be overwritten in the time it takes to process an analogRead, as analogRead's are somewhat slow on the Arduino, mainly because the buffer is only 64 bytes. This is why my error values of "qqqq" are showing up, because the original bytes were overwritten, and now there are new bytes there that cause undefined behavior.

If you are curious, you can look into increasing the size of the Serial buffer on the arduino here. That refers to modifying the builtin arduino library that defines the size of the serial buffer to 256 in that case for the Arduino Uno, but it can be easily modified for a Micro. That will probably help out quite a bit with the "qqqq" showing up, but my main recommendation is to put a Pause in between the DeviceReads, something not less than 1/2000 seconds I think should be enough.

Ian

POSTED BY: Ian Johnson

Thank you Ian for looking into my problem. With a pause the error rate is significantly lower (almost perfect). I find that the error which I reported appears ALWAYS, when the Arduino Serial Monitor pop-up window is open at the time the Mathematica program is running, but this is not surprising.

I have a second question though: Why do commands like
DeviceConfigure[arduinoObject,<|"A5"->"analogInput"|>] or DeviceConfigure[arduinoObject,<|6->"digitalInput"|>] trigger a continuously repeated transmission of the serial buffer from the Arduino Micro, rather than enable a single transmission once a DeviceRead statement is sent ?

Thanking in advance,

Hanspeter

POSTED BY: Hanspeter Helm

I'm glad that helped!

And the reasoning I had for putting the device into an always reporting mode is that once one read is triggered, it's likely that more reads are going to be expected, so that was a way to help performance with something like

Dynamic[DeviceRead["Arduino",2]]

or something similar. In the version of the driver that will be released with Mathematica soon, this has changed so that one request provides one response, rather than putting the Arduino into an always reporting cycle.

POSTED BY: Ian Johnson

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
Reply to this discussion
Community posts can be styled and formatted using the Markdown syntax.
Reply Preview
Attachments
Remove
or Discard

Group Abstract Group Abstract