RGB-LED Lesson 1 – Extra Credit Solution

Extra Credit (Recap):

See the solutions below!

1. Try experimenting a little with your new led_rgbrandom() function, perhaps by changing the sequence or by increasing the colour value from 3 to 20 and observe the results.

2. As mentioned in the User Manual, we discussed using advanced control of the LEDs to allow us to produce different colours on different LEDs.  The solution is to use high speed switching, to command several LEDs ON and OFF very quickly after another.

Now that we have our new led_time() function, we can easily achieve this in a loop (LED ON period of around 0.001 seconds seems to work nicely).

Therefore, we can recreate the 1st 5 colours of the Rainbow, according to the song we know they are:

Red, and Yellow, and Pink, and Green, Orange…

Our very colourful RGB-LED Rainbow!

Our very colourful RGB-LED Rainbow!

I will let you have a try to do this, if you have any problems then please let me know.

Once you start with this, you may wonder about “Orange” since we haven’t defined RGBLED.RGB_ORANGE in our files.  However, with a little thought, I am sure you can produce Orange too!

Earn a Gold Star*!

If you are feeling real adventurous, you may even be able to get your RGB-LEDs to display the rest of the colours (or a smooth scale of colours) and scroll them across the LEDs.

This would look rather cool – so I will post a video from the first person to who manages it!

*Unfortunately you shall have to provide your own Gold Star (available from most supermarkets).

It looks like I get to post my own video instead!  Although I would still love to see someone else’s attempt (or alternative patterns).

Solutions:

Extra Credit Part 1: The random D.I.C.S.O

 led_rgbrandom() in rgbled.py:

def led_rgbrandom(led,period,colours):
  value = randint(1,colours)
  if value < 8:
    led_time(led,RGB_LIST[value-1],period)

If you experiment with the function should should find that, any it will pick a colour from our RGB_LIST of colours (note the first element in an array is indexed with 0 – in this case RGB_RED, so we take 1 away from the randomly generated number).

The function randint() will give us an index value somewhere between 1 and the value sent to the function “colour“.  For the situation where the colour value is higher than the number of elements in the  RGB_LIST array, we would get an error if we still tried to pick an element which is outside the array.

So to protect against that, we limit looking up any values out of range.  Now we could simply take the colour value and set it to the maximum value (the size of the array) when it is too high, before picking our random number.  However, if we ignore any values which are too high, we add an extra random effect instead (for that period the LED will be switched off), with the higher values making it more likely that an LED will be skipped.

Side Note:

Good Programming Practice – Magic Numbers

When you write code, it is not just about making something work once. A major part of programming is looking back over your code and making improvements, either to do things more efficiently or making it easier for you or others to maintain.

In this case, we can improve the code by making the following adjustment to led_rgbrandom() in rgbled.py:

def led_rgbrandom(led,period,colours):
  value = randint(1,colours)
  if value < len(RGB_LIST):
    led_time(led,RGB_LIST[value-1],period)

We have removed the magic 8, and put a more meaningful len(RGB_LIST) – which will return the actual size of the array (8).

It may seem like a small thing (the result is identical), but it means if you ever decide to change the RGB_LIST, such as add or remove elements (perhaps duplicate colours or add new combinations) then the above function will still work the same (without producing an error just because the array has changed).

It is particularly important in this case, since we are using a random generator as a source, so in theory you could test the code several times and just get lucky, only to find at a later date your small change to RGB_LIST was waiting to fail.  For this test code, it is no real problem, but if this was an important system using real world inputs and output, then one day hitting that bad combination could be very serious.

A rule of thumb here is to avoid “Magic Numbers”, that is fixed numbers which are present in the code which mean something (a GPIO PIN number, the size of an array, a wait period etc).

You should either set them to a constant/variable so that if you use them more than once, there is only one place to change them (at the point where you set them), or like the above example, try to generate them logically (even if they are calculated from a combination of constant values).

Don’t pull numbers “out of a hat” and put them in, since chances are you won’t remember your reasons for selecting them when you return to the code and have to work out what the new “magic number” should be (particularly if they are referring to information which is based in another file).

Extra Credit Part 2: Red, and Yellow, and Pink, and Green, Orange…

This task, appears quite simple until you get to Orange.  There are lots of ways to do this, so don’t worry if your solution is different (it may be better), below is just the solution I went for at the time.

In order to generate RGB_ORANGE, we need to take RGB_YELLOW (which we have already created using [RGB_RED,RGB_GREEN]) and add a little more RGB_RED

i.e. RGB_ORANGE = [RGB_YELLOW, RGB_RED]

Sounds tricky since we already have one lot of RGB_RED being shown so how can we add more?

One solution would be to make the RED part of the LED twice as bright as the GREEN part (by driving it with a different voltage), however the Raspberry Pi can only drive our LEDs with ON or OFF as they are digital outputs.

The alternative is again to make use of high speed switching, and to trick our eyes into seeing more RED than we do GREEN, by switching on the RED part ON for twice as long as the GREEN.  If we do it quick enough, our eyes won’t notice it was Yellow then Red!

This is the code we can use in the main() function of rgbledcontrol.py to provide the full colour sequence we are after:

  # Multiple LEDs with different Colours
  print "Switch on Rainbow"
  for i in range(500):
    RGBLED.led_time(RGBLED.LED1,RGBLED.RGB_RED,0.001)
    RGBLED.led_time(RGBLED.LED2,RGBLED.RGB_YELLOW,0.001)
    RGBLED.led_time(RGBLED.LED3,RGBLED.RGB_MAGENTA,0.001)
    RGBLED.led_time(RGBLED.LED4,RGBLED.RGB_GREEN,0.001)
    RGBLED.led_time(RGBLED.LED5,RGBLED.RGB_YELLOW,0.001)
    RGBLED.led_time(RGBLED.LED5,RGBLED.RGB_RED,0.001)

You can see the result of this at the end of this video:

Gold Star Time: Full Rainbow

Now while the above solution is fine to manually generate individual colours, we now want to generate all the colour combinations we can, and ideally call them just like any other colour i.e. like RGB_RED or RGB_MAGENTA.

Again, this can be done in several ways, but the following solution just extends what we did to produce the dual colours.

Combo Colours

We will add a new function to allow us to make up combination colours, by allowing us to list several RGB combinations to be lit one after another (or as before a single RGB combination). rgbled.py:

#This function will allow single or multiple colours
#to be activated.
def led_combo(pins,colours,period):
  #determine if "colours" is a single integer or not
  if isinstance(colours,int):
    #Single integer - reference directly
    led_time(pins,colours,period)
  else:
    #if not, then cycle through the "colours" list
    for i in colours:
      led_time(pins,i,period)

We can now use this function with the following new colour defines and new RGB_COLOURS list (which can be added in rgbled.py below the other colour defines):

#Combo Colours
RGB_WHITISH1 = [RGB_CYAN,RGB_RED] #whitish (GB+R!)
RGB_AQUA = [RGB_CYAN,RGB_GREEN]
RGB_LBLUE = [RGB_CYAN,RGB_BLUE]
RGB_PINK = [RGB_MAGENTA,RGB_RED]
RGB_WHITISH2 = [RGB_MAGENTA,RGB_GREEN] #whitish (RB+G!)
RGB_PURPLE = [RGB_MAGENTA,RGB_BLUE]
RGB_ORANGE = [RGB_YELLOW,RGB_RED]
RGB_LIME = [RGB_YELLOW,RGB_GREEN]
RGB_WHITISH3 = [RGB_YELLOW,RGB_BLUE] #whitish (RG+B!)

RGB_COLOURS = [RGB_LIME,RGB_YELLOW,RGB_ORANGE,RGB_RED,
               RGB_PINK,RGB_MAGENTA,RGB_PURPLE,RGB_BLUE,
               RGB_LBLUE,RGB_CYAN,RGB_AQUA,RGB_GREEN]

Note:

The combinations which essentially end up with RGB being lit, tend to produce results very near to white, so they have not been included in our list of RGB_COLOURS we will use.  The colours have been ordered in the array to try to make a nice colour pattern.

We can simply call this function to produce the required combination colour i.e. RGB_ORANGE:

  RGBLED.led_combo(RGBLED.LED5,RGBLED.RGB_ORANGE,0.001)

Now we have a much larger range of colours we can use, so we can start cycling them.

Cycling the Full Rainbow

Cycling through the colours, will be much like the loops which we created for the Extra Credit Solution in Lesson 0, with a number of loops within loops.  As before, lets work out what we want to do for each part.

First we will look at the slightly easier option to cycle through the colours with all the LEDs at the same time.

Continue the cycle for as many times as we want:

Repeat the following for the cycle time period (the time we want each colour to be lit up for):

Activate all the LEDs with the same colour from the list of RGB_COLOURS for a short period

Increment the colour counter, to show the next colour on the list, and repeat for the next cycle.

To make the task easier we will create a small utility function to provide us with the next value from the array (while allowing the next value at the end index back to the start).

def next_value(number,max):
  number = number % max
  return number

The above code, just returns a number which ranges from 0 to max-1.

The result of % is the remaining number you get from dividing number by max).

For example: 11 % 12 = 11, 12 % 12 = 0, 13 % 12 = 1,  etc.  This is perfect for stepping through and looping back through arrays, without risking referencing elements out of range.

We can cycle through the colours with the following code:

for k in range(100):
  col_num = next_value(col_num+1,len(RGBLED.RGB_COLOURS))
  for i in range(20): #cycle time
    for j in range(5): #led cycle
      led_num = next_value(j,len(all_leds))
      RGBLED.led_combo(all_leds[led_num],RGBLED.RGB_COLOURS[col_num],0.001)

Next, we would like to produce a smooth scale of colours across the LEDs and then cycle through them.

Continue the cycle for as many times as we want:

Repeat the following for the cycle time period (the duration of each colour on a single LED):

Activate each LED in turn with a specific colour from the list of RGB_COLOURS (starting from the colour counter) for a short period

i.e.

LED1: RGB_LIME

LED2: RGB_YELLOW

LED3:RGB_ORANGE

LED4: RGB_RED

LED5:RGB_PINK

Increment the colour counter, so the colour shown on the first LED is the next one on the list, and repeat for the cycle time.

i.e.

LED1: RGB_YELLOW

LED2:RGB_ORANGE

LED3: RGB_RED

LED4:RGB_PINK

LED5: RGB_MAGENTA

We can cycle the LEDs now with the following code:

  for k in range(100):
    col_num = next_value(col_num+1,len(RGBLED.RGB_COLOURS))
    for i in range(20): #cycle time
      for j in range(len(all_leds)): #led cycle
        led_num = next_value(j,len(all_leds))
        led_colour = next_value(col_num+led_num,len(RGBLED.RGB_COLOURS))
        RGBLED.led_combo(all_leds[led_num],RGBLED.RGB_COLOURS[led_colour],0.001)

So we have the LEDs selected in sequence and the RGB_COLOURS, 0 through to 11 over and over.

Putting the new code into a new file rgbledrainbow.py, we get the following (I’ve put the cycling LEDs first, then the colour cycling):

#!/usr/bin/python

# rgbledrainbow.py
# Test program to show rainbow colours using the RGB-LED kit
# Python Version 2.*

import time
import rgbled as RGBLED

some_leds = [RGBLED.LED1,RGBLED.LED3,RGBLED.LED5]
all_leds = [RGBLED.LED1,RGBLED.LED2,RGBLED.LED3,RGBLED.LED4,RGBLED.LED5]
sequence_leds = [RGBLED.LED1,RGBLED.LED2,RGBLED.LED3,RGBLED.LED4,RGBLED.LED5,RGBLED.LED4,RGBLED.LED3,RGBLED.LED2]

def next_value(number,max):
  number = number % max
  return number

def main():
  print "Setup the RGB module"
  RGBLED.led_setup()

  # Multiple LEDs with different Colours
  print "Switch on Rainbow"
  led_num = 0
  col_num = 0
  for l in range(5):
    print "Cycle LEDs"
    for k in range(100):
      #Set the starting point for the next set of colours
      col_num = next_value(col_num+1,len(RGBLED.RGB_COLOURS))
      for i in range(20): #cycle time
        for j in range(len(all_leds)): #led cycle
          led_num = next_value(j,len(all_leds))
          led_colour = next_value(col_num+led_num,len(RGBLED.RGB_COLOURS))
          RGBLED.led_combo(all_leds[led_num],RGBLED.RGB_COLOURS[led_colour],0.001)

    print "Cycle COLOURs"
    for k in range(100):
      #Set the next colour
      col_num = next_value(col_num+1,len(RGBLED.RGB_COLOURS))
      for i in range(20): #cycle time
        for j in range(len(all_leds)): #led cycle
          led_num = next_value(j,len(all_leds))
          RGBLED.led_combo(all_leds[led_num],RGBLED.RGB_COLOURS[col_num],0.001)

  print "Finished"
  RGBLED.led_cleanup()

main()
#End

If all has gone well, your LED board should be doing something like this:

 

Any questions or problems, then let me know!

Comments
  1. […] RGB-LED Lesson 1 – Extra Credit Solution […]

  2. […] RGB-LED Lesson 1 – Extra Credit Solution […]

  3. Jonathan Grady says:

    Excellent tuorial and i am sure that I will be using this more often. I have tried to run the rainbow script but i have run in the below error message and I am do not know where i have gone wrong. Are you able to assist?

    Traceback (most recent call last):
    File “rgbledrainbow.py”, line 49, in
    main()
    File “rgbledrainbow.py”, line 30, in main
    col_num = next_value(col_num+1,len(RGBLED.RGB_COLOURS))
    AttributeError: ‘module’ object has no attribute ‘RGB_COLOURS’

    • Hi, glad you are enjoying it!
      Make sure you have added the RGB_COLOURS definitions to the rgbled.py file. The error is saying that is doesn’t exist, and should be located in the rgbled.py.
      Hope that helps.

      • flightlessninja says:

        Hi, Did not get round to deleting my earlier post. For some reason the updated rgbled.py did not ftp correctly to the PI. All sorted , blow this opens up a world of possabilities.
        Thanks again
        Jon

      • No problem, glad all is good!
        Enjoy.

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.