Control Arduino

Hi!
I want to control an LED strip (DotStar) from an Arduino UNO. Is there anything new in this domain?

I managed to find these 3 resources:

but maybe I missed something, perhaps this has some implications on that?

4 Likes

If your Arduino has a 32bit Arm processor (Uno is 8 bit), and you can install Linux on it, it is possible to install Julia and run you codes. However, because of ram limitations, probably the compile time will be slow.

Another way would be to control Arduino over SPI connection, which is already possible. Program your code using Arduino IDE, and get/send SPI information to Julia in realtime

I planned to make an active organization for embedded programming in Julia, however, not many people were interested in helping or it is not possible yet

4 Likes

That sounds fantastic. So would the idea be to have some function programmed in the Arduino IDE that can accept (I only need to send data to the Arduino, not receive anything, yet) some outer argument via the USB line connected to the computer? Do you have some minimal example for that? Thanks a lot!

The most common way to communicate with an arduino from a PC is over UART(AKA serial), not SPI. The Arduino has a USB-to-serial chip so when you plug it in it shows up as a serial port. You should be able to open up that serial port (e.g. /dev/ttyUSB0 or similar) and use read and write to communicate with the Arduino using its Serial class.

So youā€™d write an Arduino sketch that receives data over UART and controls the LEDs, and then write Julia code that sends the correct data over UART.

3 Likes

This all makes sense. Great! What Iā€™m missing now is practical parts:

  1. how and what do I send to a serial port in Julia?
  2. How do I receive things on the Arduino side?

I guess the what is some binary data that I know would mean one thing or another (like a number for the specific LED on a LED strip), the how is probably SerialPorts.jl or similar, and the rest I could Google. Again, if you have some working example that would be great, if not, Iā€™ll report back tomorrow and answer my own questions.

Thanks!!!

Yeah, itā€™s up to you to design a wire protocol and implement it on both ends. One thing that a lot of people seem to do is communicate using ASCII characters and separate each message with newlines. The nice thing is that this is pretty easy to debug and test (you can just use a serial console program to send messages to your arduino for testing). Itā€™s not the most efficient both because you send more bytes than you need to (e.g. to send the number 100 you need 3 bytes - ("100") instead of a single byte (0x64) with the raw number). Also you then need to convert the ASCII string to a number on the receiving side, which takes a few extra cycles.

The problem with using raw bytes is that you need some kind of message separator that will never show up in the data stream.

Probably a good middle ground is to send the data in ASCII but encoded as hex, so the number 100 becomes "64". That way it only doubles the communication overhead.

You also need to make sure the baud rate of the USB serial device matches what the arduino is expecting. This stackoverflow post goes over setting the baud rate using stty.

2 Likes

Ah, right! UART probably is much easier to implement for such simple tasks.

But if you need something with more data rate and speed SPI is a way to go.

1 Like

Just thought itā€™s worth mentioning that the code Iā€™m using on the Arduino includes the SPI library. I thinkā€¦

The issue is getting the SPI out of your computer. The arduino has a built-in USB->UART chip, but if you want to use SPI youā€™d need another piece of hardware, like this.

Definitely for talking between the Arduino and your LED strip SPI or I2C would make sense (whatever the LED strip supports, though they often use raw shift registers).

1 Like

OKā€¦ Iā€™ve managed to make some significant progress which is great, but I think Iā€™ve hit a wall ā† any help appreciated.

So for the Arduino, I managed to collect from serial input a message that will contain a & delimited list of location and intensity pairs (delimited by a :). A location indicates which of the LEDs in the strip we want to affect, and the intensity controls the red channel of said LED. This works very neatly with the Serial monitor.
Hereā€™s the sketch:

#include <Adafruit_DotStar.h>
#include <SPI.h>

#define NUMPIXELS 150
Adafruit_DotStar strip = Adafruit_DotStar(NUMPIXELS, DOTSTAR_BRG) ;


void setup() {
  strip.begin();
  strip.show();
  Serial.begin(9600); // set the baud rate
}

#define INPUT_SIZE 30

void loop() {
  if (Serial.available())
  {
//    Serial.println(true);
    char input[INPUT_SIZE + 1];
    byte size = Serial.readBytes(input, INPUT_SIZE);
    // Add the final 0 to end the C string
    input[size] = 0;

    char* command = strtok(input, "&");
    while (command != 0)
    {
      // Split the command in two values
      char* separator = strchr(command, ':');
      if (separator != 0)
      {
        // Actually split the string in 2: replace ':' with 0
        *separator = 0;
        int position = atoi(command);
        ++separator;
        int intensity = atoi(separator);

        strip.setPixelColor(position, 0, intensity, 0);
      }
      // Find the next command in input string
      command = strtok(0, "&");
    }
    strip.show() ;
  }
}

and for the Julia side, I used LibSerialPort.jl (not sure if this is ideal). For instance, here I light the second and fifth LEDs 100/255 and 22/255 respectively.

using LibSerialPort
s = open("/dev/ttyACM0", 9600)
msg = "1:100&4:22"
write(s, msg)
close(s)

While this mostly works, Iā€™m experiencing a lot of difficulty in programmatically affecting the LEDs, for instance in a for loop. Almost any attempts I make other than typing the write function in the REPL do not result in anything. If I add a sleep(2) to the for loop, I can get something. But I had hoped to link the LED strip to a Makie plot with slider etc and link the two so that one reflects the otherā€¦

It is as if I need some flag to stall Julia and wait before issuing the next command because the Arduino is not done yet. Alternatively Iā€™ll need to rethink this somehowā€¦

Thanks in advance for any wisdom!

A few comments:

  1. I think you can set the language of the code block to C++ using ```C++ or maybe Cpp so that the highlighting is correct
  2. 9600 is a pretty slow baud rate, Iā€™d go with 115200 if you can.
  3. Serial.readBytes waits for the requested amount of data to be available, or until a timeout (defaults to 1s). You might just want to use Serial.read to get a single byte, so you can handle each message as soon as it comes in.
  4. it looks like youā€™re assuming that what you get after Serial.readbytes is a full message - is that always true? For instance, if you imagine the the julia side has dumped 100 bytes of data to the serial stream, and youā€™re reading it in chunks of 30 characters, the end of the chunk youā€™ve read doesnā€™t necessarily correspond to the end of a message, and the beginning of the next chunk is misaligned as well. In general with streams you want to decouple the chunks that you read in from the message boundaries.
  5. I would simplify your communication protocol. You have fewer than 255 LEDs and the brightness seems to be 1 byte, so you could just have your protocol be something like &01FF&6400&0A10 to turn LED 1 to 255, LED 100 to 0, and LED 10 to 16. Then to parse the stream you just look for the first & and then parse the next 4 bytes into 2 uint8s. '\n' is also a nice message separator.
  6. If you use hex digits parsing is simpler than variable-length base-10 numbers - you can just do something like:
uint8_t led = (Serial.read() - '0') * 0x10 + (Serial.read() - '0')

Though youā€™d probably also want to check that Serial.read() didnā€™t return -1.

2 Likes

This is gold. Thanks!

Yes, I will always want to affect the intensity of one LED. If the speed isnā€™t affected all too much I could just do exactly that each time ā€“ affect one single LED every communication.

I have 4 strips that are 5 meters long with 30 LEDs per meter, but I will want to connect two of those to a strip that is 10 meters long with 300 LEDsā€¦ But yea, for sure, the 150 LED strips can use hex to communicate. I need to try that out.

OK, and for the real message between the lines: it seems that the kind of rapid control I hoped for is not impossible with an Arduino! Iā€™ll get back at it tomorrow and report back, thanks again!

You can also take the approach similar to I2C, where the first byte is the ā€œregisterā€, and each byte after writes to a register and auto increments. So itā€™s faster to update a sequence of LEDs, but not the whole strip starting from an arbitrary point.

Can you clarify what you mean by ā€œone communicationā€? One important thing to keep in mind is that the UART is an unbroken stream of bytes. so writing:

write(s, "1:100&4:22")
write(s, "6:20&8:60")

is the same as

write(s, "1:100&4:226:20&8:60")

So you need to include a message separator in your protocol. (Actually itā€™s possible that because youā€™re writing strings thereā€™s a null byte placed at the end of each string, but Iā€™m not sure)

I didnā€™t mean to imply this. at 115200 baud you should be able to send 2880 5-byte messages per second. with the 150LED setup this is about 20Hz update rate for the full strip. With 300 LEDs youā€™ll need a 2-byte LED index so itā€™s a 6-byte message, and you have twice as many LEDs to update, but it still should be pretty snappy.

Also as @Daniel_Berge mentioned you can make your protocol a little more sophisticated to reduce the overhead if thatā€™s a bottleneck.

One thing that tripped me up while doing a similar Arduino to LED-Strip setup (neopixels though, not dotstars), was that the Arduino Neopixel library handling the strip control disabled interrupts while sending its control sequence. Without interrupts, the Arduino drops other messages you send via the USB-serial interface. And if you send multiple characters that combine into one command this can lead to your commands being corrupted. Letā€™s say at some point the third byte of four that make up one command was lost and from now on every command starts with the second byte, because byte 4 of the previous one was interpreted as 3 and byte 1 of the new command as 4. I handled this by having the Arduino send a specific byte after it was done with one command, and the host computer would only send one multipart command for each time it received the ā€œdoneā€ byte. Donā€™t know if this applies to your specific setup, but if I can spare you the frustration I had debugging thisā€¦ :wink:

Thatā€™s probably my biggest gripe about Arduino. In order to make it user friendly, it tries its best to hide interrupts. On the other hand, if they hadnā€™t it would not be nearly as user friendly.

Thanks all, keep all the great info/advice coming :slight_smile:

I did not get that, before now. OK, so yea, I only meant ā€œone communicationā€ in the false sense of Julia sending the Arduino complete instructions for one LED (so location and intensity).

Apart from the apparent ease of a separator in strings, I would much rather work with the actual types the Arduino needs: integers. From what you wrote before about the hexes I should be able to accomplish that.

is very very very fast. Iā€™d be stocked to get something like that!

Have to admit I donā€™t know what or how you guys meanā€¦ I thought that the DotStars are intrinsically different than the Neoixels in how the strip updates etc. Not sure how this applies here.

I was afraid of the same thing, I have no idea if this applies to DotStar as well.

I tried to come up with the same solution but couldnā€™t quite get it to work properly (could be due to all the other problems I still have). In practice:

  1. Julia could send some new instructions
  2. repeatedly try to read from the serial port
  3. in the meantime the Arduino will execute the new instructions
  4. when done, itā€™ll send a ā€œdoneā€ byte
  5. Julia will finally read it, which will allow issuing new instructions to the Arduino

How exactly did you implement this? Iā€™m assuming with a few @asyncs etc?

I did this in python at the time, just spawned a different thread for that purpose which, like you said, just repeatedly tried to read the serial bus and after receiving the byte would be able to accept a new command from the main thread. Or rather I think I implemented a queue of commands that the main thread would push to, and then whenever ready the arduino thread would send the next item in the queue. Although I got some weird timings because of the way python thread switching is implementedā€¦ I would actually be interested in how much better this can be in julia :slight_smile: I have high hopes with the new work on threading

Iā€™m having a really hard time getting this to workā€¦ At least I have a much clearer picture of what I want to do.

There are two main approaches, one I know should work, the other seems more efficient, but might not work. Currently neither work for meā€¦

Send a string (should work)

In Julia:

using LibSerialPort

position = 1
intensity = 100

s = open("/dev/ttyACM0", 115200)
msg = bytes2hex([UInt8(position), UInt8(intensity)]) # this is a String
write(s, msg)
close(s)

Then in the Arduino, I can get a string message by following some of the first examples in this tutorial, but I canā€™t convert the string to two integers (one for location, one for intensity). I tried some of the Googled suggestions on converting a string hex to UInt8 arrays, but they didnā€™t work out for me.

Send an array of UInt8s (faster but harder)

In julia:

using LibSerialPort

position = 1
intensity = 100

s = open("/dev/ttyACM0", 115200)
msg = [UInt8(position), UInt8(intensity)] # this is a Vector{UInt8}
write(s, msg)
close(s)

In the Arduino I canā€™t seem to be able to read this into two integersā€¦

I know the trick is in the follow suggestion by @ssfrr:

Simplification

Since I already know that I will only send 2 bytes at a time, one for location, and one for intensity, and because the length of my message is always going to be 4 characters (in the string approach) or 2 bytes (in the UInt8 approach), I donā€™t really need a delimiter.

Iā€™ll keep hacking at itā€¦

With your send a string example, try the code in c - Convert hex string (char []) to int? - Stack Overflow - it converts a null-terminated string rather than just two characters but it should be easy to adapt for your situation.

With your array of UInt8 example, it should just be a case of

int led = Serial.read()
int intensity = Serial.read()

since the data is already in binary format. Note that according to the Arduino reference, Serial.read returns an int rather than a byte since it uses -1 as a sentinal value for no data.

(Disclaimer: Iā€™ve never programmed an Arduino, though I have done plenty of other embedded work.)