Connect Arduino UNO to Output Expander

I have 8 NeoPixel strips connected to the Serial to WS2812 8 Channel Driver which is then being controlled by the PixelBlaze. That’s working well.

However I would like to control the driver via serial with an Arduino UNO so I can send matrix values on the fly. Do I connect to the PixelBlaze with the UNO? There are RX and TX pins on the PixelBlaze but not on the expander.

And I may as well add to this, I am lost on the communication. I installed the Driver Library but I’m not clear on instantiating a matrix or at least identifying a matrix so I can run through locations and send on/off color values. Any examples would be really helpful.

Hi @hughqelliott,
If you’d like to set up Pixelblaze for use with a matrix, it can totally do that (let me know). But I’ll cover the output expander with an Arduino UNO here.

The driver was written for an esp8266, but it should work on your Uno with a little work. The key thing is to swap out Serial1 references with your Serial instance.

You can swap out the serial instances with a hack like this near the top of the PBDriverAdapter.cpp file:

#define Serial1 Serial

Or you can search and replace the instances in the code.

Now the Uno has a single hardware serial port called Serial that is also connected to the USB serial bridge that is used for the monitor and programming. Thats OK, but if you want to use it for the output expander, the baud rate has to be set to 2000000 (which is covered in the driver code), which is just within the capabilities of your Arduino. If you connect the monitor it will show mostly garbage generated by the driver, but aside from that it should work, and when you reprogram your Arduino, the output expander will ignore what it sees so you shouldn’t have to disconnect anything.

OK, on to setting up the driver.
The driver works on a list (the vector type) of channels. It uses some fancy C++ stuff to handle pointers automatically. To make a new vector of channels, say for just 1 of them, and configure the first channel, do something like this:

const int count = 1;
std::unique_ptr<std::vector<PBChannel>> channelsPointer(new std::vector<PBChannel>(count));
auto vector = *channelsPointer;
vector[0].channelId = 0; //which ID is this talking about? For multiple expander boards, the boardId * 8 is added
vector[0].startIndex = 0; //first pixel index to render (used in the callback)
vector[0].header.numElements = 3; //RGB=3, RGBW=4
vector[0].header.redi = 0; //red first
vector[0].header.greeni = 1; //green
vector[0].header.bluei = 2; //blue last
vector[0].header.pixels = 100;

pbDriverAdapter.begin();
pbDriverAdapter.configureChannels(std::move(channelsPointer));

Now that the driver is configured, we can render some pixels. This will run a callback function for each pixel, and when done it will send it to the output expander. When all pixels on all channels are done, it will issue the draw command, and all channels on all expanders will send data to the LEDs.

The show function is the same as the other drivers, so I’m going to borrow an example for an APA102 driver only slightly modified:

void loop() {
    for (int counter = 0; counter < NUM_PIXELS; counter++) {
        pbDriverAdapter.show(NUM_PIXELS, [counter](uint16_t index, uint8_t rgbv[]) {
            //if color order is set right, this should show a bright red pixel on dim green background
            if (counter == index) {
                rgbv[0] = 255;
            } else {
                rgbv[1] = 1;
            }
        });
        delay(100);
    }
}

The callback passed to show() above is going to draw a red pixel and chase that along the length of the strip. If you had a buffer that had pixel data in it, you could simply copy from it there instead.

Thanks for this! This is more than I expected.

I wonder, though, if I’m trying to bypass an already functioning system for no reason. @wizard I can share the use case and you can let me know.

I’ll have a large matrix in the order of 1200 pixels (40 m * 30 ppm is the estimate so far). Driving these is easily accomplished with what you’ve built. What I’ll want to do is update the matrix on the fly with input from an as-yet-unknown source.

Can I rapidly send new matrix values to the PixelBlaze or should I stick with the Arduino Serial solution?

How do you plan to get the data to the matrix/controller? If you want Pixelblaze to generate the pixels based on some input, then it would be a good way to go. If you are pushing pixels e.g. video from a computer, then I would recommend a different solution (though Pixelblaze might support that too at some point).

That’s my goal. PixelBlaze to generate the pixels based on some input. Like can I communicate to the PixelBlaze over Serial, for instance? That’s how the sensor board works, correct? Or potentially over the websocket?

Websocket API

Pixelblaze is controlled by a websocket connection on port 81. JSON and binary frames are sent to control and edit the patterns.

I don’t mean to be a pain, @wizard. I seem to be muddling through.

No worries, I’m happy to help.

There are a few gpio, and an adc. You can use those for simple things or reading sensors.

For network comms, websocket is the way to go.

For serial, there isn’t a general interface yet, but you can emulate the sensor board and use that to feed in arbitrary data. This would probably be the way to go if you wanted to get data to it from another micro.

Websocket sounds like the winner. Are there any examples kicking around of connecting to the PixelBlaze over the websocket and sending it matrix values? I’m pretty good at iteration and extrapolation.

The docs I have on it are here:
https://www.bhencke.com/pixelblaze-advanced

I would start with a websocket example for your language/environment, and then try connecting and sending some basic commands like changing brightness or setVars.

Firestorm might be a good reference, it connects to Pixelblaze and relays commands. It is written in NodeJS. https://github.com/simap/Firestorm

Using the setVars command, you can push data into a Pixelblaze pattern. The variable has to be an exported global, something like this in your pattern:

export var myVar = 0.5

Then you can send text/json websocket frames to change that variable like so:

{
	"setVars": {
		"myVar": 3.14159
	}
}

When you code first runs, the value will stick with the default until setVars is sent.

You can also do this with arrays:

export var myArray = array(10)

and setVars is smart enough to copy array data into your existing array, so it’s possible to send multiple values at once. This will work for many variables, but not thousands, so it’s not a great way to send 1200 pixels for example, but sending a handful of control variables will work great.

{
	"setVars": {
		"myArray":[1,2,3,4,5,6,7,8,9,10]
	}
}

You can send multiple variables in a single setVars. These will always get set between render frames, so it won’t update right in the middle of a render. If you send everything in one websocket frame, they will always update together (atomically).

You can also issue getVars, to read out the current values of everything that is exported (arrays included). The vars watch in the editor uses this command.

1 Like

@hughqelliott,
Did you have any luck? Let us know how it goes!

Creating a Node JS app is outside my skillset. I’d need to bone up on that first. I managed to get your FireStorm app working but it’s still a closed loop of communication.

Hi @hughqelliott,
I set up an example nodejs project. Right now it only has getVars, and I’ll add a few more when I get a chance.

1 Like