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?
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?
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
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.
This all makes sense. Great! What Iām missing now is practical parts:
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
.
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.
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).
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:
```C++
or maybe Cpp
so that the highlighting is correctSerial.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.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.&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 uint8
s. '\n'
is also a nice message separator.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
.
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ā¦
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
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:
How exactly did you implement this? Iām assuming with a few @async
s 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 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ā¦
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.
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:
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.)