I'm currently working on a project that involves driving a little over 400 LEDs.
For this, I made a small Python library to communicate with a program on an Arduino that effectively acts as a "middleman" between the computer and a set of NeoPixel lights.
While working on this, I ran into some performance issues with the baudrate not being as high as it seemingly should be. After doing some testing, it looks like the "effective" baudrate goes as high as 576000.
Here is the program I used (on both sides) to test the effective baudrate.
Python script to send data
import time
import serial
BAUDRATE = 1000000
BC = 25000 # Byte count to send
BPB = 10 # Bits per byte
expected = BC * (1000 / (BAUDRATE / BPB))
ser = serial.Serial('/dev/ttyACM1', baudrate=BAUDRATE, timeout=1)
time.sleep(2)
before = time.time()
written = ser.write([0] * BC)
ser.flushOutput()
print(f'written in {(time.time() - before) * 1000} milliseconds')
print(f'should be around {expected} milliseconds')
millis = int(ser.readline().decode('utf-8'))
read = int(ser.readline().decode('utf-8'))
print(f'took {millis}ms to read {read} bytes')
print(f'effective baudrate: {round(BC * BPB / (millis / 1000))}')
Arduino program to read data and respond with stats
written in 423.44188690185547 milliseconds
should be around 250.0 milliseconds
took 433ms to read 25000 bytes
effective baudrate: 577367
I'm not sure what causes this and unfortunately I don't have another board on hand to test against. I'm using an Arduino R3 connected to my laptop through a simple USB connection.
Use an oscilloscope or logic analyzer to determine the actual bit timing, which has to be correct or the hardware UART won't work at all.
I believe you are measuring some sort of effective data transfer rate, which is diluted ("capped" if you will) by other computations. You can't expect that effective rate to match the serial Baud rate.
Thanks for the reply! Unfortunately I don't have an oscilloscope on hand at the moment, I'm sure it would be a worthwhile investment in the future though.
But to address the timings and the effective rate, even when the baudrates are far above 576000 (for example 921600, or twice that much), both the computer and the Arduino seem to "fall back" and agree to communicate at 576000 somehow. This is the part that confuses me.
As for the effective rate, in the Python script I'm simply writing and flushing, in the Arduino script I'm exclusively reading and discarding the data, with no additional computations. Wouldn't this then mean that anything above 576000 simply can't be kept up with?
Not possible. The Arduino Baud rate is a register setting that you specify in Serial.begin().
That is set in the ATmega328 of the Uno R3, which communicates via UART with an ATmega16U2, which in turn communicates with the PC over USB.
If you need faster serial communications, select an Arduino compatible MCU that communicates via native USB. The Teensy series does that, and some of the other more advanced Arduinos do too.
Thanks for clarifying, I'm still not super familiar with the intricacies surrounding hardware clocks and communication with boards so this is helpful.
Coming from a software development background, I'm tempted to switch to an ESP32 and use a TCP connection instead of a communicating through serial. Do you think this is feasible or am I missing something?
Update a set of 400 NeoPixel LEDs at least 30 times a second, but with the computation/maths related to the patterns/effects being ran on a separate machine, hence the serial/tcp talk.
With my current python library and the program on the arduino, I can send updates roughly 28 times a second, but I very occasionally drop some bytes.
I understand that the Arduino can only do so much, but the baudrate is my focus at the moment since the speed doesn't seem to match my expectations. (It's why I made the minimal example with just writing/reading as well, to remove any computations from the equation)
Seems like you need to figure out where the real bottleneck is and focus on that. An Arduino Uno R3 running Neopixels doesn't have much time for anything else, like receiving new instructions.
You need to be careful with serial communications while driving addressable LEDs, generally interrupts are disabled while updating the LEDs, and serial uses interrupts.
With some basic benchmarking, I've determined that reading the incoming message, updating the LEDs and then showing the changes takes about 10 milliseconds in the worst cases. So surprisingly enough, that doesn't seem to be the bottleneck.
Well you've just given me a new perspective on the problem haha.
Here's the thing, it takes 10 milliseconds at most on the Arduino's side, but if I time the "frame" itself from the python library, I get a whopping 28ms per frame.
Obviously you can't really help me there without looking into the code. However, do you happen to know if there's a sizeable amount of latency (at least a millisecond) when sending/reading from the computer to the MCU or the other way around?
Don't neglect the fact that millis() cannot reliably be used for timing when using the common libraries used for NeoPixels, because the millis counter cannot operate when interrupts are disabled. Adafruit's library totally ignores the loss of the millis() count, fastLED estimates the error and attempts to correct the millis() count.
I wasn't aware of that, I just added some more debugging lines to check the timings from the client's perspective in addition to the Arduino's timings. In addition to (I'd estimate) 1ms of latency to send back and forth, there seems to be roughly 12ms unaccounted for on the show call.
The USB documentation will tell you that it uses a fixed length message between the two devices on the USB. Baudrate only relates to the wait time during the filling of that message. Low baud rate means partially filled messages. At some point the message will be filled and sent and the subsequent bytes will fill the next message. The USB is SO MUCH faster than any baud rate you might select, that once the optimum wait time is reached, nothing is gained but indicating faster baud rate.