Linux Kernel Module for the TSIC 306

I’ve made my first foray into the kernel, by writing a kernel module for the TSIC 306 temperature sensor. After overcoming the difficulties in setting up the tools, the actual driver development was much easier than expected. The source code is up on Google Code and is basically working, but needs a little more development.

This maps the sensor to the file-system, so you can load the module and read the temperature like this:

$ sudo insmod tsic.ko
$ cat /sys/kernel/tsic/temp
14680

In the example above, the temperature is 14.68°C and all the values are scaled by 1000 to avoid floating point maths in the module, and to match the behaviour of existing sensors such as:

$ cat /sys/class/thermal/thermal_zone0/temp 
40084

Writing the module turned out to be easier than expected, and there are some great libraries such as gpiolib and kobject which simplify development. Documentation is a bit scarce, but there are some useful resources:

Eventually I plan to add support for multiple sensors on configurable GPIO pins,  make it more robust and clean up a few issues such as the lack of time-outs in the I/O.

Building Kernel Modules on the Raspberry Pi is Ridiculously Hard!

I don’t think I’m alone on this issue… it seems almost impossible to build a working kernel driver on the Pi. After much trial and error, and on the verge of giving up completely, I finally succeeded!

I started with cross compilation on Ubuntu but couldn’t get it to work initially, it kept giving “Invalid module format”. Compiling directly on the Pi, again I was only able to obtain “Invalid module format” errors and, finally, a module that loaded but crashed the OS. The .config and Module.symvers seem correct, but it just would not build a working module. It makes me suspect some compiler toolchain difference between the kernel and the tools used to build the module.

Finally, cross compilation worked. Here is the magical recipe which  worked for me under Ubuntu, cross compiling for 3.10.33+ (mostly in case I need it again myself). First, set up the environment variables and download tools as described here.

git clone --depth 1 git://github.com/raspberrypi/linux.git
cd linux
cp your/raspi/config .config
wget https://github.com/raspberrypi/firmware/raw/master/extra/Module.symvers
ARCH=arm CROSS_COMPILE=${CCPREFIX} make oldconfig
ARCH=arm CROSS_COMPILE=${CCPREFIX} make menuconfig
# Save.. Exit..
ARCH=arm CROSS_COMPILE=${CCPREFIX} make modules_prepare
# Note: can specify -j9 for 8 core compile..

Well, now that it finally works, I’m not updating the Pi again until I’ve finished my driver 😉

Incidentally, here’s an easy way to find Module.symvers: https://github.com/raspberrypi/firmware/commits/master/extra/Module.symvers

Temperature Sensor Upgrade: the TSIC 306

I’ve been using the DS18B20 for a while now, but wanted something with a higher temperature range and faster update rate, but with a similar digital interface. After a lot of searching, it became clear that there are very, very few parts that fit these requirements…

Finally, I found a neat sensor called the TSIC 306. This has a temperature range of -50 °C to +150 °C and the update rate is a very respectable 10Hz. Better still, it’s available in a 3-wire TO92 package, similar to the DS18B20. It has one small disadvantage: it looks as if you can only have one device on the bus, so each sensor will need a dedicated GPIO pin. That said, it still looks ideal to upgrade the sensors in my coffee machine, so here goes!

The hardware interface to the Pi is very straightforward: it can accept 3V3 power, and the data line is actively driven low/high, so it can be wired directly to a GPIO line with no pull up resistor. However, it’s worth noting that the data suggests that 5V supply yields better measurement accuracy, so it may be preferable to power from 5V and use two resistors as a potential divider to reduce the data output to 3V3 logic levels for the Pi.

To date I have managed to read the sensor from C++ on the Pi, but it’s a little unreliable as it’s difficult to get accurate timing from user mode. It looks like a Kernel Driver may be needed…

Replacing the Gaggia Classic Thermostat with a DS18B20

For my Espiresso Machine, I needed to replace the stock thermostat on the Gaggia Classic with a DS18B20 (Digital Thermometer). My first attempt was pretty basic, and consisted of smearing the DS18B20 with thermal paste and using a cable tie to tether it the side of the boiler (just beneath the steam thermostat). This actually worked well in practice, but I wanted to come up with a better mounting technique.

Someone on the Gaggia Users Group forum suggested modifying this Circuit Board Standoff, which would have been perfect but unfortunately all the UK stock seem to be 7mm AF instead of 10mm AF, which wouldn’t be large enough to drill out.

Instead, I decided to make a mounting block for the sensor. I used some 10x10mm square section Aluminium bar stock, cut it down to 25mm length, drilled and countersunk a 4.5mm hole through the bar at 5mm from one end. This is designed to mount on the side of the boiler in place of the existing coffee thermostat, using an M4x0.7mm brass screw. There’s another perpendicular 6mm hole along the length of the bar, to accomodate the DS18B20 sensor (this is the waterproof version, which is encapsulated in a stainless steel tube).

The picture below shows the original thermostat (left), and the new mounting block, M4 brass screw and encapsulated DS18B20:

ds18b20_mount_vs_tstatThe 6mm hole intersects the 4.5mm hole, so that it can be filled with thermally conductive paste to ensure a good contact with the temperature sensor. The picture below shows the sensor and brass screw inserted into the block. The screw has been cut down to leave about 6mm of thread exposed, to match the existing thermostat.

ds18b20_mount_inserted

The final picture shows the sensor block mounted on the side of the boiler in place of the existing coffee thermostat (taking care not to over-tighten).

ds18b20_mount_in_situ

It was a challenge to tighten the screw in situ! Ideally I would have preferred an aluminium socket cap screw, but decided to use the materials to hand, rather than wait on another delivery 😉

Renault Modus Side Light Replacement

Now the Renault Modus isn’t a bad little car really, but it has at least one appalling design flaw: changing any of the front bulbs generally involves removing the whole front of the car!

Anyway, I recently had to change one of the sidelights, and read online that it’s possible to do this by removing  part of the front grille. I couldn’t find any pictures, so I decided to take some photos as I did the job in the hope it might help someone else in the same position!

To start with, here’s a picture of the side light from the front. The bulb (circled below) is a 501 (W5W) which is 12V and 5W.

Modus Sidelight Photo

When you lift the bonnet, there’s a rubber seal along the front edge. This is clipped into place at both ends with a plastic clip, and can be gently popped out as shown below, to reveal a plastic tab (centre of photo below):

Modus Sidelight Step 1

If you lift (and jiggle) this tab, you can pull out a long plastic shaft as shown below. Once removed, this releases the front grille.

Modus Sidelight Step 2

Having completely removed this, the grille can be popped out of the front panel as shown below with the plastic locking shaft. This needs care, to avoid breaking the tabs. Before removal I also popped out the four round plastic clips to free the top edge of the front panel so that I could see the rear of the grill, and this gives a bit more flexibility, but probably isn’t essential. To remove the grille, I pulled the lower edge of the grill forward and up (pivoting around the upper clips).

Modus Sidelight Step 3a

After you have removed the panel (hopefully without breaking any of the retaining tabs…) you will be left with a hole like this:

Modus Sidelight Step 3b

If you look inside here, you can see a rotary cover for the side light. Unscrew this and it will reveal a black plastic cylinder. This simply pulls out and you will find the bulb pushed in the end.

Modus Sidelight Step 4

Pull the bulb out of the holder and replace. Remember to refit the cover and test all the bulbs before reassembly.

This is quite a quick job and is much, much simpler than replacing the headlights, but that’s a story for another day!

Es(pi)resso: Raspberry Pi Espresso Machine

I’ve finally got around to writing up some details of my Raspberry Pi controlled Espresso machine, and put the source code on Google Code here: http://code.google.com/p/espiresso/

It’s still a work in progress, but it makes my coffee every single morning 🙂 This is the very definition of a safety critical system – imagine if it failed to work one morning (it doesn’t bear thinking about…)

Relocating an SVN Repo to Google Code

I wanted to move an existing Subversion repository to Google Code, preserving all the history. This is possible, but (as with most things) turned out to be  more complicated than expected.

To start with, you need to dump the existing repository. If you can’t log into the server directly, it’s possible to do this for a remote server using the svnrdump command:

svnrdump dump https://svnserver.com/trunk > remote.dump

This will probably contain the full history of several projects, so the next step is to filter out the specific project you need. Unfortunately, svndumpfilter won’t read the dump output from svnrdump (it doesn’t seem to like the file version).

One workaround is to temporarily create a local repository, load the dump file to that repository, and then dump it back out again. The new dump file is then accepted by svndumpfilter. Not very convenient, but it works:

svnadmin create temp
svnadmin load temp < remote.dump
svnadmin dump temp > local.dump

Then (theoretically) you can filter out the specific project path from the dump file using svndumpfilter. Again, this caused me some problems because it leaves lots of empty commits (i.e. log messages related to other projects, with no file changes).

svndumpfilter --drop-empty-revs --renumber-revs include /your/path/ < input.dump > output.dump

There is apparently a newer version of svndumpfilter which accepts a new option –drop-all-empty-revs. However, updating SVN didn’t get me this version, and I couldn’t be bothered to build it from source.

The solution was to use the much more capable svndumpsanitizer. Unlike svndumpfilter, this actually seems to work, and is a single C file with no exotic dependencies, so very simple to build.

svndumpsanitizer --infile input.dump --drop-empty --outfile output.dump --include /your/path

I did find one bug with svndumpsanitizer: it doesn’t work if your dump file contains a partial set of revisions. For example: I had a dump file which started from revision 88, and this caused it to index out of bounds of an array and output nonsensical revision numbers like 1638472. I only discovered this by adding an assertion in svndumpsanitizer to check array bounds. After that, I fed it a complete dump file containing all revisions, and it worked fine.

In my case, I also wanted to replace the username in the commit messages. This can be done using svndumptool as follows:

svndumptool.py transform-revprop svn:author OldAuthor NewAuthor input.dump output.dump

Then you need to create a local repository and load the dump file.

svnadmin create repo
svnadmin load repo < final.dump

If this doesn’t work, you might need to manually create some of the higher level folders (e.g. trunk) before loading the dump.

svn mkdir file:///full/path/to/repo/trunk

At this point you need to create your Google Code repo. Then you can use Administer… Source… Reset this project’s repository. to ensure it’s empty.

Finally, you can initialise the remote repo and sync the local repo to the remote Google Code repo.

svnsync init https://project.googlecode.com/svn/ file:///full/path/to/repo/ --username username@googlemail.com

svnsync sync https://project.googlecode.com/svn/ file:///full/path/to/repo/ --username username@googlemail.com

If all goes to plan, you should now have a copy of your project on Google Code, with full history available. If not, you can reset the repo on Google, filter the dump files again, and retry.

This took  quite a bit of trial and error and searching to find, so I hope it helps save you (and me) some pain in future.