Award
Congratulations RobertL184, for answering the question and has received an EZ-Credit award! Share what you have learned and create a tutorial to help others by clicking here.

Help Connecting A Neopixel Ring

Assistance Requested

Help merne with their question and receive $10 of EZ-Credit to get more robots and parts from our store. The following information was provided about their previous efforts searching tutorials for a resolution.

merne claims to have checked these sources:
 
#31

The problem is that the loops that change pixel colors have delays in them and the arduino is busy in those loops and not checking the serial port for new commands. I have seen a few notes on the subject on the Internet. It is what I meant when I said I was not sure how responsive the arduino would be to serial commands. There is a way to write it that will work. There are some articles on adafruit about multitasking an arduino which help explain the way to do it. When I get time I will see if I can whip up something close to what you need as an example.

Luis is a probably better at this stuff than I am, but we all have different things we have worked with. I have been playing with neopixels for some projects lately. I made a guggenhat a while back that mixes neopixels with serial input that has the fixes in it you need.

#32

Couldn't you check for the pin state in the inner loops which would make it check every 50 ms? If the pin is high, jump out. Basically at that point you could set another pin high but in the opposite direction. The v4 could be set to watch for that pin to go high and start the serial write. When it is done, the v4 could set the first pin back to low. You get the idea. The unique thing here is that there are many different ports to handle flags passed back and forth between the two devices.

if you want to work on it, thate great. I will be working on it as soon as I can. I like learning new stuff.

#33

If you want to look into using digital signals to interrupt the loops the arduino does actually support interrupts. The same adafruit multitasking an arduino tutorials actually talk about using signals pins to interrupt loops. They are a good read.


https://learn.adafruit.com/multi-tasking-the-arduino-part-1


https://learn.adafruit.com/multi-tasking-the-arduino-part-2


https://learn.adafruit.com/multi-tasking-the-arduino-part-3

I think part 3 has a lot of the fix you need but it has been a bit since I read thru them. In part 3 they get rid of the delays in the loops. The code in part 3 uses push buttons to change operations, but a serial input could be used instead. If you really want to use up digital pins on the ez-b you could use the button code as is.

#34

Thanks Robert. I will check it out and see where it leads.

#35

@RobertL184, can you post an example of the fix you have for this?

#36

I should have my parts tomorrow (unless something happens in shipping). I will work on this some time soon and post my script and how I have things wired up. I plan on using some of the things posted by Robert in his last post and using a digital pin from the V4 to a digital pin on the arduino as an interrupt when serial communication starts and stops which will allow the arduino to know to interrupt its loop and catch the serial communication from the V4 which will set which color you want to have pulse again.

#37

Okay here is a total untested shot at the code for a single neopixel with animations which should be fairly responsive to serial commands. I haven't had time to test any of it. All I can say is that it does compile. The code is mainly from adafruit lesson number 3. It will probably need a bit more work to make everything function properly. The Ring1Complete function is the one I am least sure of.

Code:


/* real time Serial Controlled Neopixel Ring -- Code not tested yet
/
/ Serial Commands
/ Cr1,g1,b1 - Set whole Ring to same color specified by r1,g1,b1
/ Rt,d - Rainbow with interval t and direction d
/ Tr1,g1,b1,r2,g2,b2,t,d - Theater Chase between color r1,g1,b1 and color r2,g2,b2 with interval t and direction d
/ Wr1,g1,b1,t,d - Color Wipe in color r1,g1,b1 with interval t and direction d
/ Fr1,g1,b1,r2,g2,b2,s,t,d - Fade between color r1,g1,b1 and color r2,g2,b2 in s steps with interval t and direction d
/ Sr1,g1,b1,t - Scanner with color specified by r1,g1,b1 and interval t
/ Pp,r1,g1,b1 - Set individual pixel p to color specified by r1,g1,b1
/
/ p = pixel number
/ r1 = 1st color red values 0 to 255
/ g1 = 1st color greern values 0 to 255
/ b1 = 1st color blue values 0 to 255
/ r2 = 2nd color red values 0 to 255
/ g2 = 2nd color greern values 0 to 255
/ b2 = 2nd color blue values 0 to 255
/ s = number of steps
/ t = interval time values 0 to 255
/ d = direction 0 = Forward 1 = Backward
*/

#include <Adafruit_NeoPixel.h>

// Pattern types supported:
enum pattern { NONE, RAINBOW_CYCLE, THEATER_CHASE, COLOR_WIPE, SCANNER, FADE };
// Patern directions supported:
enum direction { FORWARD, REVERSE };

// NeoPattern Class - derived from the Adafruit_NeoPixel class
class NeoPatterns : public Adafruit_NeoPixel
{
public:

// Member Variables:
pattern ActivePattern; // which pattern is running
direction Direction; // direction to run the pattern

unsigned long Interval; // milliseconds between updates
unsigned long lastUpdate; // last update of position

uint32_t Color1, Color2; // What colors are in use
uint16_t TotalSteps; // total number of steps in the pattern
uint16_t Index; // current step within the pattern

void (*OnComplete)(); // Callback on completion of pattern

// Constructor - calls base-class constructor to initialize strip
NeoPatterns(uint16_t pixels, uint8_t pin, uint8_t type, void (*callback)())
:Adafruit_NeoPixel(pixels, pin, type)
{
OnComplete = callback;
}

// Update the pattern
void Update()
{
if((millis() - lastUpdate) > Interval) // time to update
{
lastUpdate = millis();
switch(ActivePattern)
{
case RAINBOW_CYCLE:
RainbowCycleUpdate();
break;
case THEATER_CHASE:
TheaterChaseUpdate();
break;
case COLOR_WIPE:
ColorWipeUpdate();
break;
case SCANNER:
ScannerUpdate();
break;
case FADE:
FadeUpdate();
break;
default:
break;
}
}
}

// Increment the Index and reset at the end
void Increment()
{
if (Direction == FORWARD)
{
Index++;
if (Index >= TotalSteps)
{
Index = 0;
if (OnComplete != NULL)
{
OnComplete(); // call the comlpetion callback
}
}
}
else // Direction == REVERSE
{
--Index;
if (Index <= 0)
{
Index = TotalSteps-1;
if (OnComplete != NULL)
{
OnComplete(); // call the comlpetion callback
}
}
}
}

// Reverse pattern direction
void Reverse()
{
if (Direction == FORWARD)
{
Direction = REVERSE;
Index = TotalSteps-1;
}
else
{
Direction = FORWARD;
Index = 0;
}
}

// Initialize for a RainbowCycle
void RainbowCycle(uint8_t interval, direction dir = FORWARD)
{
ActivePattern = RAINBOW_CYCLE;
Interval = interval;
TotalSteps = 255;
Index = 0;
Direction = dir;
}

// Update the Rainbow Cycle Pattern
void RainbowCycleUpdate()
{
for(int i=0; i< numPixels(); i++)
{
setPixelColor(i, Wheel(((i * 256 / numPixels()) + Index) & 255));
}
show();
Increment();
}

// Initialize for a Theater Chase
void TheaterChase(uint32_t color1, uint32_t color2, uint8_t interval, direction dir = FORWARD)
{
ActivePattern = THEATER_CHASE;
Interval = interval;
TotalSteps = numPixels();
Color1 = color1;
Color2 = color2;
Index = 0;
Direction = dir;
}

// Update the Theater Chase Pattern
void TheaterChaseUpdate()
{
for(int i=0; i< numPixels(); i++)
{
if ((i + Index) % 3 == 0)
{
setPixelColor(i, Color1);
}
else
{
setPixelColor(i, Color2);
}
}
show();
Increment();
}

// Initialize for a ColorWipe
void ColorWipe(uint32_t color, uint8_t interval, direction dir = FORWARD)
{
ActivePattern = COLOR_WIPE;
Interval = interval;
TotalSteps = numPixels();
Color1 = color;
Index = 0;
Direction = dir;
}

// Update the Color Wipe Pattern
void ColorWipeUpdate()
{
setPixelColor(Index, Color1);
show();
Increment();
}

// Initialize for a SCANNNER
void Scanner(uint32_t color1, uint8_t interval)
{
ActivePattern = SCANNER;
Interval = interval;
TotalSteps = (numPixels() - 1) * 2;
Color1 = color1;
Index = 0;
}

// Update the Scanner Pattern
void ScannerUpdate()
{
for (int i = 0; i < numPixels(); i++)
{
if (i == Index) // Scan Pixel to the right
{
setPixelColor(i, Color1);
}
else if (i == TotalSteps - Index) // Scan Pixel to the left
{
setPixelColor(i, Color1);
}
else // Fading tail
{
setPixelColor(i, DimColor(getPixelColor(i)));
}
}
show();
Increment();
}

// Initialize for a Fade
void Fade(uint32_t color1, uint32_t color2, uint16_t steps, uint8_t interval, direction dir = FORWARD)
{
ActivePattern = FADE;
Interval = interval;
TotalSteps = steps;
Color1 = color1;
Color2 = color2;
Index = 0;
Direction = dir;
}

// Update the Fade Pattern
void FadeUpdate()
{
// Calculate linear interpolation between Color1 and Color2
// Optimise order of operations to minimize truncation error
uint8_t red = ((Red(Color1) * (TotalSteps - Index)) + (Red(Color2) * Index)) / TotalSteps;
uint8_t green = ((Green(Color1) * (TotalSteps - Index)) + (Green(Color2) * Index)) / TotalSteps;
uint8_t blue = ((Blue(Color1) * (TotalSteps - Index)) + (Blue(Color2) * Index)) / TotalSteps;

ColorSet(Color(red, green, blue));
show();
Increment();
}

// Calculate 50% dimmed version of a color (used by ScannerUpdate)
uint32_t DimColor(uint32_t color)
{
// Shift R, G and B components one bit to the right
uint32_t dimColor = Color(Red(color) >> 1, Green(color) >> 1, Blue(color) >> 1);
return dimColor;
}

// Set all pixels to a color (synchronously)
void ColorSet(uint32_t color)
{
for (int i = 0; i < numPixels(); i++)
{
setPixelColor(i, color);
}
show();
}

// Returns the Red component of a 32-bit color
uint8_t Red(uint32_t color)
{
return (color >> 16) & 0xFF;
}

// Returns the Green component of a 32-bit color
uint8_t Green(uint32_t color)
{
return (color >> 8) & 0xFF;
}

// Returns the Blue component of a 32-bit color
uint8_t Blue(uint32_t color)
{
return color & 0xFF;
}

// Input a value 0 to 255 to get a color value.
// The colours are a transition r - g - b - back to r.
uint32_t Wheel(byte WheelPos)
{
WheelPos = 255 - WheelPos;
if(WheelPos < 85)
{
return Color(255 - WheelPos * 3, 0, WheelPos * 3);
}
else if(WheelPos < 170)
{
WheelPos -= 85;
return Color(0, WheelPos * 3, 255 - WheelPos * 3);
}
else
{
WheelPos -= 170;
return Color(WheelPos * 3, 255 - WheelPos * 3, 0);
}
}
};

// Define some NeoPatterns for the two rings and the stick
// as well as some completion routines
NeoPatterns Ring1(16, 6, NEO_GRB + NEO_KHZ800, &Ring1Complete);

int red1 = 0; // red value
int green1 = 34; // green value
int blue1 = 12; // blue value
int red2 = 0; // red value
int green2 = 34; // green value
int blue2 = 12; // blue value
int my_pixel = 0; // pixel number
int s = 5; // step value
int t = 10; // interval time value
int d = 0; // direction indicator 0= Forward 1 = Backward

// Initialize everything and prepare to start
void setup()
{
Serial.begin(9600);
Serial.setTimeout(10); // set serial timeout

// Initialize all the pixelStrips
Ring1.begin();
Ring1.ColorSet(Ring1.Color(0, 0, 0));
}

// Main loop
void loop()
{
int inchar;

if (Serial.available() > 0) {
inchar = Serial.read();
if ( inchar == 'C') { // string should start with C
red1 = Serial.parseInt(); // then an ASCII number for red
green1 = Serial.parseInt(); // then an ASCII number for green
blue1 = Serial.parseInt(); // then an ASCII number for blue
Ring1.ActivePattern = NONE;
Ring1.ColorSet(Ring1.Color(red1, green1, blue1));
}
if ( inchar == 'R') { // string should start with R
t = Serial.parseInt(); // then an ASCII number for interval
d = Serial.parseInt(); // then an ASCII number for direction
if (d ==0) {
Ring1.RainbowCycle(t, FORWARD);
} else {
Ring1.RainbowCycle(t, REVERSE);
}
}
if ( inchar == 'F') { // string should start with F
red1 = Serial.parseInt(); // then an ASCII number for red
green1 = Serial.parseInt(); // then an ASCII number for green
blue1 = Serial.parseInt(); // then an ASCII number for blue
red2 = Serial.parseInt(); // then an ASCII number for red
green2 = Serial.parseInt(); // then an ASCII number for green
blue2 = Serial.parseInt(); // then an ASCII number for blue
s = Serial.parseInt(); // then an ASCII number for steps
t = Serial.parseInt(); // then an ASCII number for interval
d = Serial.parseInt(); // then an ASCII number for direction
if (d ==0) {
Ring1.Fade(Ring1.Color(red1, green1, blue1), Ring1.Color(red2, green2, blue2), s, t, FORWARD);
} else {
Ring1.Fade(Ring1.Color(red1, green1, blue1), Ring1.Color(red2, green2, blue2), s, t, REVERSE);
}
}
if ( inchar == 'T') { // string should start with T
red1 = Serial.parseInt(); // then an ASCII number for red
green1 = Serial.parseInt(); // then an ASCII number for green
blue1 = Serial.parseInt(); // then an ASCII number for blue
red2 = Serial.parseInt(); // then an ASCII number for red
green2 = Serial.parseInt(); // then an ASCII number for green
blue2 = Serial.parseInt(); // then an ASCII number for blue
t = Serial.parseInt(); // then an ASCII number for interval
d = Serial.parseInt(); // then an ASCII number for direction
if (d ==0) {
Ring1.TheaterChase(Ring1.Color(red1, green1, blue1), Ring1.Color(red2, green2, blue2), t, FORWARD);
} else {
Ring1.TheaterChase(Ring1.Color(red1, green1, blue1), Ring1.Color(red2, green2, blue2), t, REVERSE);
}
}
if ( inchar == 'W') { // string should start with W
red1 = Serial.parseInt(); // then an ASCII number for red
green1 = Serial.parseInt(); // then an ASCII number for green
blue1 = Serial.parseInt(); // then an ASCII number for blue
t = Serial.parseInt(); // then an ASCII number for interval
d = Serial.parseInt(); // then an ASCII number for direction
if (d ==0) {
Ring1.ColorWipe(Ring1.Color(red1, green1, blue1), t, FORWARD);
} else {
Ring1.ColorWipe(Ring1.Color(red1, green1, blue1), t, REVERSE);
}
}
if ( inchar == 'S') { // string should start with S
red1 = Serial.parseInt(); // then an ASCII number for red
green1 = Serial.parseInt(); // then an ASCII number for green
blue1 = Serial.parseInt(); // then an ASCII number for blue
t = Serial.parseInt(); // then an ASCII number for interval
Ring1.Scanner(Ring1.Color(red1, green1, blue1), t);
}
if ( inchar == 'P') { // string should start with P
my_pixel = Serial.parseInt(); // then an ASCII number for pixwel
red1 = Serial.parseInt(); // then an ASCII number for red
green1 = Serial.parseInt(); // then an ASCII number for green
blue1 = Serial.parseInt(); // then an ASCII number for blue
Ring1.ActivePattern = NONE;
Ring1.setPixelColor(my_pixel, Ring1.Color(red1, green1, blue1));
Ring1.show();
}
}

// Update the ring.
Ring1.Update();
}

//------------------------------------------------------------
//Completion Routines - get called on completion of a pattern
//------------------------------------------------------------

// Ring1 Completion Callback
void Ring1Complete()
{
if ((Ring1.ActivePattern == RAINBOW_CYCLE) || (Ring1.ActivePattern == FADE) ||
(Ring1.ActivePattern == THEATER_CHASE) || (Ring1.ActivePattern == SCANNER)){
Ring1.Reverse();
}
}

#38

Thank you Robert. I will give it a shot tonight and post any modifications that I make to it.

#39

Thanks Robert, I just uploaded and ran your code. First thought I had to define the pin #. 'F' and 'R' work the others don't, the pixels are dark. I'll play with it.

#40

I just tested and it is working great. Thank you Robert. This is very nice. I used 1 for right I believe. This will make a lot of EZ-Robot InMoov owners very happy.

Just to be clear of how I have everything wired up...

V4 D0 is connected to RX1 on the arduino mini pro

Ground from the Arduino mini pro is wired to the neopixel ground.

Pin 6 from the Arduino mini pro is wired to the neopixel data in.

+ Power to the neopixel and the Arduino is running from a voltage regulator that reduces from 12 V to 5 V.

- power to the Arduino is connected to the voltage regulator.

In EZ-Builder
I have tested these different modes. They are cool. You wouldn't just want to run this script as it would probably flash the neopixel and then go black. These are some examples of how to use it.

Code:


#White light
SendSerial(d0, 9600, "C100,100,100")

#Ring rotating to the right
SendSerial(d0, 9600, "R10,1")

#fade from black to red
SendSerial(d0, 9600, "F255,0,0,0,0,0,50,2000,1")

#Turn off neopixel
SendSerial(d0, 9600, "C0,0,0")