Saturday 8 December 2012

Raspberry Pi + LedBorg + Vala = Network controllable mood light

Raspberry Pi

So my first real attempt at a project using my new Raspberry Pi has already made me re-discover my love of coding outside of work. It's been far too long since I last pulled an all night-er, coding a pet project for fun; and I'm therefore eternally grateful to everyone involved with the Raspberry Pi project.

Recently I've been learning the Vala language, which is, as programming languages go, very much a new kid on the block. This isn't the place to explain in detail what Vala is, but in short, it's a C# style language that's built on the GLib object system (and as such, uses all the base GNOME libraries and subsystems). The compiler, valac, turns the Vala code into C, and in turn triggers the system's C compiler to produce native code. That sounds messy-er than it is, so it's worth checking out the project's home page at https://live.gnome.org/Vala

One thing I have noticed in the world of the Raspberry Pi, is that in the plethora of programming languages that are pushed, encouraged and provided, Vala is rarely mentioned.
Personally I think this is a shame, and so I'm sharing my first Vala-coded Raspberry Pi project in the hope that it'll help the language gain some momentum on the platform.

LedBorg

The central part of this project is a small, and very cheap add-on board for the Raspberry Pi called the LedBorg. It sits on the Pi's GPIO pins and has an ultra-bright RGB LED. Each channel - red, green and blue, has three intensities: off, half-brightness, and full-brightness. More about LedBorg can be seen at it's website http://www.piborg.org/LedBorg

The driver causes a device entry in the /dev filesystem to be created - /dev/ledborg
The simplest way to control the LedBorg is to echo colour values to this device - for example:
echo "202" > /dev/ledborg

The three digits correspond to R, G and B and can each have a value of 0, 1 or 2, which correspond to the three intensities as above. So, '202' makes the LedBorg light up bright purple (red+blue). '000' is 'black' - basically, off; and '222' is bright white.

Network Control

So after a short time playing around with the different colours, I wondered how easy it would be to control it over the network; and this project was created.

I decided that an easy way to achieve this was to use the well-known HTTP protocol. Using LibSoup in Vala, it's easy to set up a light-weight HTTP server that can respond to requests. I figured this would make it easy to test, as you could use any old web browser on the network to control it, so there was no need to write a client app too - at this stage, at least.

The server takes GET requests in the following URL format:
/?action=SetColour&red=x&green=y&blue=z
Where x, y, and z are integers between 0 and 2.

For the ease of getting started, the program also responds to all requests with a very minimal HTML form containing three drop-down selectors for the three colours, and a submit button.


The Code

To try out this code, you will need to have these packages installed - I'm assuming Raspbian/Debian is the running OS:
valac, libsoup2.4-dev plus any dependencies.

Copy and paste the code below into a new file using your favourite text editor, and save it as
LedBorgSimpleServer.vala

It can then be compiled with the following command:
valac --pkg libsoup-2.4 --pkg gio-2.0 --pkg posix --thread -v LedBorgSimpleServer.vala

I've included the -v flag so that it's more verbose and gives you an idea of what it's doing. If you want to see the C code that it generates, add the -C flag and it'll generate a new file LedBorgSimpleServer.c

So.. it's pretty rough-and-ready, but here's the code..


  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
/* LedBorgSimpleServer.vala
 * 
 * Author:
 *      Ross Taylor <ross@utonium.com>
 */


using GLib;
using Soup;


public class LedBorgSimpleServer : GLib.Object {

 // define the port number to listen on
 static const int LISTEN_PORT = 9999;

 // define the device file to write to
 static const string DEVICE = "/dev/ledborg";



 // main - this is the method that gets executed when the program is run
 public static int main (string[] args) {
  // set up http server
  var server = new Soup.Server(Soup.SERVER_PORT, LISTEN_PORT);
  
  // any requests from the client will be handled by our default_handler method
  server.add_handler("/", default_handler);
  
  // get the http server running and accepting requests
  server.run();

  return 0;
 }


 // default http handler
 public static void default_handler(Soup.Server server, Soup.Message msg, string path, GLib.HashTable<string, string>? query, Soup.ClientContext client)
 {
  // see if we're being asked to action a request
  if(query != null)
  {
   // check the 'action' url parameter just to make sure
   if(query["action"] == "SetColour")
   {
    // get red, green, blue values from url params
    string red = query["red"];
    string green = query["green"];
    string blue = query["blue"];

    // build our colour string
    /*
    The colour string should be three digits long,
    each digit representing red, green and blue respectively.
    
    Each digit's value should be either 0, 1 or 2
    corresponding to 'off', 'half brightness' or 'full brightness'
    */
    string colour = red + green + blue;

    // do colour change
    do_colour_change(colour);
   }
  }


  // build the html to send to the client
  string html = """
   <html>
    <head><title>LedBorgSimpleServer</title></head>
    <body>
     <form method="get" action="/">
      Red:
      <select name="red">
       <option value="0">Off</option>
       <option value="1">1/2 brightness</option>
       <option value="2">Full brightness</option>
      </select>
      Green:
      <select name="green">
       <option value="0">Off</option>
       <option value="1">1/2 brightness</option>
       <option value="2">Full brightness</option>
      </select>
      Blue:
      <select name="blue">
       <option value="0">Off</option>
       <option value="1">1/2 brightness</option>
       <option value="2">Full brightness</option>
      </select>
      <input type="submit" name="action" value="SetColour" />
     </form>
    </body>
   </html> 
  """;

  // send the html back to the client
  msg.set_status_full(Soup.KnownStatusCode.OK, "OK");
  msg.set_response("text/html", Soup.MemoryUse.COPY, html.data);
 }


 // do the colour change
 public static void do_colour_change(string colour)
 {

  // check file exists (if not, then either device or driver not present)
  File file = File.new_for_path(DEVICE);
  if(file.query_exists())
  {
   /*
   Here we use posix file handling to write to the file instead of
   vala's gio file handling, as we don't want the safety of
   GVFS getting in the way when operating in /dev
   */
   // open the file for writing
   Posix.FILE f = Posix.FILE.open(DEVICE, "w");
   
   // write the colour string to the file
   f.puts(colour);
  }
 }

}

2 comments:

  1. Hello Ross;

    Thank you for the article published on Magpi.
    I have a question, is there any way to execute a bash script in the vala.

    I mean in python, if I wrote ./script.sh then the script is executed. How can I do similar thing?
    lets say, based on the value returned by string "colour"

    Looking forward for your reply
    Thanks.

    ReplyDelete
    Replies
    1. Hi. It's not something I've tried yet myself, but there appears to be a number of ways of doing this.

      This post looks like it should help.. http://ubuntuforums.org/showthread.php?t=1223820

      Unfortunately the Vala documentation of the Posix namespace is somewhat bare-bones.

      Delete