I've always thought SEO was evil (in the non-theological sense!), but this effort (via @rem) seems well worth supporting. According to my Facebook page my favourite quote is 'It is amazing what you can accomplish if you do not care who gets the credit' - so said Harry Truman. Perhaps amazing is overstating the goal, but maybe it could be paraphrased 'It is amazing what you can make Google do if it is actually helpful and not (directly) self-serving'. I guess we'll find out...
Sunday, 26 September 2010
Sunday, 19 September 2010
Announcing pylibftdi - a minimal Pythonic wrapper for libftdi
[edit 2013-11-25: note that recent versions of pylibftdi have deprecated and then removed the ability to use 'Driver' in this way; replace Driver and BitBangDriver with Device and BitBangDevice in the code below]
Toggling an LED or other device from pin D0
from pylibftdi import BitBangDriver
import time
with BitBangDriver() as bb:
while True:
bb.port ^= 1
time.sleep(0.5)
Sending a string to a serial terminal
from pylibftdi import Driver
with Driver() as drv:
drv.baudrate = 19200
drv.write('Hello World!')
It's been tested on Linux (Ubuntu 10.10) and Mac OS X (10.6), with libftdi 0.17 and 0.18, but doesn't have any intended platform specific requirements other than having the libftdi library installed.
The following goals for this project differentiate it from similar projects:
- Fully supported on Linux and Mac OS X, using Intra2net's open source libftdi driver.
- Simple things are very simple, as the example above shows. The basic functionality is made as simple as possible to use, with properties and context managers used where it makes sense. Most other FTDI Python wrappers are 'just' low level bindings of the underlying API.
- There will be an increasing library of examples showing interaction with various protocols and devices - note this is a goal, not yet an accomplishment, though there is an LCD example there.
For reference, other similar projects include PyUSB, pyftdi, python-ftdi, and ftd2xx. There are probably others...
Monday, 30 August 2010
libftdi on Mac OS X
Update 2011/02/02: In trying to port pylibftdi to Python3, I found that the libraries which get built following the instructions below are 64-bit. All well and good, but the Python3.1 installation on Mac OS X 10.6 is 32-bit only (unlike the Python2.6 install).
While it's possible to get both 32-bit and 64-bit universal libraries, I haven't tried that yet. The solution to built 32-bit libraries was to use the following:
CFLAGS="-arch i386" ./configure && make && sudo make install
(Note I omitted libusb and sudo (for make install) in previous instructions - I've updated below). I also found I needed to do:
export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig/
End of Update
I've not written much about my ftdi projects recently, so here's an update. I've played around with reading values from an encoder and outputting to a RGB LED with PWM, but both of these require a fairly high constant poll rate (or in the case of reading the encoder, ideally interrupt-on-change). The jitter is quite annoying, and decent PWM seems tricky, even when following FTDI's instructions (pdf link) for maxmising the buffer size. There's always going to be a gap when one buffer has finished and the next starts.
On a different note, I'm now mostly using my new Mac mini, so I've spent a few moments getting things to work there. Prerequisites:
- libusb (tested with version 1.0.8)
- pkg-config (tested with version 0.25)
- libusb-compat (test with version 0.1.3)
- libftdi (tested with version 0.18)
./configure && make && sudo make install' in the new directory which tar has created.
On the Python side, there were a couple of issues when I tried to run the old code. First was that the library could not be found at all. This was due to the library extension (.so) being hardcoded. For a cross-platform solution, it seems the best way is to use the following:
from ctypes import *
from ctypes.util import find_library
dll = CDLL(find_library('ftdi'))
Rather than specifying 'libftdi.so', we have stripped off the 'lib' prefix and the .so extension. The simple 'ftdi' string would be used if we were linking this library with gcc using the -l flag. Try 'gcc -lftdi' and it should report only that it can't find _main, not that the library is missing (try gcc -lmadeupname and it should complain about being unable to find 'madeupname')
Once this was done, the program would some times run (especially under pdb, which lead me up the garden path of thinking it was a timing issue...), but other times would cause a segfault. This was tracked down to another hard-coded value - the size of the context structure. The following will report the size of the ftdi_context structure:
echo -e '#includeThis is somewhat fragile, as it will fail if the structure ever gets larger than 255 bytes, but this seems unlikely for the time being. On this Mac this gives 112 bytes; on my Linux boxes it gives 84 - though they are running on the previous (0.17) version of libftdi. There is also the issue that the Mac library is x86_64 (i.e. 64-bit), while the Linux libraries are 32-bit.\nint main(){return sizeof(struct ftdi_context);}' | gcc -xc - && (./a.out; echo $?)
One solution, though not exactly a purist's, is to allocate far more memory than will be needed. It won't slow anything down, as only a pointer to the start of the block is passed around, and probably won't make a difference to the memory consumption as applications will always use whole numbers of pages (4KB minimum) anyway. So for now, an allocation of 1KB seems a good solution.
The result of this is that the changes needed to the earlier code for compatibility with both Mac OS X and Linux are as follows:
...
from ctypes.util import find_library
def ftdi_start():
global ctx, fdll # frown... :P
fdll = CDLL(find_library('libftdi'))
# size of ftdi_context varies
# seen 112 (x86_64, v0.18). 84 (i386, v0.17)
# over-allocate to 1KB.
ctx = create_string_buffer(1024)
fdll.ftdi_init(byref(ctx))
fdll.ftdi_usb_open(byref(ctx), 0x0403, 0x6001)
fdll.ftdi_set_bitmode(byref(ctx), 0xFF, 0x01)
...
Me loves os.name == 'posix' :-)
Tuesday, 17 August 2010
New website - codedstructure.net
I've not got round to putting much on here for a while, but I have been busy, honest! I've finally got my own website after far too many years of not bothering: codedstructure.net.
I'm probably not the first to use scrolling like that, but I wasn't copying anyone and I think it's quite funky, especially the nice text shadows. Me likes the way web development is going, after years of shunning it...
Oh, and while I'm at it, I've got a twitter account, and hope to be churning out iPhone apps in the near future - though Objective C looks horribly like some terrible collision of Java and Smalltalk - in a bad way. Hope I can grow to like it.
Wednesday, 16 June 2010
A Python LCD status display
Anyway, with only 8 IO lines on the UM2xxR boards, we need to use the 4bit interface mode (two extra IO lines are needed beyond the 'data' path - one to act as a 'data-ready' strobe, and the other to select between 'data' and 'commands'). The wiring up is basically DB0-DB3 on the FTDI device going to D4-D7 on the LCD, with DB6 on the FTDI going to the 'RS' (register select) line on the LCD, and DB7 to the 'E' strobe. If that makes no sense, then hopefully the photo makes things slightly clearer...
I've also got an LED backlight for my display, which makes it look a whole lot cooler :-)
I'll present the code in chunks and try to explain it as I go along. First the initialisation and shutdown code, which are fundamentally unchanged from the previous examples, though they are now in functions to make them just a little tidier. (Note there is still no error checking here...)
"""
Write a string (argv[1] if run from command line) to a HD44780
LCD module connected via a FTDI UM232R/245R module
example usage:
# while true;
> do python lcd.py $( awk '{print $1}' /proc/loadavg);
> sleep 5;
> done
"""
from ctypes import *
import time, sys
def ftdi_start():
global ctx, fdll # frown... :P
fdll = CDLL('libftdi.so')
ctx = create_string_buffer(84)
fdll.ftdi_init(byref(ctx))
fdll.ftdi_usb_open(byref(ctx), 0x0403, 0x6001)
fdll.ftdi_set_bitmode(byref(ctx), 0xFF, 0x01)
def ftdi_end():
fdll.ftdi_usb_close(byref(ctx))
fdll.ftdi_deinit(byref(ctx))
The following class is an abstraction of a bus - a collection of one (probably two, technically...) or more electrical lines which should be treated as a single unit. The aim here is to be able to program in a similar style to the embedded programming on a microcontroller, where registers are typically memory mapped and writing to a bus is simply writing into a bitfield. The parameters of this abstraction are the width of the bus (in bits), and the offset from the LSB of the entire port being accessed. It also needs a reference to a driver which allows it to do the reading and writing to the port. By using this as a descriptor, we can define buses within classes representing the various devices we are using; in this case the LCD.
class Bus(object):
"""
This class is a descriptor for a bus of a given width starting
at a given offset (0 = LSB). It needs a driver which does the
actual reading and writing - see FtdiDriver below
"""
def __init__(self, driver, offset, width=1):
self.offset = offset
self.width = width
self._mask = ((1<<width)-1)
self.driver = driver
def __get__(self):
val = self.driver.read()
return (val >> offset) & self._mask
def __set__(self, obj, value):
value = value & self._mask
# in a multi-threaded environment, would
# want to ensure following was locked, eg
# by acquiring a driver lock
val = self.driver.latch()
val &= ~(self._mask << self.offset)
val |= value << self.offset
self.driver.write(val)
The following is the driver which will be used to do the actual data access. Note the use of the latch to store the last value written to the port, which cannot generally be read from the device after having been written (Latch registers for the IO ports was a big advance for the PIC18F series over the earlier 16F series, which needed the application to store this separately in order to do read-modify-write operations properly on the IO ports).
class FtdiDriver(object):
def __init__(self):
self._latch = 0
def read(self):
z = c_byte()
fdll.ftdi_read(byref(ctx), byref(z), 1)
return z.value
def latch(self):
return self._latch
def write(self, val):
self._latch = val
z = c_byte(val)
fdll.ftdi_write_data(byref(ctx), byref(z), 1)
# the following is a hack specifically to allow
# me to ignore all the timing constraints of the
# LCD. For more advanced LCD usage, this wouldn't
# be acceptable...
time.sleep(0.005)
Now we've got a Bus class and a driver to use with it, we can define the LCD module interface. I'm not going to cover the details of the interface, but the wikipedia HD44780 page has some pointers.
# need to instantiate this in global context so LCD
# class can be defined. Could tidy this up...
ftdi_driver = FtdiDriver()
class LCD(object):
"""
The UM232R/245R is wired to the LCD as follows:
DB0..3 to LCD D4..D7 (pin 11..pin 14)
DB6 to LCD 'RS' (pin 4)
DB7 to LCD 'E' (pin 6)
"""
data = Bus(ftdi_driver, 0, 4)
rs = Bus(ftdi_driver, 6)
e = Bus(ftdi_driver, 7)
def init_four_bit(self):
"""
set the LCD's 4 bit mode, since we only have
8 data lines and need at least 2 to strobe
data into the module and select between data
and commands.
"""
self.rs = 0
self.data = 3
self.e = 1; self.e = 0
self.e = 1; self.e = 0
self.e = 1; self.e = 0
self.data = 2
self.e = 1; self.e = 0
def _write_raw(self, rs, x):
# rs determines whether this is a command
# or a data byte. Write the data as two
# nibbles. Ahhh... nibbles. QBasic anyone?
self.rs = rs
self.data = x >> 4
self.e = 1; self.e = 0
self.data = x & 0x0F
self.e = 1; self.e = 0
def write_cmd(self, x):
self._write_raw(0, x)
def write_data(self, x):
self._write_raw(1, x)
All that remains is to initialise the FTDI device, initialise the LCD module, and write some data to it.
def display(string):
ftdi_start()
lcd = LCD()
lcd.init_four_bit()
# 001xxxxx - function set
lcd.write_cmd(0x20)
# 00000001 - clear display
lcd.write_cmd(0x01)
# 000001xx - entry mode set
# bit 1: inc(1)/dec(0)
# bit 0: shift display
lcd.write_cmd(0x06)
# 00001xxx - display config
# bit 2: display on
# bit 1: display cursor
# bit 0: blinking cursor
lcd.write_cmd(0x0C)
for i in string:
lcd.write_data(ord(i))
ftdi_end()
if __name__ == '__main__':
# note blatant lack of error checking...
display(sys.argv[1])
and there we have it; a slightly cumbersome but also slightly cool and slightly useful little display utility. In the spirit of UNIX programming, this only does one thing - display the command line argument on the LCD. Obviously it needs major error handling if robustness is required...
while true; do python lcd.py $( awk '{print $1}' /proc/loadavg); sleep 5; done
Tuesday, 8 June 2010
Mandelbrot Canvas...
This Mandelbrot Plotter is something I worked on a little while ago. One of the things I want to do in any language I'm learning is know how to get a pixel on a page. A long time ago it was function 0Ch of INT 10h, then memory mapped displays, and now the wonderful ImageData object underlying HTML5's Canvas element. It's so much more fun displaying a picture than 'Hello World' in text...
I'm also fascinated by fractals, and the Mandelbrot set in particular (since I sort-of understand the maths behind it). It makes me wonder whether it is an invention or a discovery, and just why it is what it is - and whether other inscrutable patterns are just waiting to have some new visual representation cast a whole new light on them...
Of course with it being hosted (albeit slightly oddly on Blogger), I can now claim to have written a 'web app', for better or worse. Anyway, it's nice to be able to put random JavaScript up within Blogger...
Thursday, 27 May 2010
Let there be LEDs
Some time later, Man discovered Gallium Arsenide, and eye-piercing LEDs have blighted us ever-since. (Although they used to be quite dim, actually). Nevertheless, the 'hello world' of the embedded world is to make an LED flash.
In part 1 I 'covered' installation and test of the libftdi module, but the program didn't actually do anything. The UM232R module is primarily a UART module (unlike the DLP USB245M, which is a FIFO), but as with most recent FTDI devices it supports a 'BitBang' mode. BitBang is embedded jargon for running a protocol at the digital bit level, specifying in firmware at what point each of the (potentially many) IO lines goes high or low.
To put these devices in to this mode, an API call is needed to the FTDI driver to establish this mode. The ftdi_set_bitmode function takes three parameters; the FTDI control context (by reference), a direction bitmap, and a mode indicator (which should be 1 for async bitbang mode). The interesting is the direction bitmap. For each of the 8 IO lines, the corresponding bit in this mask can be a '1' to indicate an output, or a '0' for an input. I'm starting with output, so...
if ((ret = ftdi_set_bitmode(&ftdic, 0xFF, 0x01)) < 0) {
fprintf(stderr, "unable to set bitmode\n");
return EXIT_FAILURE;
}
(this fits into the 'DO STUFF HERE' part of the code from last time)Once this is done, we can start writing things out to the data port.
unsigned char x = 0;
while (1) {
ftdi_write_data(&ftdic, &x, 1);
x ^= 0xFF; /* toggle all lines */
usleep(100000) /* pause for 100ms */
}
This is hackish - it never exits for a start - but it should enable an LED to flash!
To do anything more interesting, let's move to Python. This is where the fun begins - with ctypes. I remember the first time I used ctypes, using puts from the C standard library on a Solaris machine, and the fact that it just worked. Occasionally there is a need to write the equivalent of a function prototype, but most of the time things are incredibly easy.
First up: loading the shared library. Lets guess its name - libftdi.so?
[GCC 4.1.2 20061115 (prerelease) (Debian 4.1.1-20)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>>
>>> from ctypes import *
>>>
>>> fdll = CDLL('libftdi.so')
>>>
>>> fdll
<CDLL 'libftdi.so', handle 81da028 at b7cfd4ac>
>>>So far so good :) Next thing is this
ftdi_context struct which is referenced by each function in the C code. It starts as an unpopulated structure, and presumably the first functions (init and usb_open) set it up as required. While the D2XX interface just gives an integer handle, in libftdi we have to pass pointers to this structure around. So, how do we define this in ctypes?First we need its size:
#include "ftdi.h"
#include "stdio.h"
int main()
{
printf ("%d\n", sizeof(struct ftdi_context));
}
Now compile and run...
/home/user/ftdi> gcc get_size.c /home/user/ftdi> ./a.out 84
So, we need 84 bytes of storage for this context. ctypes will do that for us:
>>> ftdic = create_string_buffer(84)
Done that. Now we can rattle it off: only two other things: first, where in C you give &var to give the address of a variable (e.g. as a parameter to a function taking *var), in Python with ctypes, it's byref(var). Second, to create a something which has existence in the C world, (e.g. so can have its address taken), there are a whole bunch of c_XXX functions - we'll use c_byte, which is equivalent to an unsigned char.
from ctypes import *
fdll = CDLL('libftdi.so')
ftdic = create_string_buffer(84)
fdll.ftdi_init(byref(ftdic))
fdll.ftdi_usb_open(byref(ftdic), 0x0403, 0x6001)
fdll.ftdi_set_bitmode(byref(ftdic), 0xFF, 0x01)
import time
x = c_byte(0)
try:
while True:
fdll.ftdi_write_data(byref(ftdic), byref(x), 1)
time.sleep(0.1)
x.value ^= 0xFF # toggle all bits
except KeyboardInterrupt:
pass
fdll.ftdi_usb_close(byref(ftdic))
fdll.ftdi_deinit(byref(ftdic))
And we're done! It's surprising how easy it is to translate basic C code using libraries into Python using ctypes - normally it just works.
Next time we'll look at interfacing to an LCD module...