8 min read

Download the code files for this project here.

I love lights; specifically, I love LEDs – which have been described to me as “catnip for geeks”. LEDs are low powered but bright, which means they can be embedded into all sorts of interesting places and, when coupled with a network, can be used for all sorts of ambient display purposes.

In this post, I’ll show you how to build an “information radiator” with a bit of Python and some LEDs, which you can then use to make your own for your own personal needs.

Python

// An information radiator light showing the forecast temperature in Melbourne.

An information radiator is so called as it radiates information outwards from (often) a fixed point so that it can be interpreted by an observer. More complex information can be encoded through the use of color, brightness, or frequency of lighting to encode more information.

I’m going to show you how to build an ambient display that scrapes some data from a weather service and then display it using colored light to indicate the forecasted temperature.

This is quite a simple example, but by the end of this two-part post series, you will be able to change your information radiator to consider rain or multiple elements, or even point to something that is important to you.

Bill of materials

Item

Description

Cost

Ethernet Arduino

The Freetronics EtherTen is excellent, but an Arduino Uno with an Ethernet shield works too.

$60

RGB LED

The light discs from DFRobot are great as they produce a lot of light.

$10

Computer

This is needed to run the Python script to check the weather.

 

Wire

Red, green, blue, and white is ideal, but anything you have available is fine.

$2

Light fitting

Anything that diffuses light will be interesting.

$1+

Tools required

These common tools will come in handy:

  • Soldering iron
  • Wire strippers

Design

You don’t want the light attached to the computer all the time – what’s the point of a light if you can just look up the weather on Google? The device will connect to the network and exist somewhere visible, and then the processing can run on a mini server somewhere (such as a Raspberry Pi) and just send the device messages when needed.

So, the system design looks like this:

  • The microcontroller looks after the LED and exposes a network interface.
  • A Python script runs periodically on the server to check the weather forecast, get the data, and then send a message to the Arduino.

Building the light

The build of the light is quite straightforward. Cut four pieces of wire about 6 inches long (personal preference) and solder them to the four connections on the light disk.

 Python

// Light disc with wires soldered on.

Strip 5mm of wire from the other end and wire the light disk to the Arduino in the following way:

  • R to pin 5
  • G to pin 6
  • B to pin 9

Depending on the version of the light disc you have, wire GND to GND or 5V to 5V. The specifics are labelled on the disc itself, and the newer discs are GND.

 Python

// Light disc wired into Arduino.

That’s it! You’re all done electronics-wise.

Plug in an Ethernet cable and ensure you have 7-20V power supplied from a power pack to the Arduino.

Programming the Arduino

If you have never programmed an Arduino before, I suggest this tutorial as an excellent starting point. I’m going to assume you have got the Arduino IDE installed on your computer and you can upload sketches.

First, you need to test your wiring. The following Arduino code will cycle through combinations of colors for about 1 second each. It will print the color to the serial console as well, so you can observe it with the serial monitor:

#define RED 5
#define GREEN 6
#define BLUE 9
 
#define MAX_COLOURS 8
 
#define GND true // change this to false if 5V type
 
char* colours[] = {"Off", "red", "green", "yellow", "blue", "magenta", "cyan", "white"};
uint8_t current_colour = 0;
 
void setup() {
  Serial.begin(9600);
  Serial.println("Testing lights");
  pinMode(RED, OUTPUT);
  pinMode(GREEN, OUTPUT);
  pinMode(BLUE, OUTPUT);
  if (GND) {
    digitalWrite(RED, LOW);
    digitalWrite(GREEN, LOW);
    digitalWrite(BLUE, LOW);
  } else {
    digitalWrite(RED, HIGH);
    digitalWrite(GREEN, HIGH);
    digitalWrite(BLUE, HIGH);
  }
}
 
void loop () {
 
  Serial.print("Current colour: ");
  Serial.println(colours[current_colour]);
    
  if (GND) {
    digitalWrite(RED,   current_colour & 1);
    digitalWrite(GREEN, current_colour & 2);
    digitalWrite(BLUE,  current_colour & 4);
  } else {
    digitalWrite(RED,   !(bool)(current_colour & 1));
    digitalWrite(GREEN, !(bool)(current_colour & 2));
    digitalWrite(BLUE,  !(bool)(current_colour  & 4));
  }
  
  if ((++current_colour) >= MAX_COLOURS) current_colour=0;
  
  delay(1000);  
  
}

Notably, there is a flag to flip (#define GND true | false) depending on whether your light disc uses GND or 5V. All this does is reverse the bit-shifting logic (on the GND disc, the light goes on when the pin goes HIGH, but on the 5V disc, the light goes on when the pin goes LOW).

If the colors are muddled, you have probably just connected a wire to the wrong pin; just flip them over and it should be fine. If you aren’t seeing any light, check your connections and ensure you are getting power to the light disk.

The next thing to do is write the sketch that will take messages from the network and update the light. To do this, we need to establish a protocol. There are many ways to define this, but for simplicity, a text protocol like JSON works sufficiently well.

Each message will look like this:

{r:val, g:val, b:val}

In each case, val  is an unsigned byte, so will be in the range 0-255:

// Adapted from generic web server example as part of IDE created by David Mellis and Tom Igoe.
 
#include "Arduino.h"
#include <Ethernet.h>
#include <SPI.h>
#include <string.h>
#include <stdlib.h>
 
#define DEBUG false
 
// <1>
// Enter a MAC address and IP address for your controller below.
// The IP address will be dependent on your local network:
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xAE };  
byte ip[] = { <PUT YOUR IP HERE AS COMMA BYTES> }; //eg 192,168,0,100
byte gateway[] = { <PUT YOUR GW HERE AS COMMA BYTES}; // eg 192,168,0,1
byte subnet[] = { <PUT YOUR SUBNET HERE>}; //eg 255,255,255,0
 
// Initialize the Ethernet server library
// with the IP address and port you want to use (in this case telnet)
EthernetServer server(23);
 
#define BUFFERLENGTH 255
 
// these are the pins you wire each LED to.
#define RED 5
#define GREEN 6
#define BLUE 9
 
#define GND true // change this to false if 5V type, true if GND type light disc
 
void setup() {
  Ethernet.begin(mac, ip, gateway, subnet);
  server.begin();
    
  #ifdef DEBUG
  Serial.begin(9600);
  Serial.println("Awaiting connection");
  #endif
    
}
 
void loop() {
 
  char buffer[BUFFERLENGTH];
  int index = 0;
  
  // Listen
  EthernetClient client = server.available();
  if (client) {
    #ifdef DEBUG
    Serial.println("Got a client");
    #endif
    
    // reset the input buffer
    index = 0;  
    while (client.connected()) {
      if (client.available()){
        char c = client.read();
        // if it's not a new line, then add it to the buffer <2>
        if (c != 'n' && c != 'r') {
          buffer[index] = c;
          index++;
          
          if (index > BUFFERLENGTH) index = BUFFERLENGTH -1;
          
          continue;
        } else {
          buffer[index] = '';
        }
        
        // get the message string for processing
        String msgstr = String(buffer);
 
        // get just the bits we want between the {}
        msgstr = msgstr.substring(msgstr.lastIndexOf('{')+1, msgstr.indexOf('}', msgstr.lastIndexOf('{')));
        msgstr.replace(" ", "");
        msgstr.replace("'", "");
        #ifdef DEBUG
        Serial.println("Message:");
        Serial.println(msgstr);
        #endif
        
        // rebuild the buffer with just the URL
        msgstr.toCharArray(buffer, BUFFERLENGTH);
        
        
        // iterate over the tokens of the message - assumed flat. <3>
        char *p = buffer;
        char *str;
        while ((str = strtok_r(p, ",", &p)) != NULL) { 
          #ifdef DEBUG
          Serial.println(str);
          #endif
          
          char *tp = str;
          char *key; char *val;
          
          // get the key
          key = strtok_r(tp, ":", &tp);
          val = strtok_r(NULL, ":", &tp);
          
          #ifdef DEBUG
          Serial.print("Key: ");
          Serial.println(key);
          Serial.print("val: ");
          Serial.println(val);
          #endif
 
          // <4>
          if (GND) {          
            if (*key == 'r') analogWrite(RED, atoi(val));
            if (*key == 'g') analogWrite(GREEN, atoi(val));
            if (*key == 'b') analogWrite(BLUE, atoi(val));
          } else {
            if (*key == 'r') analogWrite(RED, 255-atoi(val));
            if (*key == 'g') analogWrite(GREEN, 255-atoi(val));
            if (*key == 'b') analogWrite(BLUE, 255-atoi(val));
          }            
        }
        
        break;
      }     
    }
   
    delay(10); // give client time to send any data back
    client.stop();
  }
}

The most notable parts of the code are as follows:

  • You add your own network settings in here
  • This text parser just adds text to a buffer until a n arrives
  • As this protocol is simple, I use a string tokenizer to break up the message into its constituent pieces as key-value pairs
  • Use the RGB values to set the appropriate level on the PWM pins (noting polarity reversal for GND vs 5V light discs)

To test the code, upload the sketch, ensure your Ethernet cable is plugged in, and attempt to connect to the device:

telnet <ip> 23 

This should return something like the following:

Trying 10.0.1.91...
Connected to 10.0.1.91.
Escape character is '^]'.

Now, enter:

{r:200,g:0, b:0} <enter>

If the light changes to red, then everything is working – time to get some data. If not, check your code and make sure the messages are being interpreted properly (plug in your computer to use the serial debugger to watch the messages).

Play around with changing the colors of your light by sending different values to the device. In the Part 2 post, I’ll explain how to scrape the weather data we want and use that to update the light periodically.

About the Author

Andrew Fisher is a creator (and destroyer) of things that combine mobile web, ubicomp, and lots of data. He is a programmer, interaction researcher, and CTO at JBA, a data consultancy in Melbourne, Australia. He can be found on Twitter at @ajfisher. 

LEAVE A REPLY

Please enter your comment!
Please enter your name here