Sorry in advance for the length of my question; I want to provide proper context. I wrote an audio visualization program in Java that (in addition to making "pretty pictures" based off of the amplitudes of different frequencies being sent from the computer) sends a color to an Arduino Uno based off the sound it is "hearing."
I know the algorithm for generating this color is functional (in addition to sending that color to the Arduino, it also changes the color of an item on the DrawingCanvas). When the color is sent to the Arduino (as 3 bytes representing red, green, and blue), though, the physical LEDs don't respond correctly (they become a random color for a few seconds then switch to another random color for a bit – the behavior is hard to describe, but certainly not desired!).
I have been able to send colors to the Arduino using the same client program, but when the audio visualizer sends colors by the same method, this odd behavior occurs. My thought is that, because the audio visualizer sends color profiles dozens of times per second, the Arduino might be getting overloaded with data. Is that possible?
Here are the relevant pieces of code from the client, the audio visualizer, and the Arduino:
Client:
public class Client extends WindowController{
//USB is a class I wrote that extends PApplet. Unless baud rates are the issue, I don't think we need to worry about this.
static USB port = new USB("COM3", 9600);
byte[] code = new byte[LENGTH];
static byte killTerm = -100;
private AudioVisualizer vis;
public void begin() {
resize(1500,900);
for (int k=0;k<LENGTH;k++){
code[k]=killTerm;
}
vis = new AudioVisualizer(10,30,1200,300,canvas);
}
public void onMouseClick(Location point) {
runVis = !runVis;
if(runVis) {
vis.start();
code[0]=-22;
sendCodeUSB(code);
} else
vis.stop();
}
public static void sendCodeUSB(byte[] code) {
for (int k=0; k<code.length; k++) {
port.writeProfile(code[k]);
}
}
AudioVisualizer
public class AudioVisualizer implements Runnable {
Thread thread = null;
DrawingCanvas canvas;
byte[] code = new byte[3];
private int xPos;
private int yPos;
private int width;
private int height;
private int scale;
private Location topLeft;
private Location bottomRight;
private FilledRect background;
private int brightness=0;
private boolean needOff = false;
public AudioVisualizer(int x, int y, int w, int h, DrawingCanvas myCanvas) {
canvas=myCanvas;
xPos=x;
yPos=y;
topLeft = new Location (xPos,yPos);
bottomRight = new Location (xPos+width, yPos+height);
width=w;
height=h;
background = new FilledRect(topLeft,width,height,canvas);
background.setColor(Color.BLACK);
initiateMonitor();
scale = height/30;
}
public void start() {
if (thread == null) {
thread = new Thread(this);
thread.start();
}
}
public void stop() {
thread=null;
}
public void run() {
while (thread != null) {
try {
Thread.sleep(40);
} catch (InterruptedException e) {
}
graphAmps();
}
thread = null;
}
public void graphAmps() {
//there is a whole bunch of stuff here that is unrelated that I removed.
//basically, just know that graphAmps() calls avgColors()
avgColors();
}
public void avgColors(){
//there is stuff here that generates avgRed,avgGreen,and avgBlue (known to be working)
Color c = new Color (avgRed,avgGreen,avgBlue);
background.setColor(c);
//so we don't spam the Arduino more than we need to
if(avgRed>10 || avgGreen>10 || avgBlue>10) {
sendArduinoColor(c);
needOff = true;
} else if (!(avgRed>10 || avgGreen>10 || avgBlue>10) && needOff) {
sendArduinoColor(c);
}
}
public void sendArduinoColor(Color c) {
//takes the color and sets code = an array of 3 bytes with values from 0-100 (100 is the highest brightness I ever want the LEDs to be)
//I am sure this is done correctly so I've left it out.
constrainArduinoColor(c);
//calls the static method from the client and passes in the byte array with the rgb values
Client.sendCodeUSB(code);
}
}
Arduino
#include <EEPROM.h>
int redPin = 11;
int bluePin = 10;
int greenPin = 9;
//declared here for debugging ease of access
int r;
int g;
int b;
//are we going through the Spectrum Cycle loop for the first time?
boolean sclFirstTime = true;
//temporary initialization
boolean waiting = true;
int index = 0;
const int LENGTH = 20;
byte profile[LENGTH];
byte avProfile[3];
byte killTerm = -100;
byte audioVisualizationCode = -22;
void setup() {
//start the serial communication
Serial.begin(9600);
//initialize the profile to all -1 values
clearProfile();
}
void loop() {
//if there is nothing over serial and nothing is in the EPROM, we are waiting
if (!eepromProfileAvailable() && Serial.available()==0){
waiting = true;
} else if (eepromProfileAvailable() && Serial.available()==0) {
//if there is something in EEPROM and nothing over Serial, we aren't waiting and load the EEPROM profile
waiting = false;
loadEEPROMProfile();
//load the EPROM color profile into the array
} else if (Serial.available()>0) {
//if something enters over Serial, we aren't waiting, clear the profile, and load the new profile with the serial info
waiting = false;
clearProfile();
while (Serial.available()<LENGTH) {}
// Wait until there are LENGTH number of Bytes waiting
for(int n=0; n<LENGTH; n++) {
profile[n] = Serial.read(); // place them in the profile.
}
Serial.flush();
//clear out the serial to prep for next time
}
//if we have a profile (either from EEPROM or Serial), save it, check for special codes (if found, apply the effect),
//and if there are no special codes, the profile is a static color profile, and set LEDS to that.
if(!waiting) {
saveEEPROMProfile(); //omitted but working
if (profile[0]==101) {
while (Serial.available()==0)
spectrumCycle(); //omitted but working
} else if (profile[0]==-22) {
audioVis();
} else {
anodeSafteyOut(redPin,profile[0]);
anodeSafteyOut(greenPin,profile[1]);
anodeSafteyOut(bluePin,profile[2]);
}
}
}
//a bunch of preset color effects omitted
//we are using a common Anode LED, so low value is 255 instead of 0
void anodeSafteyOut(int pin, int c) {
if(c>=0) {
c = 255 - c;
//as a safety measure because there are no resistors (yes, I know im breaking an intergalactic law by not using any), make sure we aren't writing something too bright
if (c>=155) {
analogWrite(pin,c);
}
}
}
//wait for 3 bytes to come in, assign them to the avProfile, set the colors to that, do it again
//THIS IS PROBABLY WHERE THE PROBLEM IS
void audioVis() {
while (Serial.available()<3);
for (int k=0;k<3;k++)
avProfile[k]=Serial.read();
anodeSafteyOut(redPin,avProfile[0]);
anodeSafteyOut(greenPin,avProfile[1]);
anodeSafteyOut(bluePin,avProfile[2]);
audioVis();
}
So that's a lot, and I've left a lot out that I didn't think was necessary, though I'm happy to provide the full programs if needed. Thanks for your help!
Here is constrainColorArduino
:
public void constrainArduinoColor(Color c) {
int r = c.getRed();
int b = c.getBlue();
int g = c.getGreen();
int r1 = (int)(r*0.4);
int b1 = (int)(b*0.4);
int g1 = (int)(g*0.4);
byte r2=0;
byte b2=0;
byte g2=0;
for (int k=0; k<r1; k++)
r2++;
for (int k=0; k<b1; k++)
b2++;
for (int k=0; k<g1; k++)
g2++;
System.out.println(r+" "+g+" "+b);
System.out.println(r1+" "+g1+" "+b1);
if (r2>100) r2=100;
if (b2>100) b2=100;
if (g2>100) g2=100;
if (r2<0) r2=0;
if (b2<0) b2=0;
if (g2<0) g2=0;
System.out.println(r2+" "+g2+" "+b2);
code[0]=r2;
code[1]=g2;
code[2]=b2;
}