14 Jun 2014

IO Expander PCF8574 with Raspberry Pi

With the Raspberry Pi, you can easily run out of IO pins. A good way to solve that problem is to use an IO expander to give your Pi a couple more IO. On Tayda Electronics website, you have two different chips, the 8-bits PCF8574 and the 16-bits MCP23017 from Microchip. These two devices can communicate with the PI using the I2C protocol, it only uses two pins : SDA (data) and SCL (clock). The MCP23017 is a bit more complicated than the other one because you have access to a lot of registers on the device. The PCF8574 does not use register address : you simply read or write from it.

First, you will need to activate the I2C port on your Raspberry Pi. There are several tutorials showing how to do this and I covered a couple links in my article about Adafruit's I2C LED matrix. You also have to find the address of your device. If it's a PCF8574A the address will be different.


The three least significant bits of the 7-bits address are A2, A1, and A0. These three bits are set on the pins of the chip that have the same name. That means that you could have a maximum of 8 PCF8574 connected to your Pi without having address conflict. This means you can have up to 64 more IO. If you use 8 PCF8574 and 8 PCF8574A you can double this number :)

On my breadboard, I wired 8 orange leds to the PCF8574 using 220 Ohms resistors. I plugged to chip on the 5V line to have a little bit more current on the LEDs than on the 3.3V line. A2, A1, and A0 are shorted to ground. The address of the device is then 0x38 (0111000). At first I did not understand it was this address because of the picture above, where we see a 0 next to A0. But A0 is really the least significant bit. An I2C address is always 7-bit.

On the rev2 Raspberry Pi, the I2C port is #1 and on rev1 it is #0. This little detail is important to know when we initialize the I2C device. Once everything is wired up and I2C is enabled, we can issue a detect command :


In the command i2cdetect -y 1 we specify that we are using port #1 for the rev2 Pi. There is also a really useful command we can use :
i2cset -y 1 0xADDRESS 0xDATA

For example, if I want to turn on all the LEDs directly on the command line, I can issue this command :
i2cset -y 1 0x38 0xFF

It's easy as that!!!

Python example

This demo would not be complete without some LED chasing and a demo Python program. In the sample below, LED1 through LED8 are the bit position of each LED in hexadecimal. I used the cycle function from the itertools library to create an infinite array that cycle from LED1 to LED8, and back to LED1, etc... Without pausing between each iteration, the Raspberry Pi would do that sequence really fast, so I'm adding a 100ms delay so we can see what is happening.

from smbus import SMBus
from itertools import cycle
from time import sleep

LED1 = 0x01
LED2 = 0x02
LED3 = 0x04
LED4 = 0x08
LED5 = 0x10
LED6 = 0x20
LED7 = 0x40
LED8 = 0x80

PATTERN = (LED1, LED2, LED3, LED4,
           LED5, LED6, LED7, LED8,
           LED7, LED6, LED5, LED4,
           LED3, LED2)

bus = SMBus(1) # Port 1 used on REV2 

for LED in cycle(PATTERN):
    bus.write_byte(0x38, LED)
    sleep(0.1)

Demo video

1 Jun 2014

Blasting wire tester project

I have been working on a new project for the last weeks. One of my friend is an electrical technician in a gold mine and regularly test blasting wire used to detonate dynamite. Sometimes, the wire is damaged, either open or short-circuited and he have to find where the problem is. He asked me if it was possible to create a simple portable device that continuously make a electrical contact for 1 second and release it for 1 second.

The challenge is quite easy: configure a 555 timer in the astable mode and wire the ouput to a relay coil and power all that with a 9V battery. However, I wanted to have something that looks nice and is robust so it needed a little effort on my part as my project are mostly programming and electronics.

The circuit

This time again I used Diptrace to design the schematic and PCB of my project. Before doing the PCB I tested the functionality of my circuit on a breadboard. The 555 timer is good to work from 5V to 16V and can source 200mA with its output pin, which is more than enough to drive my small relay. 


The circuit is pretty straightforward, I have a switch to connect the 9V battery to the circuit and I use a green "Power Good" LED and a red LED that flashes as the contact relay closes and open. The timing of the 555 is done with the 10uF capacitor (C2) and the 1k and 100k resistors (R4, R3). Below are pictures of the Printed Circuit Board.



The hardware


All the components used in the circuit have been bought on Tayda Electronics website, my favorite part supplier. I bought an aluminum case on Tayda to put the circuit in it and I wanted to have the top of the case look great, so I made a drawing on Inkscape and asked a local store to print a sticker out of it. Below is the original artwork:


I adjusted the DPI and size of the frame in Inkscape so I have a printed drawing the right size (2.4"x4.5"). The two following pictures are the sticker and the aluminum case with the sticker on it.




Once the sticker in place, I used a step drill bit (see picture below) to make holes the right size to put my LED holders and switch. The LED are held in place with a bezel panel mounting clip.





Video demonstration

If you want to hear the relay click and the red LED flash, just take a look at this YouTube video



That's it! I really like the outcome of this little project, this make me wants to do a lot more of good looking prototype put into cases :)

See you soon,
Fred

15 Feb 2014

Raspberry Pi control from mobile device or desktop web browser

In my disco ball hack project, I have been using my mobile phone browser to send commands to my Raspberry Pi. In this post, I will give you a quick overview on how to do the same thing and we will take a look at the code involved. I hooked up a LED and a button to my RPi so we can read the state of the button and toggle the LED. I'm using a self flashing RGB LED that I bought on Ebay, it looks cool :)

There are multiple ways to connect to the Raspberry Pi from another device. It could have been a usb-serial link, bluetooth or even raw socket programming. Also, you could have used other methods  to create the user interface by using native programming on the device :  Android or iPhone app, Visual Studio on the PC... The biggest advantage of using HTTP with a web server on the Pi is that it will work with all devices that have a browser. Also, you only need to know a bit of HTML and JavaScript, you don't need to learn all the other SDK and languages. I think that it's much easier to design a user interface using HTML and adding a bit of CSS for improving the look of the page than learning all the GUI toolkits out there. Using HTML5 and JavaScript is trending right now with all the web development. In Windows 8, apps can be created natively in HTML5 and JavaScript. Some of the new operating systems like Firefox OS and Tizen use HTML for everything. Anyway, let's get started with our application.

For the server side software part, I'm using Python and the web framework Flask. There is a ton of these Python web frameworks (Django is the most well known) but Flask is great because it is really simple and does not need any configuration. For the HTML part, I'm using a bit of JQuery Mobile because it helps making a great looking page in a few line of HTML. There is also another reason to use JQuery: Ajax. (Asynchronous JavaScript and XML)

In a typical webpage without Ajax in which you want to send data to the server, you have to make an HTML form with a submit button. Once you press submit, a POST or GET request is done, your data is transmitted to the server and the page is completely reloaded with new data or you are redirected. With Ajax, you can easily send data to the web server without disrupting the page displayed. It is also possible to request JSON data from the server and modify the DOM (update only certain element of the page).

The simple circuit used

My circuit is an LED connected on P1 (WiringPi pin numbers) in series with a 470 ohm resistor. I also have a button between P0 and GND. I don't need a pull-up on the button because I'm using the internal pull-up of the Raspberry Pi GPIO. The circuit is even simpler like that.

Python code to access the button and LED

This code is fairly simple, I used the standard Raspberry Pi Python module to read/write to the GPIO. I made small utility functions so that later it is easier to read the code in the server Python file. The file name is Pins.py and I will use this as a module in the web server code.

import RPi.GPIO as GPIO

# BCM to WiringPi pin numbers
P0 = 17 # LED pin
P1 = 18 # Button pin

def Init():
    GPIO.setwarnings(False) # suppress GPIO used message
    GPIO.setmode(GPIO.BCM) # use BCM pin numbers
    GPIO.setup(P0, GPIO.OUT) # set LED pin as output
    # set button pin as input
    # also use internal pull-up so we don't need external resistor
    GPIO.setup(P1, GPIO.IN, pull_up_down=GPIO.PUD_UP)

def LEDon():
    GPIO.output(P0, GPIO.HIGH)

def LEDoff():
    GPIO.output(P0, GPIO.LOW)

def SetLED(state):
    if state:
        LEDon()
    else:
        LEDoff()

# the "not" is used to reverse the state of the input
# when pull-up is used, 1 is returned when the button is not pressed
def ReadButton():
    if not GPIO.input(P1):
        return True
    else:
        return False

# if not used as a module (standalone), run this test program 
if __name__ == "__main__":
    Init()
    try:
        while(True):
            SetLED(ReadButton())
    except:
        #clean exit on CTRL-C
        GPIO.cleanup()
        quit()

Web server code

With Flask you can create a working web server in half a page of code. The Index function send the page to the web browser. Notice that the page is rendered only once, the rest is handled with Ajax. The function names that starts with the underscore are the Ajax request. They are called with the GetJSON function with JQuery (see the web page JavaScript in the next section). The _led function does not return JSON, but extract the state parameter from the GET url from the Ajax request to set the state of the LED. The _button function does not have parameter but instead return a JSONified text string corresponding to the button state. As a bonus, when the page is rendered first, I send the uptime of the Raspberry Pi. You can try to type "uptime" in a Unix terminal, you will see a status string displayed. It gives you the time, uptime, # of connected users and load average. I only wanted the uptime so I parsed it.

By the way, to get the Flask module on your Raspbian Python folder, enter the following commands:
sudo apt-get install python-pip
sudo pip install flask

The first command is to install pip (a tool for installing and managing Python packages).

from flask import Flask, render_template, request, jsonify
import Pins

app = Flask(__name__)

# return index page when IP address of RPi is typed in the browser
@app.route("/")
def Index():
    return render_template("index.html", uptime=GetUptime())

# ajax GET call this function to set led state
# depeding on the GET parameter sent
@app.route("/_led")
def _led():
    state = request.args.get('state')
    if state=="on":
        Pins.LEDon()
    else:
        Pins.LEDoff()
    return ""

# ajax GET call this function periodically to read button state
# the state is sent back as json data
@app.route("/_button")
def _button():
    if Pins.ReadButton():
        state = "pressed"
    else:
        state = "not pressed"
    return jsonify(buttonState=state)

def GetUptime():
    # get uptime from the linux terminal command
    from subprocess import check_output
    output = check_output(["uptime"])
    # return only uptime info
    uptime = output[output.find("up"):output.find("user")-5]
    return uptime
    
# run the webserver on standard port 80, requires sudo
if __name__ == "__main__":
    Pins.Init()
    app.run(host='0.0.0.0', port=80, debug=True)

Web page HTML code

In the web page, I import everything needed to get JQuery Mobile working. In the script section, I have two JavaScript functions : the first one detect a change on the slider-switch and send the state of this switch with an Ajax GET to the webserver, the second one is called once each 500 ms to get the button state. The rest of the HTML is the minimal structure of the page.

<!doctype html>
<head>
<meta name="viewport" content="initial-scale=1, maximum-scale=1">
<link rel="stylesheet" href="http://code.jquery.com/mobile/1.4.0/jquery.mobile-1.4.0.min.css" />
<script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
<script src="http://code.jquery.com/mobile/1.4.0/jquery.mobile-1.4.0.min.js"></script>
</head>

<style>
h3, h4 {text-align: center;}
span {font-weight: bold;}
</style>

<script type=text/javascript>
    $(
    // When the LED button is pressed (change)
    // do an ajax request to server to change LED state
    function() 
    {
        $('#flip-1').change(function() 
        {
        $.getJSON('/_led', {state: $('#flip-1').val()});
        });     
    }
    );

    $(
    // periodically (500ms) do an ajax request to get the button state
    // modify the span tag to reflect the state (pressed or not)
    // the state text comes from the JSON string returned by the server
    function button() 
    {
        $.getJSON('/_button', function(data)
            {
                $("#buttonState").text(data.buttonState);
                setTimeout(function(){button();},500);
            });
    }
    );
    

</script>
<!-- Simple JQuery Mobile page that display the button state on the breadoard -->
<!-- You can also change the LED state with the slider switch -->
<!-- The Raspberry Pi uptime is displayed in the footer (Jinja2 expands the template tag) -->

<div data-role="page" data-theme="b">
  <div data-role="header">
    <div><h3>Raspberry Pi Web Control</h3></div>
  </div>

  <div data-role="content">
    <form>
    <p>The button is <span id="buttonState"></span></p>
    <br>
    <select name="flip-1" id="flip-1" data-role="slider" style="float: left;">
        <option value="off">LED off</option>
        <option value="on">LED on</option>
    </select>
    </form>
  </div>
 <div data-role="footer">
    <div><h4>This Raspberry Pi has been {{uptime}}</h4></div>
  </div>
</div>

In action : demo video on Youtube





Here is a demo of the server running and being controlled by my mobile phone. On my main computer, I connect to the Raspberry Pi with SSH. I then start the python application. If you look closely on the computer screen around 0:15, you will see a lot of requests being made to the server. This is due to the 500ms timer sending the Ajax request to read the status of the button.



I really hope that this code can help you in your projects. When I started doing electronics stuff in 2007, I was dreaming about doing something like this. At that time, we did not have smart phones and low cost Linux single board computer. Things changed a lot since!

11 Feb 2014

Hacking a Disco Ball - Part 2

In my last blog post, I gave you an overview of the Disco Ball project. A couple weeks ago I received the printed circuit boards from OSH Park. So here are a couple of pictures of the boards and assembly :





After soldering the circuit, I was really relieved that everything worked perfectly. The first thing I did was to test if I was able to identify the Propeller chip with the programming software and load a program in the EEPROM memory. Once done, I soldered the 3 MOSFET LED drivers and fired a 3.3V signal on their gates. The LEDs of the disco ball turned on as normal. I was a bit stressed with the last part, driving the 120V motor with the Triac. I tested the connections on the breadboard before doing the PCB, but there is always a chance that something is not correct. I closed my eyes when I plugged the 120V in... no explosion or blue fume, phew :)

Software

Originally, I wrote a command interpreter in Python that compile a sequence file and send the compiled sequence to the microcontroller on the PCB. The compiled sequence was stored in the unused part of the boot EEPROM and the main program purpose was to run the loaded sequence. The Python code is shown below :

# -*- coding: utf-8 -*-
"""
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
"""
import sys, re
#Pyserial needed to connect to the printed circuit board
try:
    import serial
except:
    sys.exit(COM_LIBRARY_UNDEFINED)

#This constant value needs to fit with the one in the microcontroller, it won't work otherwise    
MAX_COMMANDS = 1024 
    
#Error codes, ease the debugging process and script interpreting troubleshooting
OK = 0                      #Everything is OK
COM_UNDEFINED = 1           #COM Port undefined on the first line
COM_ERROR = 2               #Unable to connect to the board
SYNTAX_ERROR = 3            #Incorrect command syntax
COLOR_ERROR = 4             #bad color range (0-100)
DELAY_ERROR = 5             #bad delay range
LABEL_NOT_FOUND_ERROR = 6   #unable to find loop label
LABEL_DUPLICATE_ERROR = 10  #loop lable duplicate
SCRIPT_NOT_FOUND = 7        #wrong script path
COM_LIBRARY_UNDEFINED = 8   #pyserial not installed on the machine
ARGS_ERROR = 9              #need path as command line argument

#we need the disco light sequence script path as first argument
try:
    script_path = sys.argv[1]
except:
    sys.exit(ARGS_ERROR)

#read the script and put it in a list    
try:
    script_file = open(script_path)
    raw_script = script_file.readlines()
    script_file.close()
except:
    sys.exit(SCRIPT_NOT_FOUND)

#only keep the important stuff (remove comments)    
def strip(line):
    if line[0] == '[':
        index = line.find(']')
        if index==-1:
            return
        else:
            return line[1:index].lower()

#list comprehension to execute the above function on all the lines
cleaned_script = [strip(line) for line in raw_script if strip(line)]
print "Clean script:\n"+str(cleaned_script)

#find all the labels
labels = {}
index = -1 #Strip the COM Port line
for command in cleaned_script:
    index += 1
    if command[0] == '.':
        labels[command[1:]] = index

#look for duplicates        
unique_labels = tuple(set(labels))
if tuple(labels) != unique_labels:
    sys.exit(LABEL_DUPLICATE_ERROR)

print "Labels:\n"+str(labels)

#regex patterns to parse commands
pat_com = re.compile("(com\d+)")
pat_col = re.compile("([rgb]):(\d+)")
pat_del = re.compile("\$(\d+)")
pat_jmp = re.compile("jmp\.(.+)?(\d+)")
pat_jmpINF = re.compile("jmp\.(.+)")
pat_lbl = re.compile("\.(.+)")

#get the serial COM Port and set the link speed
m = pat_com.match(cleaned_script[0])
if not m:
    sys.exit(COM_UNDEFINED)
else:
    com_port = m.group(1)
    print "Port: "+str(com_port)
try:
    BAUDRATE = 115200
    ser = serial.Serial(com_port,BAUDRATE)
except:
    sys.exit(COM_ERROR)



#The next lines parse all the commands and generate
#   a list of parsed command in an easy to use way
commands = []
for command in cleaned_script[1:]:
    m = pat_col.match(command)
    if m:
        color = m.group(1)
        brightness = m.group(2)
        if int(brightness) > 100:
            print brightness
            sys.exit(COLOR_ERROR)
        commands.append(("COLOR",color,brightness))
        continue
    m = pat_del.match(command)
    if m:
        delay = m.group(1)
        if int(delay) > 100000:
            sys.exit(DELAY_ERROR)
        delay = int(delay)
        b0 = delay >>  0 & 0b11111111
        b1 = delay >>  8 & 0b11111111
        b2 = delay >> 16 & 0b11111111
        b3 = delay >> 24 & 0b11111111
        commands.append(("DELAY",b0,b1,b2,b3))
        continue
    m = pat_jmp.match(command)
    if m:
        label = m.group(1)
        label = label[:-1] # Strip the question mark
        times = m.group(2)
        if not label in labels:
            sys.exit(LABEL_NOT_FOUND_ERROR)
        commands.append(("REPEAT",labels[label],times))
        continue
    m = pat_jmpINF.match(command)
    if m:
        label = m.group(1)
        if not label in labels:
            sys.exit(LABEL_NOT_FOUND_ERROR)
        commands.append(("LOOP FOREVER",labels[label]))
        continue
    m = pat_lbl.match(command)
    if m:
        label = m.group(1)
        commands.append(("LABEL",labels[label]))
        continue
    if command == "stop":
        commands.append(("MOTOR",False))
        continue
    if command == "start":
        commands.append(("MOTOR",True))
        continue
    sys.exit(SYNTAX_ERROR)

print "Commands:\n"+str(commands)

#Patch for the ser.write : we want to send 
#   the decimal number as a byte
commands_sent = 0
def write(num):
    global commands_sent
    commands_sent += 1
    if num != 0:
        pass#print num
    ser.write(chr(int(num)))

#Special read : read the bytes as string and when there is a newline, it marks the end of string
#Return all the strings in a array (tuple) stop reading when null character received chr(0)
def readLineFeed():
    data = []
    buffer = []
    while(True):
        value = ser.read()
        if value == "\n": #LineFeed
            data.append("".join(buffer))
            buffer = [] #Flush the buffer
        elif value == chr(0):
            break
        else:
            buffer.append(value)
    return tuple(data)

def readINF():
    while(True):
        value = ser.read()
        print value
#After the commands are parsed, we send 
#   serial commands to the controller PCB
for command in commands:    
    if command[0] == "COLOR":
        if command[1] == 'r':
            write(1)
            write(command[2]) #brightness
        elif command[1] == 'g':
            write(2)
            write(command[2]) #brightness
        elif command[1] == 'b':
            write(3)
            write(command[2]) #brightness
            
    elif command[0] == "MOTOR":
        if command[1]:
            write(4) #Start
        else:
            write(5) #Stop
            
    elif command[0] == "DELAY":
        write(6)
        write(command[1]) #delay b0
        write(command[2]) #delay b1
        write(command[3]) #delay b2
        write(command[4]) #delay b3
        
    elif command[0] == "LABEL":
        write(7)
        write(command[1])
        
    elif command[0] == "REPEAT":
        write(8)
        write(command[1]) #label
        write(command[2]) #times

    elif command[0] == "LOOP FOREVER":
        write(9)
        write(command[1]) #label

if commands_sent != MAX_COMMANDS:
    print "Before looping: "+str(commands_sent)
    to_max = MAX_COMMANDS-commands_sent
    for index in range(to_max):
        write('0')
    print "After looping: "+str(commands_sent)

   
#Print the data echoed (debug)
for data in readLineFeed():
    print(data)
    
#Everything should be fine if we get there   
ser.close()
print "Script OK!\n"
sys.exit(OK)

In the end, my friend and I decided to control the board with a Raspberry Pi, by using a serial port on each side. The interface is really simple : you have commands to dim the 3 LEDs from 0 to 100% and a command to turn the motor on and off. The reason for the Raspberry Pi is that with an internet connected device, we can easily make a simple webpage to control the board with our mobile phones.

I love Python, so for the web framework on the Pi I decided to use Flask. For the HTML code, I used jQuery Mobile for a good page rendering on mobile devices. There is an excellent jQuery Mobile tutorial on the w3schools website. To make the page interactive, I used a bit of AJAX with jQuery. I really should do a little tutorial about all that in another article. It is really cool to be able to control the IO of the Pi with a webpage! Pictures of the webpage:



Here is a YouTube video showing the Disco Ball in action




Finally, I did not gave too much explanation about the software side of the project (webpage source and microcontroller programs)... I might just be a little bit lazy :) If someone is interested, I can send my source code. Thanks for reading!