Afew week ago I had a really good time testing 4DSystems 4Duino-24 board . One of the things I noticed is that the Serial Command Set interface is really flexible. You can easily drive the display from an 8-bit microcontroller. But you can also use more powerful controllers like an ESP8266 or an ARM machine like a Raspberry Pi or even my laptop.
4DSystems provide libraries forall those platforms and others. Most of those libraries share a common language: C (they havealso developed libraries in Basic for PicAxe and Pascal). But even thou I spend a lot of time write C code, when I’m on my laptop a prefer higher level languages like Node.js or python. So why not using Python to control these displays?
Actually, Python being written in C itself has a great support to wrapC libraries so you can use them from the language. Using Python to develop has several advantages:
Powerful language with complex but easy-to-use data structures Rapid development since it’s an interpreted language Mostly platform independent (you still need to compile the C libraries for your platform, but the wrapper and example should work without modifications) It’s coolSince the 4Duino-24 uses a Picaso chip I asked 4DSystems for a sample of one of their Diablo16 products. A gen4-uLCD-32DCT-CLB display arrived shortly after but I had no time to play with it until this week.


The gen4-uLCD-32DCT-CLB display with a nice bezel and the flat cable connected to the interface board

The back of the gen4-uLCD-32DCT-CLB display

The Diablo16 controller
Compiling the C librarySo what I wanted to do was a wrapper library in Python for the Diablo16 Serial Library . So first thing was to have a library to wrap.
The easiest way is to checkout the Diablo16 Serial linux Library repository and build it. It is meant for Raspberry Pi but doeswork on my x86_64Linux laptop. Once you have checked it out and “cd” to the folder it should be as simple as typing “make”. But it isn’t.
$ make[Compile] diabloSerial.c
diabloSerial.c: In function ‘OpenComm’:
diabloSerial.c:2240:14: warning: ignoring return value of ‘write’, declared with attribute warn_unused_result [-Wunused-result]
write(cPort, (unsigned char *)&ch, 1);
^
[Link (Dynamic)]
/usr/bin/ld: diabloSerial.o: relocation R_X86_64_32 against `.rodata.str1.1' can not be used when making a shared object; recompile with -fPIC
diabloSerial.o: error adding symbols: Bad value
collect2: error: ld returned 1 exit status
make: *** [diabloSerial.so] Error 1
It complains about you trying to link a dynamic library to a static one. But, good enough, it gives you the solution. Just edit the “Makefile” to add the -fPIC flag to the compile options:
CFLAGS = $(DEBUG) -Wall $(INCLUDE) -Winline -pipe -fPICAnd…
$ make clean; makerm -f diabloSerial.o *~ core tags *.bak Makefile.bak libdiabloSerial.*
[Compile] diabloSerial.c
diabloSerial.c: In function ‘OpenComm’:
diabloSerial.c:2240:14: warning: ignoring return value of ‘write’, declared with attribute warn_unused_result [-Wunused-result]
write(cPort, (unsigned char *)&ch, 1);
^
[Link (Dynamic)]
The result is a “libdiabloSerial.so” you will have to copy somewhere the python wrapper could find it. More about this soon.
Wrapping it upThe Diablo16 and Picaso wrapper libraries arereleased as free open software and can be checked out at my 4DSystems Python repository onBitbucket.
The trickis to use the ctypes library for Python.ctypes isa “foreign function library(that)provides C compatible data types, and allows calling functions in DLLs or shared libraries. It can be used to wrap these libraries in pure Python”.
It’s actually really magic when you call your first C function from python code. So I spent a few hours wrapping the Diablo16 Serial library. I found a way to preserve most of the API in a simple and fast to code way. I only changed the way the wrapper returns values in some cases, like when returning strings or arrays of values.
Since it was the first time I was writing a wrapper with ctypes I spent some time trying to find a way to avoid writing thousands of lines of function definitions. I’m quite happy with the solution I found. Most of the code has been moved to configuration, and only those functions that deal with pointers (strings, arrays,…) or byref variables have their own wrapper function in the library.
I also realised that the differences between Diablo16 and Picaso APIs are really minimal so I created two libraries that both inherit from the same classand only define those calls that are different.
So this is the DiabloSerial.py library, that inherits from the BaseSerial.py and defines only those calls that are specific for the Diablo16 controller and also the relative path to the dynamic library to wrap.
from ctypes import *from BaseSerial import BaseSerial
class DiabloSerial(BaseSerial):
def library(self):
return cdll.LoadLibrary("libs/libdiabloSerial.so")
def definitions(self):
definitions = super(DiabloSerial, self).definitions()
definitions['bus_Read8'] = [c_uint16, []]
definitions['bus_Write8'] = [None, [c_uint16]]
definitions['putstr'] = [c_uint16, [c_char_p]]
return definitions
There is little error check and I’m not sure all the calls would work, specially the error callbacks. I’m testing the wrapper as I’m writing sample code and so far so good. But if you happen to find a bug or have a suggestion please tell me.
Wiring the displayIn the box, aside from the display, there was a small documentation booklet, a 30 pin flat cable and a gen4-IB interface board . This board brings out the minimum pins required to communicate with the display controller: 5V, TX, RX, GND and RES for reset.

So I grabbed my FTDI based USB to UART board and wired them together using 5V logic and crossing RX and TX lines. I didn’t have to wire the RES pin.

A simple example
So let’s test thewrapper.
import sysfrom DiabloSerial import DiabloSerial
from DiabloConstants import *
def callback(errcode, errbyte):
print "ERROR: ", errcode, errbyte
sys.exit(1)
if __name__ == "__main__":
diablo = DiabloSerial(500, True, callback)
diablo.OpenComm('/dev/ttyUSB0', 9600)
diablo.gfx_ScreenMode(PORTRAIT);
dia