Aquaticus

Projects for geeks

BeagleBone Web LED

15 Jul 2012
Android screenshot

The goal of this tutorial is to show how to remotely control brightness of the LED using BeagleBone Linux powered board.

BeagleBone will run web service that allows to control LED using simple web application. Thanks to that you do not need to install any extra software. Any device that has web browser installed can be used as remote controller, e.g. smartphone, laptop etc.

The software is written in Python. The example code was tested on the kernel version 3.2.5+ on board rev A5 running Ångström Linux distribution (shipped with the board).

Hardware configuration

BeagleBone can not directly handle any type of LED. The maximum output current from a pin is only 6mA. Standard small LED needs about 20mA, bright LED needs much more, for 3W LED it is 750mA. These values shows that LED driver is required to provide appropriate current to power the LED. There are many types of drivers. The simples is just a transisotr, more advanced can power many power LEDs. When choosing driver for this example, it is important that the driver has PWM input pin. In addition this pin must be compatible with 3.3V as BeagleBone uses 3.3V logic. The maximum power of the driver depends what kind of LED is used. In the referal design it was driver for bright LEDs which can provide 1400mA.

Basic LED driver requirements:

  • PWM pin
  • 3.3V logic (not 5V)
  • Maximum current appropriate for selected LED

Brightness

When LED blinks with the frequency >50Hz human eye see LED as continuous lighting. Any PWM signal of frequency greater than 50 Hz can be used for that purpose. Brightness of LED is proportional to the amount of energy provided to LED. We can regulate brightness changing duty cycle of PWM signal. Greater duty cycle means grater brightness.

Configuration

BeagleBone has 8 PWM channels. To control LED only 1 PWM signal is needed. We use EHRPWM1A channel. The PWM pin is located on P9 pin 14. So, the LED driver’s PWM input must be connected to that pin.

To play with other PWM channels you can look at PWM BeagleBone utility you can look at BeagleBone-tools on GitHub. This Python script allows to control 6 PWM channels.

PWM timer

First PWM timer must be activated. To do that specific values must be written to timer register. In the latest Ångström builds this is done automatically. If you use new distribution you can omit the next part.

First additional Python module must be installed. It provides functions for register manipilation.

opkg install python-mmap

If the installer is unable to find the module try first update module list.

opkg update

Then open a text editor and start writing Python script.

from mmap import mmap
import struct
 
MMAP_OFFSET = 0x44c00000                # base address of registers
MMAP_SIZE   = 0x48ffffff-MMAP_OFFSET    # size of the register memory space
CM_PER_BASE = 0x44e00000 - MMAP_OFFSET
CM_PER_EPWMSS1_CLKCTRL = CM_PER_BASE + 0xcc
 
with open("/dev/mem", "r+b") as f:
    mem = mmap(f.fileno(), MMAP_SIZE, offset=MMAP_OFFSET)
 
def setReg(address, new_value):
    """ Sets 32 bits at given address to given value. """
    mem[address:address+4] = struct.pack("<L", new_value)
 
setReg(CM_PER_EPWMSS1_CLKCTRL, 0x2)

The above code enables MODULEMODE in CM_PER_EPWMSS1_CLKCTRL register which runs PWM clock. (The code is a copy from BeagleBone discussion group)

PWM pin

One pin on the board can have many functions. When you look at the pin description, you see that PWM function EHRPWM1A for pin gpmc_a2 is available in mode 6.

Note that unlike the previous step, from now you can access other PWM functions by file system. For example see what’s inside directory /sys/kernel/debug/omap_mux/gpmc_a2.

First we write high level Python function to set PWM parameters using file system.

def writeDevice(device, value):
    with open(device,"w") as f:
        f.write("%d\n" % int(value))

To set mode 6 for pin P9.14 (gpmc_a2).

writeDevice("/sys/kernel/debug/omap_mux/gpmc_a2", 6)

PWM frequency and duty cycle

There is one rule for frequency setting. Before you set PWM signal frequency, duty cycle must be set to 0.

For test purposes we set frequency to 1Hz. That way the LED will blink once per second. That allows to verify if everything works fine. Later the frequency will be changed to value better suitable for brightness control.

pwmSetDutyPercent(0) #set to 0 before setting frequency
pwmSetFrequency(1) #1Hz 
pwmRun(1) #start PWM

The last line of code runs PWM. Writing 1 starts PWM, 0 stops it. After execution, LED should blink slowly.

Web server

Python provides easy ways to create simple web server. Instead of handling low level TCP transmission we use web framework CherryPy

Web template

We assume that the web application should provide 2 basic functions:

  1. Control LED brightness
  2. Turn on or off LED

There are two parameters sent to server:

  1. LED brightness in percent from 0% to 100%: power
  2. On/Off switch state 1(on) or 0(off): switch

URL to set brightness to 50% is http://localhost:8080/?power=50. To switch LED off http://localhost:8080/?switch=0
For simplicty the app have no any access control or any other advanced functions.

The example template consists 5 buttons to control brightness and On and Off switch. In addition bulb icon indicates On/Off state and simple power bar graph.

<html>
<head>
<title>BeagleBone LED Server</title>
<style>img { border: 0px }</style> <!-- for IE -->
<meta name="viewport" content="width=device-width, initial-scale=1.0"/> <!-- Looks better on smartphones -->
</head>
<body>
<img src="images/bulb_on.png"> <img src="images/level100.png">
<br>
<p>
<a href="?power=0"><img src="images/button0.png"></a>
<a href="?power=20"><img src="images/button20.png"></a>
<a href="?power=40"><img src="images/button40.png"></a>
<a href="?power=60"><img src="images/button60.png"></a>
<a href="?power=80"><img src="images/button80.png"></a>
<a href="?power=100"><img src="images/button100.png"></a>
<p>
<a href="?switch=1"><img src="images/on.png"></a>
<a href="?switch=0"><img src="images/off.png"></a>
</body>
</html>

CherryPy installation

To use CherryPy module, it must be first compiled and installed.

From BeagleBone console type the following commands:

opkg install python-distutils
opkg install  python-compile
wget http://download.cherrypy.org/cherrypy/3.2.2/CherryPy-3.2.2.tar.gz
tar xzf CherryPy-3.2.2.tar.gz
cd CherryPy-3.2.2
python setup.py install

If any of the modules can not be found, first invoke opkg update.

Server code

The entire server code is actually one function:

class ServerLed(object):
    '''Power ratio in percents. From 0 to 100%'''
    led_power=20 #Initial 20% of power
    '''Switch state 1 (on) or 0 (off)'''
    led_switch=1 #Initial LED on
 
    def index(self, power='', switch=''):
        if power:
            self.led_power = ( int(power) / 20 ) * 20
            print "New power %d%%" % self.led_power
 
        if switch:
            self.led_switch = int(switch)
            print "New switch state %d" % self.led_switch
 
        #read HTML template from file
        html = open('led.html','r').read()
 
        #replace level bar graph
        level_icon = "level%d.png" % self.led_power
        html = html.replace('level100.png', level_icon)
 
        #compute duty cycle based on current power ratio and switch status
        if self.led_switch:
            duty = self.led_power
        else:
            duty = 0 #disable
 
        pwmSetDutyPercent( duty ) #set PWM duty cycle
 
        #replace bulb icon if LED is disabled (off)
        if not self.led_switch:
            html = html.replace('bulb_on.png', 'bulb_off.png')
 
        return html
 
    index.exposed = True

Server configuration

The default server works on port 8080, default http port 80 can not be used because BeagleBone already uses it for serving documentation. If you stop this service you can use port 80.

conf = {
        'global' : { 
            'server.socket_host': '0.0.0.0', #interface 0.0.0.0 or board IP
            'server.socket_port': 8080 #server port
        },
 
        '/images': { #images served as static files
            'tools.staticdir.on': True,
            'tools.staticdir.dir': os.path.abspath('images')
        },
 
        '/favicon.ico': {  #favorite icon
            'tools.staticfile.on': True,  
            'tools.staticfile.filename': os.path.abspath("images/bulb.ico")
        }
    }

Run

To run server invoke: python server-led.py and go to http://192.168.1.44:8080 address in your web browser, replace 192.168.1.44 with address of BeagleBone.

On the console you should see something like that:

PWM Running 10000 Hz.
[31/May/2012:16:18:54] ENGINE Listening for SIGHUP.
[31/May/2012:16:18:54] ENGINE Listening for SIGTERM.
[31/May/2012:16:18:54] ENGINE Listening for SIGUSR1.
[31/May/2012:16:18:54] ENGINE Bus STARTING
[31/May/2012:16:18:54] ENGINE Started monitor thread ‘Autoreloader’.
[31/May/2012:16:18:54] ENGINE Started monitor thread ‘_TimeoutMonitor’.
[31/May/2012:16:18:54] ENGINE Serving on 0.0.0.0:8080
[31/May/2012:16:18:54] ENGINE Bus STARTED

Source code

Full source code is available at GitHub
To clone it invoke:

git clone https://github.com/aquaticus/Beaglebone-Web-LED.git

You can also download ZIP archive with all files.

Example

References

Gallery