top of page

COLORCLOCK

What time is it?

Amber o'clock-ish (?)

ColorClock is an RGB LED art installation that challenges conventional time representation through color variation, shedding light on perceptual limitations. Housed in a practical lean-to structure with a bench, it offers daytime shade and evening contemplation. Concentric circles of colored light traverse the spectrum, emphasizing temporal fluidity. Participants can control the central light color changing cycle time, enhancing their engagement and understanding of the piece.



See the code repository here.

IO Expander Analog Write Fails Me

Although the code for the IO expander compiles successfully, it doesn't produce the expected variable output voltage. The analogWrite function accepts values from 0 to 255 (0xFF) and normally setting it to 128 would result in half the output voltage compared to setting it to 255. However, I discovered that the output voltage remained the same whether the pin was set to 128 or 255.


In a previous test, I connected an LED and cycled through output values from 0 to 255, observing the LED brightening. But the differences in output voltage didn't scale as anticipated; there might be a variation up to a certain point, beyond which the voltage levels off.


// pin_num0 and pin_num1 produce the same output voltage
analogWrite(pin_num0, 128);
analogWrite(pin_num1, 255);

The digitalWrite function still works but I haven't the digitalRead yet. If digitalRead works, we might use this IO expander for the user control panel input buttons. But my confidence level in this piece of hardware is low, so we'll see if I actually end up using it.


Now I'm back to using ALL the Arduino PWM output pins to wire up the four LED strips. I'm a bit apprehensive about maxing out the available analog output pins, so I've ordered a new multiplexer, the CD74HC4067, as a backup solution for expanding analog outputs. The new parts have arrived, so I'll be soldering and testing them this weekend... 🤞


The new multiplexor. I'll need to solder on the headers prior to testing.


12V Level Shifter Works!

In this project, an Arduino output pin provides up to 3.3V, insufficient for the 12V needed by the RGB LED strips. So we're using a level shifter to boost the voltage to 12V.


In recent testing, I connected the Arduino's analog output to the level shifter input and the RGB strip to its output, and... it was a success! 🥳 The light cycled through colors as programmed, though transitions were slightly choppy, a topic I'll address later in this post.


The level shifter input pins are connected to the Arduino analog output pins and the level shifter output pins are connected to the RGB LED strips.


Lesson Learned: Don't Use a Member Variable to Define a Global

After successfully testing the level shifter, I quickly integrated the three additional ColorClock objects into the code, assigning analog output pins for each new strip. However, upon connecting more LED strips and running the code, the board appeared to freeze. Subsequent attempts to upload new code failed as the computer no longer detected the Arduino's serial port. Resetting the board and reseating the USB cable eventually restored the serial port connection. Despite this, re-uploading the program continued to cause the board to freeze again 😵‍💫


I barely touched the code! I was worried there was something on the board shorting so it took me about a day of swapping out hardware, doing incremental testing, and meticulously comparing Git commits before I identified the problem.


In the last commit where everything broke, I made one tiny optimization in an effort to reduce the use of 'magic numbers'... In the definition of the time periods for each color clock, I used a static member variable, TimeCalcs::SEC_IN_HR.


float cc0_period =  6.0 / TimeCalcs::SEC_IN_HR;
float cc1_period = 12.0 / TimeCalcs::SEC_IN_HR;
float cc2_period = 30.0 / TimeCalcs::SEC_IN_HR;
float cc3_period = 60.0 / TimeCalcs::SEC_IN_HR;

I should note that the three cc#_period variables used are globals in the main driver file. It seemed like the driver file was not aware of the value of TimeCalcs::SEC_IN_HR by the time it ran that part of the code. When I removed this reference to TimeCalcs::SEC_IN_HR it worked again 😅


float cc0_period =  6.0 / 3600.0;
float cc1_period = 12.0 / 3600.0;
float cc2_period = 30.0 / 3600.0;
float cc3_period = 60.0 / 3600.0;

I'll probably create some #defines at the top of the file to keep it looking a little cleaner.


Smooth Transitions

At first, I thought updating the color output every second would be enough since our original design had a 24-hour cycle time. But once we added a user-controlled light that allow participants to set cycle times as short as six seconds, I realized that smoother color transitions were essential for seamless aesthetics.


I am using a real-time clock module to output the current time, which the program uses to determine the color and the module's smallest granularity is one second. However, Arduino has a built-in function that outputs the current number of milliseconds since the program started, so I added a function to output the current millisecond within each second.


int FluxClock::get_milli(byte crnt_sec)
{
  static byte prev_sec = 0;
  static long int prev_millis = 0;
         long int crnt_millis = millis();

  // Resent millisecond count if its a new second
  if (crnt_sec != prev_sec)
  {
    prev_sec = crnt_sec;
    prev_millis = crnt_millis;
  }

  return static_cast<int>(crnt_millis - prev_millis);
}

The two videos included below demonstate the differences when updating the color output every second versus every millisecond. The three RGB LED strips in the videos cycle through all hues in the color wheel at different times: 6 seconds, 12 seconds, and 30 seconds.


🌈



In this implementation, the program is updating the color output every second. Notice how abrupt the color transitions appear



Here, the colors are updated every millisecond. The transitions in this implementation are much smoother than before.


OS Reinstallation is Always an Option

In the period of time that I started writing this post, I experienced an issue where the Ubuntu partition on my desktop decided not to connect to the internet. I struggled for a few days with all sorts of network settings before I realized it wasn't worth the trouble and just reinstalled Ubuntu in its entirety. As I was going through my whole new environment setup process, I documented most of what I did to streamline the next time I have to start over 😅


User Interface Software Design Begins!

The next step in the ColorClock saga is to design the software implementation for the participant-facing user control panel. I wired up a little prototype for testing. The final design will use arcade buttons embedded in an enclosure that will house the electronics.


In this small prototype I'm using breadboard-sized push buttons and LEDs. The final control panel will use light up triangular arcade buttons for red, green, blue, and frequency modification. I'm using one current-limiting resistor for all the LEDs and 10K pull-down resistors for each push-button.


The soldering work is not beautiful but it gets the job done.

A Beautiful Breadboard

Behold! The first design of the of the fully populated solderless breadboard!

This to-scale-ish design contains all of the modules that will be present on the final circuit board for ColorClock. The user interface controls are not present on this board.


🌈


To streamline the final phase of hardware testing and preempt the challenges of electronic troubleshooting, I found it important to meticulously design the layout of the prototyping breadboard. I started this layout by looking at the datasheets, obtaining the dimensions of each module, and drawing each component to scale. Then I decided it would be actually be more functional if I were using units of breadboard holes (approximately 0.1" between holes) instead of true dimensions. To determine the size represented on the diagram I placed each module on the breadboard and noted how many holes they covered and the positioning of all the pins. The next step is to actually wire this thing up and get to testing with the real LED light display 🙌


Clock Crafting

And on the topic of the light display, it's just about finished! My partner bought a plunge base and circle jig in order to cut the circle and route out pockets for the LED strips to rest in. I'm very pleased with the results 😀 We are using a reflective metallic finish to maximize the light output, and a sheet of translucent acrylic that will sit on top to diffuse the light.


The final routed wood piece features outer rings designed to house RGB LED strips. Each ring will display colors corresponding to the current time in days, hours, and minutes. The center of the piece will have a flower-like shape of LEDs that will be controllable by participant interaction via user control panel.


The LEDs are glued to the inside of the pockets, and the colored light will be reflected off the metallic finish.


IO Expander Integration Trials

In my last post, I was working on integrating the Arduino Nano ESP32 into the project. I successfully tested this board with the new RTC module and the new alphanumeric display 🤗 But then... I was in the process of testing the IO expander and ended up messing with my board configuration so much that I was no longer able to upload any code to the board 😦 Thankfully, ptillisch came to the rescue and resolved my issue 🥳


Though I was able to talk to the Nano board, I was unable to include the library to interface with the IO expander 😭 I chased my tail for a couple of weeks before I decided to move in a different direction (see notes at the end of this post).


The good news was that I could interface the IO expander with the MKR 1000 that I had been testing on before I got the Nano board. Sooooo I ordered two more Arduinos, the MKR 1010 (they no longer make the MKR 1000) and decided to move forward.


Can I Just Get Some Variable Voltage Control??

The plan:


  • Use the IO expander for analog output (required to create the RGB color mixing)

  • Use the Arduino on-board GPIO for digital input from user interface


When the new boards came in, I promptly began testing the IO expander. I was able to get the digitalWrite functions to work with ease, but I struggled with the analogWrite function. After days of banging my head against the wall 😖 my new contingency plan was to swap the IO functionality of the on-board Arduino GPIO and the IO expander, i.e. the IO expander for the digital IO and Arduino PWM for the RGB LED control.


ColorClock has four light strips and three variable voltage lines per strip (one for each red, green and blue) so I need exactly 12 PWM pins. According to the product page for the MKR 1010, there are supposed to be 13 PWM pins on the board BUT one of those listed pins would be unusable for variable voltage output on ColorClock because it needed to be used for the I2C SCL required for communication to the various modules.


This PWM output count would leave me NO wiggle room if one of the pins didn't work as expected. My concern grew when I noticed that the datasheet had conflicting information from the product website on which pins were capable of PWM output (see the notes at the end of this post for more information).


So, to get some clarity, I manually tested each individual pin. I wrote a tiny test program and hooked up a simple circuit with an LED where I varied the output voltage. I observed the LED to see if the light got brighter as the value of the output increased. I discovered more discrepancies between my empirical testing and the documentation (see notes).


We spent the afternoon at a brewery where I performed my variable output voltage test while my partner played his Steam Deck killing zombies or something.


Wiring Matters

The inconsistency between my findings and the documentation left me uneasy, so I went back to testing the IO expander with a minimal example. I knew my code was solid, so I double-checked the circuit wiring and sure enough, there was a mistake. After fixing it, I was able to see a variable output voltage on a multimeter using the analogWrite function using the IO expander 🎉 So now I'm back to the original plan of using the IO expander to control the lights, and the on-board GPIO for the digital inputs.


The input values set by my program didn’t correlate as expected with the measured voltage with the multimeter, so it looks like I’ll need to write some calibration functions so I can programmatically compensate for hardware characteristics.


Moving Forward

So, next steps will be:


  • Wire up the breadboard so beautifully depicted above.

  • Characterize the RGB LED strip, so I can programmatically adjust for the non-linear relationship between the IO expander's output voltage and the visible color from the lights.


We've got 2 1/2 months and counting... 😅



 

Notes

IO Expander Adafruit AW9523

There have been others that have had issues integrating the Adafruit_AW9523 library with the Arduino Nano ESP32. Though the GitHub issues page states that this issue was resolved, I'm not so sure it was...


Arduino MKR 1010 PWM Pins

According to the MKR 1010 product website, the following pins are available for PWM out:

0 .. 8, 10, 12, A3, A4

According to the MKR 1010 datasheet the following pins are available for PWM out:

0 .. 9

According to my empirical testing, the following pins are available for variable voltage output:

0 .. 8, 10, A3, A4, A0* **

* I learned that the A0 pin provides a true analog output, not a PWM signal which is why it was listed on neither the website nor the datasheet as a PWM pin. This page describes the difference between analog and PWM output.


** Pins 9 and 12, as listed on the datasheet and website, respectively, did not produce variable output voltages in my experiment.



bottom of page