Random acts of…

Here’s a program suggested by Chris. The idea (if I understood it) is something like:

  • Randomly turn a bulb to red, until they are all on
  • Turn them all off
  • Randomly turn a bulb to green, until they are all on
  • Lather, rinse, repeat.

Sounds easy enough and it’s in my GitHub Xmas 2013 thingy as randomActsOfColour.

Let’s take a look at the code:

We start by including the G35String library that does all the clever stuff then define some parameters depending on how many strings, how many lights and which arduino pins are used.

#include <G35String.h>

//#define TWO_STRINGS

#ifdef TWO_STRINGS
  #define LIGHT_COUNT 50
  #define G35_PIN1 9
  #define G35_PIN2 10
  G35String lights1(G35_PIN1, LIGHT_COUNT);
  G35String lights2(G35_PIN2, LIGHT_COUNT);
#else
  #define LIGHT_COUNT 49
  #define G35_PIN 9
  G35String lights(G35_PIN, LIGHT_COUNT);
#endif

Out of habit really, I include this function that fell off the back of an internet (think it was on the Adafruit site) to see how much RAM is available at the start of the sketch. It’s not really needed here but old habits, and all that…

// Simple function to get amount of RAM free
int freeRam () {
  extern int __heap_start, *__brkval; 
  int v; 
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); 
}

Next, we define our colours (note the out of date comment)…

// Colour table for rainbow
const int COLOURS[] = {COLOR_RED, COLOR_GREEN};
const int NCOLOURS = sizeof(COLOURS)/sizeof(int);

We need to know which bulbs are on at any given time. We could store this information in many different ways, however memory is very limited so we have to be efficient. The best we can do is one bit per bulb (it’s either “on” or “off”). We get 8 bits per byte so to cover 50 bulbs we need 50 bits and that means we need 7 bytes (7×8) to have enough room.

// Storage for bitmap of lights 7x8 bits = 56 (we need 50)
#ifdef TWO_STRINGS
byte bulbs1[7];
byte bulbs2[7];
#else
byte bulbs[7];
#endif
const int bulbBytes = 7;

// Macros to work out which bit of which byte to set..
#define GETBIT int bit = bulb % 8
#define GETIND int ind = bulb / 8

The two macros defined above tell us which byte any particular bulb is in (just divide the bulb number by 8) and we get which bit in that byte corresponds to the bulb by taking the remainder on dividing by 8. If it’s not obvious, work through some examples and all will become clear.

Next I define a delay to use after any bulb is turned on and another delay after a string is complete. These can be tweaked to vary the overall speed of the “animation”.

// Delays after each bulb change and after each string is done
#define BETWEEN_BULBS 50
#define AFTER_STRING 1000

We’ll need some “helper functions”. We need a way to mark all the bulbs as “off” which is the case on startup and when we come to turn them off in the animation:

// Clear bitmap
void clearBulbs(byte bulbs[]) {
  for (int i=0; i < bulbBytes; i++) {
    bulbs[i] = 0x00;
  }
}

When we turn a bulb “on” we need to set it’s bit in the array:

void setBulb(byte bulbs[], const int bulb) {
  GETBIT;
  GETIND;
  bulbs[ind] |= 1 << bit;
}

There’s no point turning on a bulb that is already on so we write a function to return True if the bulb is currently off (or unset):

bool bulbUnset(byte bulbs[], const int bulb) {
  GETBIT;
  GETIND;
  return (bulbs[ind] & (1 << bit)) == 0;
}

We will need to know when we have finished with a string – i.e. the number of bulbs that are on matches the number of bulbs in the string. This is a simple (and inefficient) way of bit counting but it should be fast enough for these purposes…

int countBits(byte bulbs[]) {
  int count=0;
  for (int bulb=0; bulb < LIGHT_COUNT; bulb++) {
    GETBIT;
    GETIND;
    if ( bulbs[ind] & (1 << bit) ) count++;
  }
  return count;
}

So, we’re ready to begin. Here’s a fairly simple “setup” which prints the free RAM, enumerates the strings and marks all the bulbs as “off”:

void setup() {
  Serial.begin (115200);
  Serial.println(freeRam());
#ifdef TWO_STRINGS
  lights1.enumerate();
  lights2.enumerate();
  clearBulbs(bulbs1);
  clearBulbs(bulbs2);
#else
  lights.enumerate();
  clearBulbs(bulbs);
#endif

  // Assuming analogue pin 0 is floating then this should
  // give us a different pattern every time, not really but
  // it will do...
  randomSeed(analogRead(0));
}

We need to track which colour we’re dealing with so we declare a global variable to hold the index into the COLOURS array that we defined earlier.

// Which colour to display
int colour = 0;

Then it’s time to put it all together with the main “loop” function. This is cut down for one string here to try and make it simpler to see what is going on:

void loop() {
  #ifdef TWO_STRINGS

  #else
    // Set first colour
    while ( countBits(bulbs) < LIGHT_COUNT ) {
      int bulb = random(LIGHT_COUNT);
      if ( bulbUnset(bulbs, bulb) ) {
        setBulb(bulbs, bulb);
        lights.set_color(bulb, G35::MAX_INTENSITY, COLOURS[colour]);
        delay(BETWEEN_BULBS);
      }
    }
    delay(AFTER_STRING);
    
    // Clear the string
    lights.fill_color(0, LIGHT_COUNT, G35::MAX_INTENSITY, COLOR_BLACK);
    clearBulbs(bulbs);
    
  #endif
}

colour++;
colour = colour % NCOLOURS;

All the above does is to keep choosing a bulb at random until they are all on. If the bulb is off, we mark it as on, set it to the current colour and then delay for BETWEEN_BULBS milliseconds.

Once all bulbs are on, we delay for AFTER_STRING milliseconds before turning and marking them all off then moving on to the next colour.

For the TWO_STRINGS case, we have the following code:

    // Set first colour
    while ( countBits(bulbs1) < LIGHT_COUNT ||
            countBits(bulbs2) < LIGHT_COUNT ) {

      // Set the bulb on first string
      int bulb = random(LIGHT_COUNT);
      if ( bulbUnset(bulbs1, bulb) ) {
        setBulb(bulbs1, bulb);
        lights1.set_color(bulb, G35::MAX_INTENSITY, COLOURS[colour]);
        delay(BETWEEN_BULBS);
      }

      // Set the bulb on second string
      bulb = random(LIGHT_COUNT);
      if ( bulbUnset(bulbs2, bulb) ) {
        setBulb(bulbs2, bulb);
        lights2.set_color(bulb, G35::MAX_INTENSITY, COLOURS[colour]);
        delay(BETWEEN_BULBS);
      }
    }
    delay(AFTER_STRING);
    
    // Clear the strings
    lights1.fill_color(0, LIGHT_COUNT, G35::MAX_INTENSITY, COLOR_BLACK);
    lights2.fill_color(0, LIGHT_COUNT, G35::MAX_INTENSITY, COLOR_BLACK);
    clearBulbs(bulbs1);
    clearBulbs(bulbs2);

For two strings, we loop until both strings are fully on. It’s the same as the single string case really, but we need to operate on each individual string. To have the strings use different colours to each other then change the lights2.set_color call to use COLOURS(NCOLOURS-colour) which will work if there are only 2 colours defined.

That’s it. It’s fairly effective, if not too exciting. As ever, I can’t test on two strings but it’s running fine on one…

This entry was posted in Uncategorized and tagged , , . Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *

*