Control Arduino

OMG, solved…

I had a horrible time getting the fact the loop function loops but the state needs to be preserved between iterations. Following works:

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

#define NUMPIXELS 150

Adafruit_DotStar strip = Adafruit_DotStar(NUMPIXELS, DOTSTAR_BRG);

const byte numVar = 2;
uint8_t variables[numVar]; // an array to store the received data

static byte ndx = 0;

void setup() {
  strip.begin();
  strip.show();
  Serial.begin(115200);
}


void loop() {
  recvWithEndMarker();
  showNewData();
}


void recvWithEndMarker() {
  while (Serial.available() > 0 && ndx < numVar) {
    variables[ndx] = Serial.read();
    ndx++;
  }
}


void showNewData() {
  if (ndx == numVar) {
    strip.setPixelColor(variables[0], variables[1], 0, 0);
    strip.show();
    ndx = 0;
  }
}

THANKS!!!

1 Like

OK! I thought it would be good to post my complete solution, in case anyone finds it useful.

The following allows a user to control a DotStar 5 m LED strip with 30 LEDs per meter. The user can control the location, intensity, and size of the source of light from 3 Makie.sliders. The whole thing is pretty snappy!

For the Arduino:

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

#define NUMPIXELS 150

Adafruit_DotStar strip = Adafruit_DotStar(NUMPIXELS, DOTSTAR_BRG);

const byte numVar = 3;
uint8_t variables[numVar];
uint8_t oldp = 0;
uint8_t oldl = 1;
const uint32_t off = strip.Color(0, 0, 0);

static byte ndx = 0;

void setup() {
  strip.begin();
  strip.show();
  Serial.begin(115200);
}


void loop() {
  recInstructions();
  updateStrip();
}


void recInstructions() {
  while (Serial.available() > 0 && ndx < numVar) {
    variables[ndx] = Serial.read();
    ndx++;
  }
}


void updateStrip() {
  if (ndx == numVar) {
    strip.fill(off, oldp, oldl);
    uint32_t color = strip.Color(variables[1], 0, 0);
    strip.fill(color, variables[0], variables[2]);
    strip.show();
    ndx = 0;
    oldp = variables[0];
    oldl = variables[2];
  }
}

From Julia:

using LibSerialPort, Makie

n = 150

s = open("/dev/ttyACM0", 115200)
psl, pol = textslider(1:n, "Position", start = 1);
ssl, sol = textslider(1:2:21, "Size", start = 1);
isl, iol = textslider(0:255, "Intensity", start = 0);
sc = hbox(psl, ssl, isl)
msg = lift(pol, iol, sol) do p, i, s
    p = p - (s - 1)/2 - 1
    if p < 0
        s += p
        p = 0.0
    end
    pend = p + s
    if pend > n - 1
        s = n - 1
    end
    [UInt8(p), UInt8(i), UInt8(s)]
end
on(msg) do m
    write(s, m)
end
6 Likes

Nice! It’d be great to see a video of it in action.

One potential issue is that if your serial stream ever gets desynchronized there’s no way to recover - i.e. if the Arduino drops a byte or something then everything will be shifted over by a byte. One solution is to reserve a value (like 0xFF) that is never allowed to be part of your data and using that as a frame separator.

Might not be a problem in practice, but the strip ever starts acting wonky that might be what’s going on.

I’m trying to think if I should:

  1. read “infinitely” until I get the frame separator byte.
  2. read the n bytes I know are in one frame, and test if the last byte is indeed my separator byte. If it is not, then and only then keep reading bytes (and tossing them) until I get to the next separator byte.

Methods #1 is simple, but then I’m testing for the separator every loop. In method #2 I’m checking for the separator byte only once a frame. On the other hand I think I miss one extra frame, which is fine.

I suspect I’m fretting over virtually nothing…

@ssfrr, one more question: I need to operate 4 strips. Each has 150 LEDs. I’m considering connecting the 4 strips serially, to the one Arduino. I’ll then need to have a location of more than 255 (==256).
I’m trying to figure out how to do that on the Julia and the Adruino sides…
In Julia, something like this:

n = rand(0:600)
b1 = UInt8(n ÷ 255)
b2 = UInt8(mod(n, 255))
@assert 255b1 + b2 == n

and in the Arduino, like that?

position = 255*Serial.read()  + Serial.read() 

Thanks! I’ll be sure to upload a video of it tomorrow!

It’s more involved, but you find something similar in other UART protocols. This example is from the u-blox GPS chipset. A message header, register, length, payload, and finally a checksum. It’s a bit more work to read, as you have to validate parts of the message as it comes in, and have a timeout to make sure it doesn’t get stuck at a partial frame.

COBS is my favorite to solve the framing problem:

In Arduino, e.g.:

1 Like

Wow, COBS looks really cool. How would I encode my payload COBS-like? Also, wow, didn’t know there were libraries for stuff like this. I feel I should explore some more, maybe I’ll find other useful libraries for the Arduino…

Just to answer my own question:
In Julia, where p2 is a UInt16:

low = UInt8(p2 & 0xFF)
high = UInt8((p2 >> 8) & 0xFF)

and then in the Arduino, where frame contains the payload with the two bytes:

uint16_t p = frame[1] | (frame[0] << 8);

Sorry for the poopy quality, holding phone at the same time…

1 Like

That’s awesome! Looks like you got it to be quite snappy.

1 Like

On Linux, I’ve been using Julia code like this, to access serial ports:

serial_dev_name = "/dev/serial/by-id/usb-[...]"
run(`stty -F $serial_dev_name speed 115200`)
serial_io = open(serial_dev_name, read = true, write = true)

I’ve settled on LibSerialPort.jl, works great for me.

I suggest you use the millis() function which keeps a track of the number of milliseconds since the last restart of the board.
now = millis() - where now is an unsigned long integer ( you also need to assign ‘prev’ ( previous )

if (( now - prev ) > 200 )
{

prev = now ; }

This is a way of doing something every 200mS but not holding up the processing of other routines.

1 Like