Toronto
Offline
Newbie
Karma: 0
Posts: 9
Arduino rocks
|
 |
« on: January 06, 2010, 07:06:00 pm » |
This is my first Arduino (or even electronics) project ever. It uses an Arduino Mega, 65 macetech MegaBrites, an 4x20 LCD display, 4 rotary encoders, a power supply and a lot of wire.
I started working on this back in September and the first version was a balloon blob that we used for a Halloween party. It had 13 MegaBrites which were controlled from a panel. It was a total mess. The whole thing came down halfway through the night. The electrical connections were unstable which forced me to have to fiddle with it a few times - in the dark - pain in the ass. But it helped me figure out where the weaknesses were and I was able to correct them for the wall panel version.
I can't post any links or images of my project until I've made at least one post. Hopefully I can post in my reply to this.
|
|
|
|
|
Logged
|
|
|
|
|
Toronto
Offline
Newbie
Karma: 0
Posts: 9
Arduino rocks
|
 |
« Reply #1 on: January 06, 2010, 07:06:14 pm » |
|
|
|
|
|
Logged
|
|
|
|
|
Toronto
Offline
Newbie
Karma: 0
Posts: 9
Arduino rocks
|
 |
« Reply #2 on: January 06, 2010, 07:10:15 pm » |
I have some questions about performance and this is probably just my inexperience with the platform and with code optimization so hopefully it can be made faster.
1. When I went from 48 lights to 64 lights I saw a huge decrease in performance. From ~ 25ms / loop ~ 45ms / loop. I probably just need to optimize my code but are there better platforms to be working on for this other than arduino? It would be nice to get refreshes down below 20ms/loop.
2. My 4X20 green LCD screen needs to have delays added whenever a refresh is done otherwise it goes all wonky and this slows the whole light panel down when I'm changing parameters in the program because it's updating the LCD screen. I guess the same as question 1 – are there any platforms that are multi-threaded or is there a way to get around this "refresh" wait?
I eventually want to move up to 128 lights but I'm not sure how I'm going to do it with the current performance I'm getting. Is it possible to run arduino's in parallel maybe? I could hook up 2 or 4 and have them work together maybe.
I dunno. Let me know your thoughts.
Thanks, R
|
|
|
|
|
Logged
|
|
|
|
|
Toronto
Offline
Newbie
Karma: 0
Posts: 9
Arduino rocks
|
 |
« Reply #3 on: January 06, 2010, 07:11:42 pm » |
One final thing. This forum and Garrett Mace were an amazing resource for me during the entire development of this project. I couldn't have done it without you guys. Thanks!
|
|
|
|
|
Logged
|
|
|
|
|
SF Bay Area
Offline
Edison Member
Karma: 6
Posts: 1215
Arduino Ninja
|
 |
« Reply #4 on: January 06, 2010, 07:26:33 pm » |
Looks great!
I've been scratching my head over the performance change, and I'll probably ask to see your code at some point. But it strikes me that algorithms don't always scale linearly. For example, maybe moving to 64 lights meant that some of your mathematics got pushed from 8-bit math into 16-bit, or 16-bit into 32-bit, etc. Or you crossed a barrier in how much you can get done between timer interrupts, so it has to put what you were doing on the stack and get back to it later.
If I take a look at some of your code I might have a few ideas to streamline some of the processing. The 80 MegaBrite wall we made seemed to update pretty fast, I remember something like 30ms, though I didn't need that much.
|
|
|
|
|
Logged
|
|
|
|
|
Nowhere
Offline
God Member
Karma: 3
Posts: 851
|-\ |\|\
|
 |
« Reply #5 on: January 07, 2010, 09:52:19 pm » |
Please put the code into code bracket things---the button looks like a # in the post editing page. It looks a lot nicer that way, and you don't have to do a bunch of scrolling.
|
|
|
|
|
Logged
|
|
|
|
|
Toronto
Offline
Newbie
Karma: 0
Posts: 9
Arduino rocks
|
 |
« Reply #6 on: January 10, 2010, 01:12:04 am » |
#include "Functions.h" #include "Color.h" #include "Controls.h" #include "MegaBrite.h" #include "Screen.h" #include "Programs.h" #include "ShiftyVU.h"
MegaBrite megaBrite; ShiftyVU shifty; Programs progs; Screen screen; Controls controls(progs);
int mode = 0; //0 - off, 1 - on, 2 - pulse
double beatSegments[9] = {1.00/16.00,1.00/8.00,1.00/4.00,1.00/2.00,1.00,2.00,4.00,8.00,16.00};
int currentProgram = 0; int effectTime = 0; int millisecondsPerBeat = 60000 / 128; int effectDuration = millisecondsPerBeat; int pulseDuration = millisecondsPerBeat * beatSegments[2]; int currentLED = 0; int color; int fadeArray[NUMLEDS]; bool effecting = false; bool pulsing = false; long refreshDif = 0; long refreshTimer = millis(); long timer; long ms; long palette[PALETTEARRAY]; long plasma[SCREENHEIGHT][SCREENWIDTH]; ColorRGB colorFrom; ColorRGB colorOff = {0, 0, 0};
void setup() { Serial.begin(19200); randomSeed(analogRead(0));
controls.setup();
for(int x = 0; x < NUMLEDS; x++) fadeArray[x] = -1; }
void loop() {
refreshDif = millis() - refreshTimer; refreshTimer = millis();
//check if param positions have changed and update LCD if so controls.checkKnobs();
//check if bpm has changed if(controls.bpmChanged()) millisecondsPerBeat = (int)(60000.00 / controls.bpm);
if(controls.programChanged()) { progs.currentProgram(controls.program); currentProgram = progs.currentProgram();
//generate the palette ColorRGB rgb; ColorHSV hsv; for(int x = 0; x < 256; x++) { ColorHSV hsv = {x, 255, 255}; rgb = HSVtoRGB(hsv); palette[x] = RGBtoInt(rgb); }
//setup for next effect switch (currentProgram) { //Beat case PROGRAM_VU_CYCLE: { color = 0; effectDuration = progs.param1(); break; } //Beat case PROGRAM_VU_FIXED: { color = progs.param1(); break; } //Sine Plasma case PROGRAM_PLASMA_FIXED: case PROGRAM_PLASMA_CYCLE: { color = -1; effectDuration = progs.param2();
//generate the plasma once for(int x = 0; x < SCREENHEIGHT; x++) { for(int y = 0; y < SCREENWIDTH; y++) { long color; //straight line / b & w //color = long(128.0 + (128.0 * sin(y / 8.0))); //plasma like color = long( 128.0 + (128.0 * sin(x / 8.0)) + 128.0 + (128.0 * sin(y / -16.0)) + 128.0 + (128.0 * sin(sqrt(double((x - 128 / 2.0)* (x - 128 / 2.0) + (y - 6 / 2.0) * (y - 6 / 2.0))) / 8.0)) + 128.0 + (128.0 * sin(sqrt(double(x * x + y * y)) / -16.0)) ) / 4; plasma[x][y] = color; } } break; } //Chaser case PROGRAM_CHASER_FIXED: case PROGRAM_CHASER_CYCLE: { effectDuration = millisecondsPerBeat * beatSegments[progs.param2()]; pulseDuration = millisecondsPerBeat * beatSegments[progs.param3()]; break; } //Random Colour case PROGRAM_RANDOM_FIXED: case PROGRAM_RANDOM_CYCLE: case PROGRAM_RANDOM_RAND: { effectDuration = millisecondsPerBeat * beatSegments[progs.param2()]; pulseDuration = millisecondsPerBeat * beatSegments[progs.param3()]; break; } //Blink case PROGRAM_BLINK: { color = progs.param1(); effectDuration = millisecondsPerBeat * beatSegments[progs.param2()]; pulseDuration = millisecondsPerBeat * beatSegments[progs.param3()]; break; } //Test case PROGRAM_TEST_FIXED: case PROGRAM_TEST_CYCLE: { effectDuration = millisecondsPerBeat * beatSegments[progs.param2()]; pulseDuration = millisecondsPerBeat * beatSegments[progs.param3()]; break; } //Nothing - Program Not Found - This shouldn't happen default: { break; } }
pulsing = true;
ms = millis(); }
//check if mode has been switched to 0 (OFF) if(controls.modeChanged()) { mode = controls.mode(); if(mode==0) { effecting = true; effectDuration = (millisecondsPerBeat) * beatSegments[4]; for (int i = 0; i < NUMLEDS; i++) { megaBrite.ledChannels(i, COLORFROM, megaBrite.ledChannels(i, COLORCURRENT)); } } }
|
|
|
|
|
Logged
|
|
|
|
|
Toronto
Offline
Newbie
Karma: 0
Posts: 9
Arduino rocks
|
 |
« Reply #7 on: January 10, 2010, 01:13:03 am » |
if(mode > 0) { switch (currentProgram) { //Beat case PROGRAM_VU_FIXED: case PROGRAM_VU_CYCLE: { if(currentProgram==PROGRAM_VU_CYCLE) { effectDuration = progs.param1(); color = color + effectDuration; } else if(currentProgram==PROGRAM_VU_FIXED) { color = progs.param1(); } if(color>PALETTEARRAY) { color = 0; }
//holy nuts i hate these strongly typed variables float vu = (float)shifty.adcAvg() / (float)1023; double temp = ((double)vu*(double)100); double vuRatio = temp * temp * temp * temp / 100000000;
colorFrom = InttoRGB(palette[color]); colorFrom.r = int((double)colorFrom.r * vuRatio); colorFrom.g = int((double)colorFrom.g * vuRatio); colorFrom.b = int((double)colorFrom.b * vuRatio);
for (int i = 0; i < NUMLEDS; i++) { megaBrite.ledChannels(i, COLORCURRENT, colorFrom); } break; } //Sine Plasma case PROGRAM_PLASMA_FIXED: case PROGRAM_PLASMA_CYCLE: { if(color != progs.param1() && currentProgram==PROGRAM_PLASMA_FIXED) { color = progs.param1(); //generate the palette ColorRGB rgb; ColorHSV hsv; for(int x = 0; x < 256; x++) { //use HSVtoRGB to vary the Hue of the color through the palette ColorHSV hsv = {color, 255, x}; rgb = HSVtoRGB(hsv); palette[x] = RGBtoInt(rgb); } } effectDuration = progs.param2();
//the parameter to shift the palette varies with time long paletteShift; paletteShift = (long)(millis() / (double)effectDuration);
ColorRGB current;
//draw every pixel again, with the shifted palette color for(int x = 0; x < SCREENHEIGHT; x++) { for(int y = 0; y < SCREENWIDTH; y++) { current = InttoRGB(palette[((long)plasma[x][y] + paletteShift) % 256]); megaBrite.ledChannels(screen.pixel[x][y], COLORCURRENT, current); } } //shows the LED in the panel megaBrite.ledChannels(NUMLEDS - 1, COLORCURRENT, current); break; } //Chaser case PROGRAM_CHASER_CYCLE: case PROGRAM_CHASER_FIXED: { if(currentProgram==PROGRAM_CHASER_CYCLE) { color = color + progs.param1(); if(color>PALETTEARRAY) { color = 0; } } else { color = progs.param1(); }
effectDuration = (millisecondsPerBeat) * beatSegments[progs.param2()]; pulseDuration = (millisecondsPerBeat) * beatSegments[progs.param3()];
timer = millis(); effectTime = timer - ms; if(effectTime>effectDuration) { ms = millis(); currentLED++; if(currentLED>=SCREENWIDTH) currentLED = 0;
for(int x = 0; x < SCREENHEIGHT; x++) { for(int y = 0; y < SCREENWIDTH; y++) { if(y==currentLED) { fadeArray[screen.pixel[x][y]] = 0; megaBrite.ledChannels(screen.pixel[x][y], COLORFROM, InttoRGB(palette[color])); } } } }
for (int i = 0; i < NUMLEDS; i++) { if(fadeArray[i]>-1) { if(fadeArray[i]>pulseDuration) { fadeArray[i] = pulseDuration; } if(fadeArray[i]<=pulseDuration) { ColorRGB current; current.r = quadraticEaseOut((long)(pulseDuration - fadeArray[i]), megaBrite.ledChannels(i, COLORFROM).r, colorOff.r, (long)pulseDuration); current.g = quadraticEaseOut((long)(pulseDuration - fadeArray[i]), megaBrite.ledChannels(i, COLORFROM).g, colorOff.g, (long)pulseDuration); current.b = quadraticEaseOut((long)(pulseDuration - fadeArray[i]), megaBrite.ledChannels(i, COLORFROM).b, colorOff.b, (long)pulseDuration); megaBrite.ledChannels(i, COLORCURRENT, current); } if(fadeArray[i]==pulseDuration) { fadeArray[i] = -1; } else { fadeArray[i] += refreshDif; } } }
break; } //Random Colour case PROGRAM_RANDOM_FIXED: case PROGRAM_RANDOM_CYCLE: case PROGRAM_RANDOM_RAND: { currentLED = progs.param1(); if(currentProgram==PROGRAM_RANDOM_FIXED) { color = currentLED; currentLED = 2; } else if(currentProgram==PROGRAM_RANDOM_CYCLE) { color = color + (currentLED); currentLED = 2; if(color>PALETTEARRAY) { color = 0; } } else { color = random(256); } effectDuration = (millisecondsPerBeat) * beatSegments[progs.param2()]; pulseDuration = (millisecondsPerBeat) * beatSegments[progs.param3()]; timer = millis(); effectTime = timer - ms; if(effectTime>effectDuration) { ms = millis(); for (int i = 0; i < currentLED; i++) { int newRandom = random(NUMLEDS); fadeArray[newRandom] = 0;
megaBrite.ledChannels(newRandom, COLORFROM, InttoRGB(palette[color])); } } for (int i = 0; i < NUMLEDS; i++) { if(fadeArray[i]>-1) { if(fadeArray[i]>pulseDuration) { fadeArray[i] = pulseDuration; } if(fadeArray[i]<=pulseDuration) { ColorRGB current; current.r = quadraticEaseOut((long)(pulseDuration - fadeArray[i]), megaBrite.ledChannels(i, COLORFROM).r, colorOff.r, (long)pulseDuration); current.g = quadraticEaseOut((long)(pulseDuration - fadeArray[i]), megaBrite.ledChannels(i, COLORFROM).g, colorOff.g, (long)pulseDuration); current.b = quadraticEaseOut((long)(pulseDuration - fadeArray[i]), megaBrite.ledChannels(i, COLORFROM).b, colorOff.b, (long)pulseDuration); megaBrite.ledChannels(i, COLORCURRENT, current); } if(fadeArray[i]==pulseDuration) { fadeArray[i] = -1; } else { fadeArray[i] += refreshDif; } } } break; } //Nothing - Program Not Found - This shouldn't happen default: { break; } } } //None/Off else { if(effecting) { timer = millis(); effectTime = timer - ms; if(effectTime>effectDuration) { effecting = false; effectTime = effectDuration; }
for (int i = 0; i < NUMLEDS; i++) { ColorRGB current; current.r = quadraticEaseOut((long)(effectDuration - effectTime), megaBrite.ledChannels(i, COLORFROM).r, 0, (long)pulseDuration); current.g = quadraticEaseOut((long)(effectDuration - effectTime), megaBrite.ledChannels(i, COLORFROM).g, 0, (long)pulseDuration); current.b = quadraticEaseOut((long)(effectDuration - effectTime), megaBrite.ledChannels(i, COLORFROM).b, 0, (long)pulseDuration); megaBrite.ledChannels(i, COLORCURRENT, current); } } }
megaBrite.writeLEDArray(); //delayMicroseconds(50); }
|
|
|
|
|
Logged
|
|
|
|
|
Toronto
Offline
Newbie
Karma: 0
Posts: 9
Arduino rocks
|
 |
« Reply #8 on: January 11, 2010, 02:17:34 pm » |
Here's a full demo of the lights and the current programs: you can download a higher quality version of this video here - http://www.djmikeryan.com/blinker/blinker.mkv - it's 235 MB
|
|
|
|
|
Logged
|
|
|
|
|
0
Offline
Newbie
Karma: 0
Posts: 9
Arduino rocks
|
 |
« Reply #9 on: January 11, 2010, 06:19:54 pm » |
I really like it, I can even think of this as purposes as home lighting (of which I regularly looking around for) Was thinking of fixing something myself, but this is certainly an inspiration, you have day to day lighting and even some party lighting for when you are having a party @ home
|
|
|
|
|
Logged
|
|
|
|
|
Atascadero, CA
Offline
Sr. Member
Karma: 2
Posts: 468
Arduino addict
|
 |
« Reply #10 on: January 11, 2010, 11:40:18 pm » |
Nice work! Man, it looks really professional too.
What are the black objects attached to the front?
|
|
|
|
|
Logged
|
What about elevensies? Luncheon? Afternoon tea? Dinner? Supper?
|
|
|
|
0
Offline
Newbie
Karma: 0
Posts: 18
Arduino rocks
|
 |
« Reply #11 on: July 11, 2010, 09:25:30 am » |
Hi djmikeryan, Good work on this project! I'm putting together a similar project, and would like to ask you a few questions about some of the issues you've probably solved already. I started a thread for my project at... http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1278852236You can find specifics about what I wanna do there, but a few questions for you here... What's the most MegaBrites you've linked together in one chain? I plan to have 170 per chain. Do you think that's possible without latency problems? I plan to show video on my setup, so a good refresh rate is crucial. Would I be able to refresh the whole chain at at least 25fps? Cheers! 
|
|
|
|
|
Logged
|
|
|
|
|
0
Offline
Newbie
Karma: 0
Posts: 13
Arduino rocks
|
 |
« Reply #12 on: August 20, 2010, 05:40:06 am » |
Hi, I love your design and would like to add DMX capability to it. Would you please be so kind to share the complete code with us? Thanks in advanced. Siliconsoul.
|
|
|
|
|
Logged
|
|
|
|
|
Portugal
Offline
God Member
Karma: 5
Posts: 962
|
 |
« Reply #13 on: August 20, 2010, 07:02:16 am » |
You can change some things in the code, things like ints storing very low values, an int takes 16 bits, and doing maths on then take longer than doing maths in a uint8_t that is only 8 bits and can have a max value of 255, see if you can use it instead, and using ints over longs and things like those.
|
|
|
|
|
Logged
|
|
|
|
|
0
Offline
Newbie
Karma: 0
Posts: 13
Arduino rocks
|
 |
« Reply #14 on: August 21, 2010, 07:39:08 pm » |
Changing the values to suite DMX is no problem. I was actually talking about the include part: #include "Functions.h" #include "Color.h" #include "Controls.h" #include "MegaBrite.h" #include "Screen.h" #include "Programs.h" #include "ShiftyVU.h"
|
|
|
|
|
Logged
|
|
|
|
|
|