My 6 year old son wants to be a robot for Halloween. We are building him a costume but I wanted to surprise him with a lighted robot face. I was going to build this with components (which would be hard because it has been 20 years since I did anything with components) and came across Arduino. I am sold this will be easier.
I want to:
Make a mouth light when he talks
Have eyes that light
I was thinking of purchasing:
Starter Pack for Arduino
Adafruit Bicolor LED Square Pixel Matrix
(2) x NeoPixel Ring - 16
Electret Microphone Amplifer
So, the questions
1) Can I use the NeoPixel ring with the Arduino? (I think I answered this with a google search)
2) Can I drive both the NeoPixel ring AND the Pixel Matrix simultaneously? The pixel ring would swirl a color around. The Matrix would feed off the Mic to mimic a robot mouth (the piccolo example is NEARLY everything I want!)
3) is the Uno too big to be mounted to a robot helmet? I figured I could just put on the back in a cardboard enclosure ... Should I be interested in a smaller board?
4) is there any hardware missing from my shopping cart to make this work?
Thanks!
Robot Face for Halloween ...
Moderators: adafruit_support_bill, adafruit
Please be positive and constructive with your questions and comments.
- Franklin97355
- Posts: 23940
- Joined: Mon Apr 21, 2008 2:33 pm
Re: Robot Face for Halloween ...
Take a look at this and related tutorials. https://learn.adafruit.com/electronic-d ... e/overview
- Klogg96
- Posts: 13
- Joined: Wed Sep 23, 2015 5:58 pm
Re: Robot Face for Halloween ...
Ok, so assigning backpacks channels via jumpers. Matrices of matrices for the displays ... So all that makes sense. My only question left would be does the pixel ring behave like a backpack?
The back of the board does not seem to show any jumpers. from what I have seen, this should all work just fine and I feel confident I can figure but out. Just didn't want to order parts that won't do what I am hoping for.
Thanks!
The back of the board does not seem to show any jumpers. from what I have seen, this should all work just fine and I feel confident I can figure but out. Just didn't want to order parts that won't do what I am hoping for.
Thanks!
Last edited by Klogg96 on Wed Sep 23, 2015 9:27 pm, edited 1 time in total.
- Franklin97355
- Posts: 23940
- Joined: Mon Apr 21, 2008 2:33 pm
Re: Robot Face for Halloween ...
A pixel ring acts like a Neopixel strip. Check out the tutorials https://learn.adafruit.com/adafruit-neo ... e/overview
- Klogg96
- Posts: 13
- Joined: Wed Sep 23, 2015 5:58 pm
Re: Robot Face for Halloween ...
Great! Thank you for putting up with my elementary questions. I am convinced what I want to do can be done. I also skimmed past a "multitasking" article which I will read when I need to. I think I'll order the parts, follow the piccolo totorial and then try the rings out. After that j will try to get multitasking up and running.
Thanks for pointing me to the answers.... This may open a whole new world of learning for me and my son!
Thanks for pointing me to the answers.... This may open a whole new world of learning for me and my son!
- pburgess
- Posts: 4161
- Joined: Sun Oct 26, 2008 2:29 am
Re: Robot Face for Halloween ...
The LED Ampli-Tie project might be an even better starting point:
https://learn.adafruit.com/led-ampli-tie
This code is a lot simpler as it just responds to audio amplitude from the mic. The Piccolo project is doing all kinds of crazy math based on frequency and will be a lot harder for newcomers to follow.
https://learn.adafruit.com/led-ampli-tie
This code is a lot simpler as it just responds to audio amplitude from the mic. The Piccolo project is doing all kinds of crazy math based on frequency and will be a lot harder for newcomers to follow.
- Klogg96
- Posts: 13
- Joined: Wed Sep 23, 2015 5:58 pm
Re: Robot Face for Halloween ...
Thanks! I am headed down that track as a startarting point. I do agree about the simpler code and does what I need. I am also reading the multitasking thread as it seems the sooner I move to that the more portable snippets of code will be ...
I ordered all the stuff Thursday so I am waiting and trying to make sense of he language. I found the arduino library, but I cannot seem to find any adafruit library's. Also, for the piccolo project, which might be fun to play with, I cannot find documentation on the ffft code. Maybe I am just a little slow, but it makes reading and understanding the code difficult.
Thanks for your reply!
I ordered all the stuff Thursday so I am waiting and trying to make sense of he language. I found the arduino library, but I cannot seem to find any adafruit library's. Also, for the piccolo project, which might be fun to play with, I cannot find documentation on the ffft code. Maybe I am just a little slow, but it makes reading and understanding the code difficult.
Thanks for your reply!
- pburgess
- Posts: 4161
- Joined: Sun Oct 26, 2008 2:29 am
Re: Robot Face for Halloween ...
For most of the Adafruit libraries, these can be installed from the Sketch->Include Library->Manage Libraries... menu. The ffft library needs to be installed the "old school" way, which is described here:
https://learn.adafruit.com/adafruit-all ... -libraries
I don't have any documentation for the ffft library. It's quite vintage now, there's a few notes here:
http://elm-chan.org/works/akilcd/report_e.html
For new projects, you might have a look at the FFT and FHT libraries here:
http://wiki.openmusiclabs.com/wiki/
https://learn.adafruit.com/adafruit-all ... -libraries
I don't have any documentation for the ffft library. It's quite vintage now, there's a few notes here:
http://elm-chan.org/works/akilcd/report_e.html
For new projects, you might have a look at the FFT and FHT libraries here:
http://wiki.openmusiclabs.com/wiki/
- Klogg96
- Posts: 13
- Joined: Wed Sep 23, 2015 5:58 pm
Re: Robot Face for Halloween ...
Ok, Thank you for the links ... I have also found that this example is VERY close to what I want to do.
https://learn.adafruit.com/adafruit-mic ... -levelsurl
So, I received my order yesterday and have soldered the 8x8 bicolor together, installed the arduino IDE, wired up the press button example, and then installed the adafruit libs for the 8x8 including the GFX lib.
Now I am running the bicolor8x8 example and SUCCESS!!!! I did have to GUESS what signal pins to use and assumed that they were the ones illustrated in the above example. I do have a couple questions ...
In trying to understand the example ... Why aren't the com pins specified in the setup routine? are they defaulted to A4 and A5? Am I overlooking some documentation that discusses where they should be? I have no issues with what is working but I am just trying to understand.
Also, in the code (I will say I have never understood c very well, I do scripting with "simpler" languages so please forgive me for my stupidity) there is:
Just so I understand ...
This says declare a single byte static constant variable in FLASH memory (rather than default SRAM). I assume this is done because the information that follows is somewhat large for the SRAM?
So, my interpretation is that each array that follows (and there are three of them):
are 9x8 = 72 bytes each ... that is assuming each character within the array consumes 1 byte as that is the memory that is allocated to each in the declaration? I assume I have this right but that only makes 216 bytes for all three arrays ... this definitely isn't too big for the default SRAM size ... What am I missing?
The rest of the code is 100% clear to me ...
Thanks,
https://learn.adafruit.com/adafruit-mic ... -levelsurl
So, I received my order yesterday and have soldered the 8x8 bicolor together, installed the arduino IDE, wired up the press button example, and then installed the adafruit libs for the 8x8 including the GFX lib.
Now I am running the bicolor8x8 example and SUCCESS!!!! I did have to GUESS what signal pins to use and assumed that they were the ones illustrated in the above example. I do have a couple questions ...
In trying to understand the example ... Why aren't the com pins specified in the setup routine? are they defaulted to A4 and A5? Am I overlooking some documentation that discusses where they should be? I have no issues with what is working but I am just trying to understand.
Also, in the code (I will say I have never understood c very well, I do scripting with "simpler" languages so please forgive me for my stupidity) there is:
Code: Select all
static const uint8_t PROGMEM
smile_bmp[] =
{ B00111100,
B01000010,
B10100101,
B10000001,
B10100101,
B10011001,
B01000010,
B00111100 } ... three more defined after this ...
Code: Select all
static const uint8_t PROGMEM
So, my interpretation is that each array that follows (and there are three of them):
Code: Select all
smile_bmp[] =
{ B00111100,
B01000010,
B10100101,
B10000001,
B10100101,
B10011001,
B01000010,
B00111100 }
The rest of the code is 100% clear to me ...
Thanks,
- pburgess
- Posts: 4161
- Joined: Sun Oct 26, 2008 2:29 am
Re: Robot Face for Halloween ...
Certain hardware capabilities only work on specific pins of the microcontroller. The I2C protocol (handled by the Wire library) is supported on pin A4 and A5. Newer boards have two additional pins labeled SDA and SCL for this purpose, but they're just a secondary connection to A4 and A5 on the Arduino Uno. On other boards like the Leonardo, it's a different pair of pins, so the use of SDA/SCL is actually a bit more modern and recommended. Other hardware-specific functions include SPI (on pins 11/12/13) and PWM (various pins around the board, usually marked with a dot).Why aren't the com pins specified in the setup routine? are they defaulted to A4 and A5?
Correct!This says declare a single byte static constant variable in FLASH memory (rather than default SRAM). I assume this is done because the information that follows is somewhat large for the SRAM?
The 'B' prefix indicates these are binary values -- so each one is just a single byte. For example, B00111100 = 60 decimal. Each array is 8 bytes (corresponding to the 8 rows of the LED matrix).So, my interpretation is that each array that follows [...] are 9x8 = 72 bytes each
The 'B' prefix is also an Arduino-specific anachronism. Newer C compilers (including later versions of the Arduino compiler) can use the '0b' syntax, similar to '0x' for hexadecimal numbers. e.g. 0b00111100 could be used instead.
The bitmaps for this this specific code could fit in RAM, but that would set a bad example. We like it when folks adapt these projects to their own needs...and if that includes adding a whole lot more bitmaps, or doing other things at the same time (like the voice changer project, which uses a ton of RAM), keeping the bitmaps in RAM would become problematic. Since they never change, and retrieving data from flash is relatively fast (usually just a couple extra clock cycles per byte compared to RAM), it's a good place for them.that only makes 216 bytes for all three arrays ... this definitely isn't too big for the default SRAM size
- Klogg96
- Posts: 13
- Joined: Wed Sep 23, 2015 5:58 pm
Re: Robot Face for Halloween ...
Ok, I have started on my own code now (Liar ... Most of it is from Ampli-Tie). I am trying to get the Ampli-Tie code to drive my Bi-Color backpack rather than the NeoPixel Ring. I have verified that it drives my NeoPixels fine. Here is my code:
I am having a problem. The display doesn't seem to do anything at all ... it even behaves like it is ADDING to the display instead of drawing the complete bitmap. I am thinking that there isnt a problem with the code, but that my Mic gain is a little weak. I noticed it was very difficult to use the total range of the neopixel ring unless i cranked my INPUT_CEILING 300 down to 50.
I decided to put the signalMax, signalMin serial outputs into the code ... I get 0, 20 respectively for complete quiet. For me speaking loudly into the mike I get 0, 50. Here are the raw outputs:
Could the gain on my Mic be the problem here? or is it more likely something wrong with the code?
Thanks,
Code: Select all
#include <math.h>
#include <Wire.h> // include I2C on Pins A4, A5 (SDA, SCL)
#include "Adafruit_LEDBackpack.h"
#include "Adafruit_GFX.h"
/* define vairables that setup the matrix */
Adafruit_BicolorMatrix mouth = Adafruit_BicolorMatrix();
static const int nBMP = 33;
static const uint8_t PROGMEM
aLVL[33][8] = {
{ B00000000,
B00000000,
B00000000,
B00000000,
B00000000,
B00000000,
B00000000,
B00000000 },
<omit many of these>
{ B11111111,
B11111111,
B11111111,
B11111111,
B11111111,
B11111111,
B11111111,
B11111111 }};
/* define variables that setup the audio sample and dynamic scale */
#define MIC_PIN A1 // Microphone is attached to this analog pin
#define SAMPLE_WINDOW 10 // Sample window for average level
#define INPUT_FLOOR 10 //Lower range of analogRead input
#define INPUT_CEILING 300 //Max range of analogRead input, the lower the value the more sensitive (1023 = max)
byte peak = 16; // Peak level of column; used for falling dots
unsigned int sample;
void setup() {
Serial.begin(9600);
mouth.begin(0x70);
mouth.writeDisplay();
}
void loop() {
unsigned long startMillis= millis(); // Start of sample window
float peakToPeak = 0; // peak-to-peak level
unsigned int signalMax = 0;
unsigned int signalMin = 1023;
unsigned int c, l, y;
// collect data for length of sample window (in mS)
while (millis() - startMillis < SAMPLE_WINDOW) {
sample = analogRead(MIC_PIN);
if (sample < 1024) { // toss out spurious readings
if (sample > signalMax) {
signalMax = sample; // save just the max levels
} else if (sample < signalMin) {
signalMin = sample; // save just the min levels
}
}
}
peakToPeak = signalMax - signalMin; // max - min = peak-peak amplitude
//Scale the input logarithmically instead of linearly
l = fscale(INPUT_FLOOR, INPUT_CEILING, 0, nBMP, peakToPeak, 2);
Serial.println(signalMin);
Serial.println(signalMax);
mouth.drawBitmap(0, 0, aLVL[l], 8, 8, LED_YELLOW);
mouth.writeDisplay();
}
float fscale( float originalMin, float originalMax, float newBegin, float
newEnd, float inputValue, float curve){
float OriginalRange = 0;
float NewRange = 0;
float zeroRefCurVal = 0;
float normalizedCurVal = 0;
float rangedValue = 0;
boolean invFlag = 0;
// condition curve parameter
// limit range
if (curve > 10) curve = 10;
if (curve < -10) curve = -10;
curve = (curve * -.1) ; // - invert and scale - this seems more intuitive - postive numbers give more weight to high end on output
curve = pow(10, curve); // convert linear scale into lograthimic exponent for other pow function
// Check for out of range inputValues
if (inputValue < originalMin) {
inputValue = originalMin;
}
if (inputValue > originalMax) {
inputValue = originalMax;
}
// Zero Refference the values
OriginalRange = originalMax - originalMin;
if (newEnd > newBegin){
NewRange = newEnd - newBegin;
} else {
NewRange = newBegin - newEnd;
invFlag = 1;
}
zeroRefCurVal = inputValue - originalMin;
normalizedCurVal = zeroRefCurVal / OriginalRange; // normalize to 0 - 1 float
// Check for originalMin > originalMax - the math for all other cases i.e. negative numbers seems to work out fine
if (originalMin > originalMax ) {
return 0;
}
if (invFlag == 0){
rangedValue = (pow(normalizedCurVal, curve) * NewRange) + newBegin;
} else { // invert the ranges
rangedValue = newBegin - (pow(normalizedCurVal, curve) * NewRange);
}
return rangedValue;
}
I decided to put the signalMax, signalMin serial outputs into the code ... I get 0, 20 respectively for complete quiet. For me speaking loudly into the mike I get 0, 50. Here are the raw outputs:
Code: Select all
Loud Speaking:
0
39
0
40
0
34
0
36
0
39
0
38
0
37
2
30
0
29
0
31
0
47
0
43
0
32
0
33
0
36
0
31
0
31
0
30
0
35
0
29
Code: Select all
Silence:
0
24
0
27
0
23
0
23
0
25
0
24
0
25
0
25
0
27
0
25
0
26
0
26
0
27
0
24
0
27
0
23
0
25
Thanks,
- pburgess
- Posts: 4161
- Joined: Sun Oct 26, 2008 2:29 am
Re: Robot Face for Halloween ...
Code: Select all
it even behaves like it is ADDING to the display instead of drawing the complete bitmap
- Klogg96
- Posts: 13
- Joined: Wed Sep 23, 2015 5:58 pm
Re: Robot Face for Halloween ...
Thank you for the reply. This worked perfectly. I am having trouble finding documentation on the NeoPixel and BicolorMatrix libraries ... There is a text file in the download but it doesn't really give syntax or explanation. Do you have a link to the library documentation? The examples have been very helpful so far.
So, right now, I am trying to multi-task my eyes an mouth so I can draw patterns with the eyes and mouth separately. I have read the multitasking three part series and found it informative. I have also adapted the NeoPixel class to my needs. It is attached here:
Important things to note:
you will see in the callback that I had to set the right and left eyes last updates equal to eachother. This ensured they stayed in timing at the end of each cycle.
The code as displayed above works fine ... however, in the blink update and initialization methods, I wanted to use a variable update frequency to make the eyes blink like a lid was closing over the eye rather than constant speed AROUND the eye. The two methods would then look like this:
This caused weirdness ... one eye would stay on a really fast update interval for an extra cycle ... the other would just keep blinking ... Any thoughts? Now, this isnt my main problem.
The mouth I am trying to multitask as well. I tried this (which is a mess):
Which compiles ... but I just get garbage on the display ... almost like it is scrolling memory contents ... Is there something wrong with using Program Memory here?
If I move the Bitmap array out of the class I get a "not defined in context" error ... I tried making it static const ... which I thought would work but no dice ...
I am sorry for my amaturish questions ... c and I are just not friends. I actually thought what I need to do is feed the class a pointer to the portion of the multidimensional array in the lead in function ... but I guess I couldn't figure out multi dimensional pointers ...
Thanks for the support. I am really getting close ...
My entire script ...
So, right now, I am trying to multi-task my eyes an mouth so I can draw patterns with the eyes and mouth separately. I have read the multitasking three part series and found it informative. I have also adapted the NeoPixel class to my needs. It is attached here:
Code: Select all
// NeoPattern Class - derived from the Adafruit_NeoPixel class
class NeoPatterns : public Adafruit_NeoPixel {
public:
// Pattern types supported:
enum pattern { NONE, RAINBOW_CYCLE, THEATER_CHASE, COLOR_WIPE, SCANNER, FADE, BLINK };
// Patern directions supported:
enum direction { FORWARD, REVERSE };
// 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;
case BLINK:
BlinkUpdate();
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) % 4 == 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 {// Fading tail
setPixelColor(i, DimColor(getPixelColor(i)));
}
}
show();
Increment();
}
// Initialize for a BLINK
void Blink(uint32_t color1, uint8_t interval) {
ActivePattern = BLINK;
TotalSteps = (numPixels() / 2);
Color1 = color1;
Index = 0;
Interval = interval;
//Interval = (unsigned long) interval * sin(3.14159 * (Index + 1) / (TotalSteps + 1)); // calc first var. interval
Direction = FORWARD;
}
// Update the Scanner Pattern
void BlinkUpdate() {
for (int i = 0; i < numPixels() ; i++) {
if (i <= Index || i >= TotalSteps * 2 - Index) { // Close Lid
setPixelColor(i, Color(0,0,0));
} else {// Portion of eye open
setPixelColor(i, Color1);
}
}
show();
Increment(); // increase index by one
//Interval = (unsigned long) (Interval / sin(3.14159 * (Index) / (TotalSteps + 1))) // scale interval up
// * sin(3.14159 * (Index + 1) / (TotalSteps + 1));
}
// 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);
}
}
};
- 1) I moved the enumerations into the class
- 2) I added a blink pattern
Code: Select all
// Define the eyes
NeoPatterns eyeL(16, 8, NEO_GRB + NEO_KHZ800, &eyesComplete);
NeoPatterns eyeR(16, 7, NEO_GRB + NEO_KHZ800, NULL);
int blinkRate = 10;
uint8_t defaultUpdate = 100;
int nBlink; // cycles since blink
uint32_t eyeColor = eyeL.Color(15,11,0);
// Initialize everything and prepare to start
void setup()
{
// Initialize all the pixelStrips
eyeL.begin();
eyeR.begin();
// set number of cycles since last blink
nBlink = 0;
//kickoff the standard THEATER_CHASE eyes
eyeL.TheaterChase(eyeColor, eyeL.DimColor(eyeColor), defaultUpdate, eyeL.FORWARD);
eyeR.TheaterChase(eyeColor, eyeL.DimColor(eyeColor), defaultUpdate, eyeR.REVERSE);
}
// Main loop
void loop()
{
// Update the rings.
eyeL.Update();
eyeR.Update();
}
// eye Completion Callback
void eyesComplete(){
nBlink++;
if (nBlink == blinkRate) { // check cycles since last blink
eyeL.Blink(eyeL.DimColor(eyeColor), defaultUpdate/5);
eyeR.Blink(eyeR.DimColor(eyeColor), defaultUpdate/5);
} else if (nBlink == blinkRate + 1) { // if
eyeL.Reverse();
eyeR.Reverse();
nBlink = 0;
eyeR.lastUpdate = eyeL.lastUpdate;
} else { // Back to normal
eyeL.TheaterChase(eyeColor, eyeL.DimColor(eyeColor), defaultUpdate, eyeL.FORWARD);
eyeR.TheaterChase(eyeColor, eyeR.DimColor(eyeColor), defaultUpdate, eyeR.REVERSE);
}
}
The code as displayed above works fine ... however, in the blink update and initialization methods, I wanted to use a variable update frequency to make the eyes blink like a lid was closing over the eye rather than constant speed AROUND the eye. The two methods would then look like this:
Code: Select all
void Blink(uint32_t color1, uint8_t interval) {
ActivePattern = BLINK;
TotalSteps = (numPixels() / 2);
Color1 = color1;
Index = 0;
Interval = (unsigned long) interval * sin(3.14159 * (Index + 1) / (TotalSteps + 1)); // calc first var. interval
Direction = FORWARD;
}
// Update the Scanner Pattern
void BlinkUpdate() {
for (int i = 0; i < numPixels() ; i++) {
if (i <= Index || i >= TotalSteps * 2 - Index) { // Close Lid
setPixelColor(i, Color(0,0,0));
} else {// Portion of eye open
setPixelColor(i, Color1);
}
}
show();
Increment(); // increase index by one
Interval = (unsigned long) (Interval / sin(3.14159 * (Index) / (TotalSteps + 1))) // scale interval up
* sin(3.14159 * (Index + 1) / (TotalSteps + 1));
}
The mouth I am trying to multitask as well. I tried this (which is a mess):
Code: Select all
class LEDMatrix : public Adafruit_BicolorMatrix {
public:
unsigned long Interval; // milliseconds between updates
unsigned long lastUpdate; // last update
int drawBMP = 0;
const int nBMP = 17;
uint8_t PROGMEM
aLVL[17][8] = {
{ B00000000,
B00000000,
B00000000,
B00000000,
B00000000,
B00000000,
B00000000,
B00000000 }, // truncated
{ B11111111,
B11111111,
B11111111,
B11111111,
B11111111,
B11111111,
B11111111,
B11111111 }};
void (*LeadIn)(); // callback reservation
// Constructor - calls base-class constructor to initialize strip
LEDMatrix(unsigned long interval, void (*leadin)()) : Adafruit_BicolorMatrix() {
Interval = interval;
LeadIn = leadin;
}
void Update() {
if((millis() - lastUpdate) > Interval) {
lastUpdate = millis();
if (LeadIn != NULL) {
LeadIn();
}
clear();
drawBitmap(0, 0, aLVL[drawBMP], 8, 8, LED_YELLOW);
writeDisplay();
}
}
};
class Mic {
public:
unsigned int PIN; // store the pin the Mic is using
unsigned int sample;
unsigned int signalMax;
unsigned int signalMin;
// Constructor
Mic(int iPIN) {
PIN = iPIN;
}
void Sample(unsigned int length) {
signalMin = 1023;
signalMax = 0;
unsigned long startMillis = millis();
while (millis() - startMillis < length) {
sample = analogRead(PIN);
if (sample < 1024) { // toss out spurious readings
if (sample > signalMax) {
signalMax = sample; // save just the max levels
} else if (sample < signalMin) {
signalMin = sample; // save just the min levels
}
}
}
}
float peakToPeak() {
return signalMax - signalMin;
}
};
Mic mic(A1);
#define Input_Floor 20
#define Input_Ceiling 100
LEDMatrix mouth(50, &mouthIn);
// Initialize everything and prepare to start
void setup()
{
// Initialize the Bicolor Matrix
mouth.begin(0x70);
mouth.clear();
}
// Main loop
void loop()
{
mouth.Update();
}
// mouth lead in
void mouthIn(){
// sample audio
mic.Sample(10);
unsigned int c;
c = fscale(Input_Floor, Input_Ceiling, 0, mouth.nBMP, mic.peakToPeak(), 2);
mouth.drawBMP = c;
Serial.println(c);
}
If I move the Bitmap array out of the class I get a "not defined in context" error ... I tried making it static const ... which I thought would work but no dice ...
I am sorry for my amaturish questions ... c and I are just not friends. I actually thought what I need to do is feed the class a pointer to the portion of the multidimensional array in the lead in function ... but I guess I couldn't figure out multi dimensional pointers ...
Thanks for the support. I am really getting close ...
My entire script ...
Code: Select all
#include <Adafruit_NeoPixel.h>
#include <Wire.h> // include I2C on Pins A4, A5 (SDA, SCL)
#include "Adafruit_LEDBackpack.h"
#include "Adafruit_GFX.h"
#include <math.h>
// Pattern types supported:
//enum pattern { NONE, RAINBOW_CYCLE, THEATER_CHASE, COLOR_WIPE, SCANNER, FADE, BLINK };
// Patern directions supported:
//enum direction { FORWARD, REVERSE };
// NeoPattern Class - derived from the Adafruit_NeoPixel class
class NeoPatterns : public Adafruit_NeoPixel {
public:
// Pattern types supported:
enum pattern { NONE, RAINBOW_CYCLE, THEATER_CHASE, COLOR_WIPE, SCANNER, FADE, BLINK };
// Patern directions supported:
enum direction { FORWARD, REVERSE };
// 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;
case BLINK:
BlinkUpdate();
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) % 4 == 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 {// Fading tail
setPixelColor(i, DimColor(getPixelColor(i)));
}
}
show();
Increment();
}
// Initialize for a BLINK
void Blink(uint32_t color1, uint8_t interval) {
ActivePattern = BLINK;
TotalSteps = (numPixels() / 2);
Color1 = color1;
Index = 0;
Interval = interval;
//Interval = (unsigned long) interval * sin(3.14159 * (Index + 1) / (TotalSteps + 1)); // calc first var. interval
Direction = FORWARD;
}
// Update the Scanner Pattern
void BlinkUpdate() {
for (int i = 0; i < numPixels() ; i++) {
if (i <= Index || i >= TotalSteps * 2 - Index) { // Close Lid
setPixelColor(i, Color(0,0,0));
} else {// Portion of eye open
setPixelColor(i, Color1);
}
}
show();
Increment(); // increase index by one
//Interval = (unsigned long) (Interval / sin(3.14159 * (Index) / (TotalSteps + 1))) // scale interval up
// * sin(3.14159 * (Index + 1) / (TotalSteps + 1));
}
// 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);
}
}
};
class LEDMatrix : public Adafruit_BicolorMatrix {
public:
unsigned long Interval; // milliseconds between updates
unsigned long lastUpdate; // last update
int drawBMP = 0;
const int nBMP = 17;
uint8_t PROGMEM
aLVL[17][8] = {
{ B00000000,
B00000000,
B00000000,
B00000000,
B00000000,
B00000000,
B00000000,
B00000000 },
{ B00000000,
B00000000,
B00000000,
B00011000,
B00011000,
B00000000,
B00000000,
B00000000 },
{ B00000000,
B00000000,
B00000000,
B00111100,
B00111100,
B00000000,
B00000000,
B00000000 },
{ B00000000,
B00000000,
B00000000,
B01111110,
B01111110,
B00000000,
B00000000,
B00000000 },
{ B00000000,
B00000000,
B00011000,
B01111110,
B01111110,
B00011000,
B00000000,
B00000000 },
{ B00000000,
B00000000,
B00111100,
B01111110,
B01111110,
B00111100,
B00000000,
B00000000 },
{ B00000000,
B00000000,
B01111110,
B01111110,
B01111110,
B01111110,
B00000000,
B00000000 },
{ B00000000,
B00011000,
B01111110,
B01111110,
B01111110,
B01111110,
B00011000,
B00000000 },
{ B00000000,
B00111100,
B01111110,
B01111110,
B01111110,
B01111110,
B00111100,
B00000000 },
{ B00000000,
B00111100,
B01111110,
B11111111,
B11111111,
B01111110,
B00111100,
B00000000 },
{ B00011000,
B00111100,
B01111110,
B11111111,
B11111111,
B01111110,
B00111100,
B00011000 },
{ B00111100,
B00111100,
B01111110,
B11111111,
B11111111,
B01111110,
B00111100,
B00111100 },
{ B00111100,
B01111110,
B01111110,
B11111111,
B11111111,
B01111110,
B01111110,
B00111100 },
{ B01111110,
B01111110,
B01111110,
B11111111,
B11111111,
B01111110,
B01111110,
B01111110 },
{ B01111110,
B01111110,
B11111111,
B11111111,
B11111111,
B11111111,
B01111110,
B01111110 },
{ B01111110,
B11111111,
B11111111,
B11111111,
B11111111,
B11111111,
B11111111,
B01111110 },
{ B11111111,
B11111111,
B11111111,
B11111111,
B11111111,
B11111111,
B11111111,
B11111111 }};
//uint8_t *ActiveBMP[];
void (*LeadIn)(); // callback reservation
// Constructor - calls base-class constructor to initialize strip
LEDMatrix(unsigned long interval, void (*leadin)()) : Adafruit_BicolorMatrix() {
Interval = interval;
LeadIn = leadin;
}
void Update() {
if((millis() - lastUpdate) > Interval) {
lastUpdate = millis();
if (LeadIn != NULL) {
LeadIn();
}
//Serial.println(aLVL[drawBMP]);
clear();
drawBitmap(0, 0, aLVL[drawBMP], 8, 8, LED_YELLOW);
writeDisplay();
}
}
};
class Mic {
public:
unsigned int PIN; // store the pin the Mic is using
unsigned int sample;
unsigned int signalMax;
unsigned int signalMin;
// Constructor
Mic(int iPIN) {
PIN = iPIN;
}
void Sample(unsigned int length) {
signalMin = 1023;
signalMax = 0;
unsigned long startMillis = millis();
while (millis() - startMillis < length) {
sample = analogRead(PIN);
if (sample < 1024) { // toss out spurious readings
if (sample > signalMax) {
signalMax = sample; // save just the max levels
} else if (sample < signalMin) {
signalMin = sample; // save just the min levels
}
}
}
}
float peakToPeak() {
return signalMax - signalMin;
}
};
// Define the eyes
NeoPatterns eyeL(16, 8, NEO_GRB + NEO_KHZ800, &eyesComplete);
NeoPatterns eyeR(16, 7, NEO_GRB + NEO_KHZ800, NULL);
int blinkRate = 10;
uint8_t defaultUpdate = 100;
int nBlink; // cycles since blink
uint32_t eyeColor = eyeL.Color(15,11,0);
Mic mic(A1);
#define Input_Floor 20
#define Input_Ceiling 100
LEDMatrix mouth(50, &mouthIn);
// Initialize everything and prepare to start
void setup()
{
Serial.begin(9600);
// Initialize all the pixelStrips
eyeL.begin();
eyeR.begin();
// set number of cycles since last blink
nBlink = 0;
//kickoff the standard THEATER_CHASE eyes
eyeL.TheaterChase(eyeColor, eyeL.DimColor(eyeColor), defaultUpdate, eyeL.FORWARD);
eyeR.TheaterChase(eyeColor, eyeL.DimColor(eyeColor), defaultUpdate, eyeR.REVERSE);
// Initialize the Bicolor Matrix
mouth.begin(0x70);
mouth.clear();
}
// Main loop
void loop()
{
// Update the rings.
eyeL.Update();
eyeR.Update();
mouth.Update();
}
// eye Completion Callback
void eyesComplete(){
nBlink++;
if (nBlink == blinkRate) { // check cycles since last blink
//eyeL.Fade(eyeColor, eyeL.Color(50,40,0), eyeL.TotalSteps, defaultUpdate/8, FORWARD);
eyeL.Blink(eyeL.DimColor(eyeColor), defaultUpdate/5);
eyeR.Blink(eyeR.DimColor(eyeColor), defaultUpdate/5);
} else if (nBlink == blinkRate + 1) { // if
//eyeL.Fade(eyeL.Color(50,40,0), eyeColor, eyeL.TotalSteps, eyeL.Interval/8, FORWARD);
eyeL.Reverse();
eyeR.Reverse();
nBlink = 0;
eyeR.lastUpdate = eyeL.lastUpdate;
//eyeL.Interval = defaultUpdate;
//eyeR.Interval = defaultUpdate;
} else { // Back to normal
eyeL.TheaterChase(eyeColor, eyeL.DimColor(eyeColor), defaultUpdate, eyeL.FORWARD);
eyeR.TheaterChase(eyeColor, eyeR.DimColor(eyeColor), defaultUpdate, eyeR.REVERSE);
}
}
// mouth lead in
void mouthIn(){
// sample audio
mic.Sample(10);
unsigned int c;
c = fscale(Input_Floor, Input_Ceiling, 0, mouth.nBMP, mic.peakToPeak(), 2);
mouth.drawBMP = c;
Serial.println(c);
}
float fscale( float originalMin, float originalMax, float newBegin, float
newEnd, float inputValue, float curve){
float OriginalRange = 0;
float NewRange = 0;
float zeroRefCurVal = 0;
float normalizedCurVal = 0;
float rangedValue = 0;
boolean invFlag = 0;
// condition curve parameter
// limit range
if (curve > 10) curve = 10;
if (curve < -10) curve = -10;
curve = (curve * -.1) ; // - invert and scale - this seems more intuitive - postive numbers give more weight to high end on output
curve = pow(10, curve); // convert linear scale into lograthimic exponent for other pow function
// Check for out of range inputValues
if (inputValue < originalMin) {
inputValue = originalMin;
}
if (inputValue > originalMax) {
inputValue = originalMax;
}
// Zero Refference the values
OriginalRange = originalMax - originalMin;
if (newEnd > newBegin){
NewRange = newEnd - newBegin;
} else {
NewRange = newBegin - newEnd;
invFlag = 1;
}
zeroRefCurVal = inputValue - originalMin;
normalizedCurVal = zeroRefCurVal / OriginalRange; // normalize to 0 - 1 float
// Check for originalMin > originalMax - the math for all other cases i.e. negative numbers seems to work out fine
if (originalMin > originalMax ) {
return 0;
}
if (invFlag == 0){
rangedValue = (pow(normalizedCurVal, curve) * NewRange) + newBegin;
} else { // invert the ranges
rangedValue = newBegin - (pow(normalizedCurVal, curve) * NewRange);
}
return rangedValue;
}
- pburgess
- Posts: 4161
- Joined: Sun Oct 26, 2008 2:29 am
Re: Robot Face for Halloween ...
Reference for the NeoPixel library is here: https://learn.adafruit.com/adafruit-neo ... no-library
And GFX here: https://learn.adafruit.com/adafruit-gfx ... y/overview
It's a lot of code to sift through...just skimming it though, it's possible that the aLVL array needs to be declared const in addition to PROGMEM.
And GFX here: https://learn.adafruit.com/adafruit-gfx ... y/overview
It's a lot of code to sift through...just skimming it though, it's possible that the aLVL array needs to be declared const in addition to PROGMEM.
- Klogg96
- Posts: 13
- Joined: Wed Sep 23, 2015 5:58 pm
Re: Robot Face for Halloween ...
Thanks, I have actually read those pages, I guess I thought there may be more ...
In terms of my LEDMatrix problem ... I have messed with it all night. To take my incompidence with pointers out of the equation, I simplified my code to the following. I am getting nothing but garbage on the display ... I am quite confused. The garbage is static (as in not dynamic) in this case ... my more complex code was attempting to update the matrix and thus there there was dynamic garbage in the previous example ... the garbage was more strange character looking ...
Here is the code:
Your support has been awesome, sorry about my large code dump earlier...
In terms of my LEDMatrix problem ... I have messed with it all night. To take my incompidence with pointers out of the equation, I simplified my code to the following. I am getting nothing but garbage on the display ... I am quite confused. The garbage is static (as in not dynamic) in this case ... my more complex code was attempting to update the matrix and thus there there was dynamic garbage in the previous example ... the garbage was more strange character looking ...
Here is the code:
Code: Select all
#include <Wire.h> // include I2C on Pins A4, A5 (SDA, SCL)
#include "Adafruit_LEDBackpack.h"
#include "Adafruit_GFX.h"
class LEDMatrix : public Adafruit_BicolorMatrix {
public:
unsigned long Interval; // milliseconds between updates
unsigned long lastUpdate; // last update
uint8_t ActiveBMP[8] =
{ B00000000,B11111111,B00000000,B11111111,B00000000,B11111111,B00000000,B11111111 };
void (*LeadIn)(); // Pre-Update reservation
// Constructor - calls base-class constructor to initialize strip
LEDMatrix(unsigned long interval, void (*leadin)()) : Adafruit_BicolorMatrix() {
Interval = interval;
LeadIn = leadin;
}
void Update() {
if((millis() - lastUpdate) > Interval) {
lastUpdate = millis();
if (LeadIn != NULL) {
LeadIn();
}
clear();
drawBitmap(0, 0, ActiveBMP, 8, 8, LED_YELLOW);
writeDisplay();
}
}
};
LEDMatrix mouth(50, NULL);
// Initialize everything and prepare to start
void setup()
{
// Initialize the Bicolor Matrix
mouth.begin(0x70);
mouth.clear();
}
// Main loop
void loop()
{
mouth.Update();
}
Please be positive and constructive with your questions and comments.