16. Interface and application programming¶
My lack of programming knowledge (especailly at the beginning of this entire program), has really made many of the weeks difficult when it comes to troubleshooting. The simpler the code, obviously the easier it is to find erros or issues. However, once things start to get more complicated, the issues are harder to figure out. Originally, I was so excited to get the following code working in Processing.
One of the things I was never able to figure out, though is why the little box on the screen (it was supposed to change it’s color to match the read on the sensor) never changed colors. You can see in the images below the original code that it was working - it was able to “sense” and display specific colors.
Processing Code¶
/* For use with the colorview Arduino example sketch
Update the Serial() new call to match your serial port
e.g. COM4, /dev/usbserial, etc!
*/
import processing.serial.*;
import java.awt.datatransfer.*;
import java.awt.Toolkit;
Serial port;
void setup(){
size(200,200);
// remember to replace COM20 with the appropriate serial port on your computer ex Serial.list()[1]
port = new Serial(this, "/dev/cu.usbmodem1401", 9600);
port.clear();
}
String buff = "";
int wRed, wGreen, wBlue, wClear;
String hexColor = "ffffff";
void draw(){
background(wRed,wGreen,wBlue);
// check for serial, and process
while (port.available() > 0) {
serialEvent(port.read());
}
}
void serialEvent(int serial) {
if(serial != '\n') {
buff += char(serial);
} else {
int cRed = buff.indexOf("R");
int cGreen = buff.indexOf("G");
int cBlue = buff.indexOf("B");
//int clear = buff.indexOf("C");
//if(clear >=0){
// String val = buff.substring(clear+3);
// val = val.split("\t")[0];
// wClear = Integer.parseInt(val.trim());
//} else { return; }
if (cRed >= 0) {
String val = buff.substring(cRed + 3);
val = val.split("\\s+")[0]; // split using one or more whitespace characters
try {
wRed = Integer.parseInt(val);
} catch (NumberFormatException e) {
println("Invalid value for red: " + val);
}
} else {
return;
}
if (cGreen >= 0) {
String val = buff.substring(cGreen + 3);
val = val.split("\\s+")[0]; // split using one or more whitespace characters
try {
wGreen = Integer.parseInt(val);
} catch (NumberFormatException e) {
println("Invalid value for green: " + val);
}
} else {
return;
}
if (cBlue >= 0) {
String val = buff.substring(cBlue + 3);
val = val.split("\\s+")[0]; // split using one or more whitespace characters
try {
wBlue = Integer.parseInt(val);
} catch (NumberFormatException e) {
println("Invalid value for blue: " + val);
}
} else {
return;
}
//wRed *= 255; wRed /= wClear;
//wGreen *= 255; wGreen /= wClear;
//wBlue *= 255; wBlue /= wClear;
print("Red: "); print(wRed);
print("\tGrn: "); print(wGreen);
print("\tBlue: "); print(wBlue);
//print("\tClr: "); println(wClear);
hexColor = hex(color(wRed, wGreen, wBlue), 6);
print("\t HEX: ");
print(hexColor);
println();
buff = "";
}
}
In this image, the sensor found red (hex code):
In this image, the sensor found white (hex code):
However, the box on the screen would never change once it was initialized. I guess in my fear of using other methods of understanding, I missed the fact that I could have in fact used sites such as ChatGPT to help me understand the code. Tom Dubick had a conversation with me recently about coding. He said the majority of code out there, is just recycled from what works, and then you adjust it for your specific needs. I knew that part was true, since we searched the internet anytime we had a new board or component we were supposed to use. The part I missed though, is being able to use AI services to actually help me understand why something was or was not working. This has made finishing this week so helpful. I was able to take the code I knew I had working from my original start, and have Gemini explain it to me line-by-line. Once I understood this, I was able to then have it show me how to isolate the parts that weren’t doing what I needed them to do and rewrite the lines.
This method of understanding is completely different and so much more helpful than just searching the internet for code and pasting it in. To me, that was just going through the motions of getting an assignment done. Once I understood what the program lines were trying to do, it made understanding the whole code more exciting!
Armed with the new method, I was able to transform the code within a few hours and now it worked as I intended it do.
By the time I understood all of this and got around to updating my interface week, we were out of the sensors I originally used for my project. So, I came up with the idea to use a similar one. This helped me in two ways: (1) It allowed me to really make sure I understood what I was doing if I could replicate getting another sensor to work and I had done the original; and (2) helped me isolate concerns and issues that I was able to compare to the original sensor.
Since we had several in the lab, I decided to use the Adafruit APDS996 Proximity, Light, RGB and Gesture Sensor. Although it does more than my original sensor, I focused on the RGB color sensor portion. Setting it up was extremely easy using this site from Adafruit. The setup was simple and easy to understand.
Once I had it set up, I used the same code from above to have it sense the color. It worked just as the last sensor did. You could see the color box initialize, but nothing happened as the colors changed.
So, I plugged the code into Gemini and had it explain to me what was going on. (All of the work with Gemini and ChatGPT is in the attached pdf in my files below.) As I was reading through the notes, what I realized is once the box was initialized, I never had it go back and change the color. Issue #1! So, I had it help me compile new code with the adjustment. This time, I still saw the box initialize, but now the readings weren’t making sense on the output. Once again, I asked what was going on. With the new sensor, it explained to me the values coming out of the sensor were not compatible with the way it was reading the results. I needed to have the Arduino parse the numbers coming off the sensor so it would make sense. It provided me with the new code that I had to upload from Arduino to the sensor so that when I plugged it back into Processing, the readings would update.
It absolutely worked! The numbers changed as expected and the box initialized the way it should. Now, back to the issue of the box not changing color. Once I realized the initialization happened, but there wasn’t a “loop” to change the color of the box as the values changed, it was much easier to isolate that issue. After it helped me complile new code again, the entire process worked!
Something interesting I learned when using this new sensor is that there was not that much variation in the colors showing on the screen. The colors in the box were muted and very dull looking. I remembered the sensor I originally used had an LED light that came on the sensor was working. This provided a bright surface for the sensor to really pick up the color of the item. To test this, I used the same items I was currently using, but turned the light on from my iPhone and angled it towards the sticks. Instantly, the colors began to vary greatly. As I moved the sticks closer and further away from the sensor, the variations in the readings reflected this. After learning this, I was very happy I used the original TCS34725 sensor because it is important to have a good light source when testing for color.
Updated Codes¶
This code is for the Arduino to send to the sensor.
#include <Wire.h>
#include <Adafruit_APDS9960.h>
Adafruit_APDS9960 apds;
void setup() {
Serial.begin(9600);
while (!Serial);
if (!apds.begin()) {
Serial.println("Failed to initialize APDS9960!");
while (1);
}
apds.enableColor(true);
}
void loop() {
uint16_t r, g, b, c;
if (apds.colorDataReady()) {
apds.getColorData(&r, &g, &b, &c);
// Normalize color to 0-255 range using clear channel
int red = map(r, 0, c == 0 ? 1 : c, 0, 255);
int green = map(g, 0, c == 0 ? 1 : c, 0, 255);
int blue = map(b, 0, c == 0 ? 1 : c, 0, 255);
Serial.print("R:"); Serial.print(red); Serial.print(" ");
Serial.print("G:"); Serial.print(green); Serial.print(" ");
Serial.print("B:"); Serial.println(blue);
}
delay(100); // Adjust refresh rate if needed
}
Next, you upload this code to Processing. Make sure you change the code to include the correct port or it won’t work correctly!
import processing.serial.*;
Serial port;
String buff = "";
int wRed = 0, wGreen = 0, wBlue = 0;
String hexColor = "ffffff";
void setup() {
size(300, 300);
// Replace this with the correct serial port on your machine
printArray(Serial.list()); // See available ports in console
port = new Serial(this, Serial.list()[0], 9600); // Adjust index if needed
port.clear();
port.bufferUntil('\n'); // Handle one line at a time
}
void draw() {
background(wRed, wGreen, wBlue);
// Draw color swatch and label
fill(255);
textSize(16);
textAlign(CENTER, CENTER);
text("R: " + wRed + " G: " + wGreen + " B: " + wBlue + "\n#" + hexColor.toUpperCase(), width / 2, height - 40);
}
void serialEvent(Serial port) {
String input = port.readStringUntil('\n');
if (input == null) return;
input = input.trim();
if (input.length() == 0) return;
try {
String[] parts = input.split("\\s+");
for (String part : parts) {
if (part.startsWith("R:")) {
wRed = constrain(Integer.parseInt(part.substring(2)), 0, 255);
} else if (part.startsWith("G:")) {
wGreen = constrain(Integer.parseInt(part.substring(2)), 0, 255);
} else if (part.startsWith("B:")) {
wBlue = constrain(Integer.parseInt(part.substring(2)), 0, 255);
}
}
hexColor = hex(color(wRed, wGreen, wBlue), 6);
println("R: " + wRed + "\tG: " + wGreen + "\tB: " + wBlue + "\tHEX: #" + hexColor);
} catch (Exception e) {
println("Error parsing line: " + input);
}
}
Click the link for the file below to download all of the files I used in this week’s assignments. Files