Neopixels Driven off RPI
-
I have a project I'm working on that uses an Arduino Pro Mini driving 3x 16 WS2812 NeoPixel rings. The LED data in are wired to individual data pins and the code currently works. What I would like is to be able to modify the sketch so the NeoPixels are driven by a single pin daisy chained together. The end goal is to be able to move from an Arduino based MCU to a Raspberry Pi.
I know you need to define the sketch with something like:
(index 0
led count 16)
(index 16
led count 16)
(index 32
led count 16)But I know there's more involved than that. Can anyone help with this?
#include <Adafruit_NeoPixel.h> #define NeoPixelStartupAnimationActive true //Show Startup Animation for all Neopixels (true = activated / false = deactivated) !!Attention!! Animation will only be played if all NeoPixels have the same number of LEDs #define COLOR(r, g, b) (((uint32_t)r << 16) | ((uint32_t)g << 8) | b) #define NumberNeoPixels 3 struct StructNeoPixelConfig { bool Active; uint32_t StartupAnimationColor; neoPixelType Type; uint16_t LEDs; uint16_t Pin; int8_t PixelOffset; float TempOffset; bool AnimationActive; bool AnimationReverse; uint8_t Brightness; bool AnimationMemoryActive; bool AnimationMemoryRunning; uint8_t AnimationMemoryPosition; uint8_t AnimationMemoryPosition_Memory; uint8_t AnimationMemoryRangeBegin; uint8_t AnimationMemoryRangeEnd; uint8_t AnimationMemoryAnimationColor; }; StructNeoPixelConfig NeoPixelConfig[NumberNeoPixels] = { { // Neopixel 1 Active : true, StartupAnimationColor : COLOR(0, 0, 255), Type : NEO_GRB + NEO_KHZ800, LEDs : 16, Pin : 7, PixelOffset : 2, TempOffset : 0, AnimationActive : true, AnimationReverse : false, Brightness : 8, }, { // Neopixel 2 Active : true, StartupAnimationColor : COLOR(255, 0, 0), Type : NEO_GRB + NEO_KHZ800, LEDs : 16, Pin : 6, PixelOffset : 0, TempOffset : 0, AnimationActive : true, AnimationReverse : false, Brightness : 8, }, { // Neopixel 3 Active : true, StartupAnimationColor : COLOR(0, 255, 0), Type : NEO_GRB + NEO_KHZ800, LEDs : 16, Pin : 8, PixelOffset : 0, TempOffset : 0, AnimationActive : true, AnimationReverse : false, Brightness : 8, }, }; #define COLOR1(x) COLOR(x) // expand a single argument to 3 for COLOR #define NeopixelRefreshSpeed 200 unsigned long NeoPixelTimerRefresh = millis(); uint8_t NeoPixelID; uint8_t NeoPixelLEDID; Adafruit_NeoPixel *NeoPixel_Device[NumberNeoPixels]; static int ConvertPosition2PixelIndex(int PixelCount, int PixelOffset, int Position, bool ReverseDirection) { int newposition; int newpixeloffset; if (ReverseDirection == false) { newpixeloffset = PixelOffset; } else{ newpixeloffset = -PixelOffset; } if ((Position + newpixeloffset) > PixelCount) { newposition = (Position + newpixeloffset) - PixelCount; } else if ((Position + newpixeloffset) < 1) { newposition = PixelCount + (Position + newpixeloffset); } else { newposition = (Position + newpixeloffset); } if (ReverseDirection == false) { return (PixelCount - newposition); } else { return (newposition - 1); } } // Initialize everything and prepare to start void setup() { } // Main loop void loop() { pinMode(LED_BUILTIN, OUTPUT); // Initialize Variables int16_t BrightnessID = 0; uint8_t NeoPixelAnimationStep; uint8_t NeoPixelAnimationCount; uint8_t NeoPixelAnimationID; uint32_t AnimiationColor[NumberNeoPixels]; // Initialize Neopixels for (NeoPixelID = 0; NeoPixelID < NumberNeoPixels; NeoPixelID++){ if (NeoPixelConfig[NeoPixelID].Active == true) { NeoPixel_Device[NeoPixelID] = new Adafruit_NeoPixel(NeoPixelConfig[NeoPixelID].LEDs, NeoPixelConfig[NeoPixelID].Pin, NeoPixelConfig[NeoPixelID].Type); NeoPixel_Device[NeoPixelID]->begin(); NeoPixel_Device[NeoPixelID]->show(); } } //Set start values for (NeoPixelID = 0; NeoPixelID < NumberNeoPixels; NeoPixelID++){ NeoPixelConfig[NeoPixelID].AnimationMemoryPosition = 0; NeoPixelConfig[NeoPixelID].AnimationMemoryPosition_Memory = 0; NeoPixelConfig[NeoPixelID].AnimationMemoryRangeBegin = 0; NeoPixelConfig[NeoPixelID].AnimationMemoryRangeEnd = 0; NeoPixelConfig[NeoPixelID].AnimationMemoryRunning = false; NeoPixelConfig[NeoPixelID].AnimationMemoryAnimationColor = 0; } // Startup-Animation int NeoPixelCount = 0; bool NeoPixelStartupAnimationActive_Consistency = true; if (NeoPixelStartupAnimationActive == true) { // Animation Consistency-Check for (NeoPixelID = 0; NeoPixelID < NumberNeoPixels; NeoPixelID++){ if (NeoPixelConfig[NeoPixelID].Active == true) { if (NeoPixelCount == 0) { NeoPixelCount = NeoPixelConfig[NeoPixelID].LEDs; } else { if (NeoPixelConfig[NeoPixelID].LEDs != NeoPixelCount) { NeoPixelStartupAnimationActive_Consistency = false; } } } } //Activate startup sequence & Co. if all activated NeoPixels have the same number of LEDs if (NeoPixelCount > 0 && NeoPixelStartupAnimationActive_Consistency == true) { //Initialize Neopixels && Build AnimationColor-Array NeoPixelAnimationCount = 0; for (NeoPixelID = 0; NeoPixelID < NumberNeoPixels; NeoPixelID++){ if (NeoPixelConfig[NeoPixelID].Active == true) { NeoPixel_Device[NeoPixelID]->setBrightness(255); AnimiationColor[NeoPixelAnimationCount] = NeoPixelConfig[NeoPixelID].StartupAnimationColor; NeoPixelAnimationCount++; } } //Startup Animation Phase #1 for (NeoPixelAnimationStep = 0; NeoPixelAnimationStep < NeoPixelAnimationCount; NeoPixelAnimationStep++) { for (NeoPixelLEDID = 0; NeoPixelLEDID < NeoPixelCount; NeoPixelLEDID++) { NeoPixelAnimationID = NeoPixelAnimationStep; for (NeoPixelID = 0; NeoPixelID < NumberNeoPixels; NeoPixelID++) { if (NeoPixelConfig[NeoPixelID].Active == true) { NeoPixel_Device[NeoPixelID]->fill(COLOR(0, 0, 0)); NeoPixel_Device[NeoPixelID]->setPixelColor(ConvertPosition2PixelIndex(NeoPixelConfig[NeoPixelID].LEDs,NeoPixelConfig[NeoPixelID].PixelOffset,(NeoPixelLEDID + 1), NeoPixelConfig[NeoPixelID].AnimationReverse), AnimiationColor[NeoPixelAnimationID]); NeoPixel_Device[NeoPixelID]->show(); } NeoPixelAnimationID++; if (NeoPixelAnimationID >= NeoPixelAnimationCount) { NeoPixelAnimationID = 0; } } delay(30); } } //Startup Animation Phase #2 for (NeoPixelAnimationStep = 0; NeoPixelAnimationStep < NeoPixelAnimationCount; NeoPixelAnimationStep++) { //Part A for(BrightnessID = 0; BrightnessID < 255; BrightnessID++) { NeoPixelAnimationID = NeoPixelAnimationStep; for (NeoPixelID = 0; NeoPixelID < NumberNeoPixels; NeoPixelID++) { if (NeoPixelConfig[NeoPixelID].Active == true) { NeoPixel_Device[NeoPixelID]->setBrightness(BrightnessID); NeoPixel_Device[NeoPixelID]->fill(AnimiationColor[NeoPixelAnimationID]); NeoPixel_Device[NeoPixelID]->show(); } NeoPixelAnimationID++; if (NeoPixelAnimationID >= NeoPixelAnimationCount) { NeoPixelAnimationID = 0; } } delay(1); } //Part B for(BrightnessID = 255; BrightnessID >= 0; BrightnessID--) { NeoPixelAnimationID = NeoPixelAnimationStep; for (NeoPixelID = 0; NeoPixelID < NumberNeoPixels; NeoPixelID++) { if (NeoPixelConfig[NeoPixelID].Active == true) { NeoPixel_Device[NeoPixelID]->setBrightness(BrightnessID); NeoPixel_Device[NeoPixelID]->fill(AnimiationColor[NeoPixelAnimationID]); NeoPixel_Device[NeoPixelID]->show(); } NeoPixelAnimationID++; if (NeoPixelAnimationID >= NeoPixelAnimationCount) { NeoPixelAnimationID = 0; } } delay(1); } } } } //Initial Neopixel for Loop for (NeoPixelID = 0; NeoPixelID < NumberNeoPixels; NeoPixelID++){ if (NeoPixelConfig[NeoPixelID].Active == true) { NeoPixel_Device[NeoPixelID]->clear(); NeoPixel_Device[NeoPixelID]->setBrightness(NeoPixelConfig[NeoPixelID].Brightness); NeoPixel_Device[NeoPixelID]->show(); } } }
-
@dhusolo said in Neopixels Driven off RPI:
#define NumberNeoPixels 3
I'd say, when you set this line to '1' and also reduce the
StructNeoPixelConfig NeoPixelConfig[NumberNeoPixels] = {
to have only one strip of 48 LEDs, the animation will still work, but slower.
Some of the outer For-loops will be obsolete, since you only have one strip, but I wouldn't care.
What surprises me, is the fact, that they've put the whole initialisation in the loop instead of the setup
You want to run it from RPi, so you'd have to rewrite the whole code in Python?
Is the RPi not able to run more than one LED strip? -
@o_lampe said in Neopixels Driven off RPI:
You want to run it from RPi, so you'd have to rewrite the whole code in Python?
Is the RPi not able to run more than one LED strip?The Pi reportedly struggles a bit with WS2812 strips because of the precise timing required - something that isn't also trying to run a whole operating system doing a ton of other stuff at the same time keeps the necesary timing better. There are various approaches, but a surprisingly limited number of pins that work with them - see https://pypi.org/project/rpi-ws281x/
When I've put LED strips on Pis I've used APA102 or similar that are more tolerant of timing (and also faster data rate). I just wish someone did a RGBW APA102 (or equivalent). https://learn.adafruit.com/adafruit-dotstar-leds has a comparison (where 'dotstar' is their name for APA102 and 'neopixel' is their name for WS2812).
-
@o_lampe Sorry I should've clarified. Normally the code would be in setup but I put it in loop for testing purposes. I wanted to the animation to loop continuously. Once I get the code figured out I will put it back into setup.
-
For SPI the Raspbian spidev driver is used (/dev/spidev0.0). This library and test program set the clock rate to 3X the desired output frequency and creates a bit pattern in RAM from an array of colors where each bit is represented by 3 bits
Is that the same idea behind the Duet2 NeoPixel implementation?
If so, then I have three remarks/questions- The pin described in the NeoPixel Wiki is not SPI MOSI pin, but Pin 5 of the CONN_SD is? Just a Typo?
- When I set M150 Q2400000, the reported frequency is 2.5MHz.
- I can't use M150 X1 on Duet2, but only X2=BitBanging?
-
@o_lampe the Duet 2 doesn't have a spare SPI channel, so it only uses bit banging. This requires all other activity to be suspended in order to meet the timing requirements of NeoPixels.
The NeoPixel outputs on Duet 3 boards use dedicated SPI channels.
I guess it should be possible to use the shared SPI channel and an external gate to drive NeoPixels on Duet 2.
-
@dc42 said in Neopixels Driven off RPI:
I guess it should be possible to use the shared SPI channel and an external gate to drive NeoPixels on Duet 2.
That sounds promissing!
The ext. gate for level shifting?
Do I have to put the request in the firmware wishlist section?