After setting up and testing some of the hardware in part 1, its now time to add the devices into wiringPi.
As in part 1, I’m writing this as I go, to give you an idea of my thoughts and a little more insight into the various processes I’m using.
wiringPi is a pin-based GPIO library. It has an abstraction layer that gives you a pin number that is then mapped onto the physical hardware, so rather than using e.g. bank 0, bit 17 you simply use “pin 0”, and so on for other pins. The Raspberry Pi does not bring out it’s usable GPIO pins in any sensible order and has in the past changed pins, so as long as wiringPi knows the internal mapping, then your programs don’t need to change or know what the hardware revision is.
wiringPi also supports the native pins and the board physical pin numbers should you wish to use them that way. This page has an explanation of the pin numbering history The easiest way to get a full mapping of the pins on your Raspberry pi, just type gpio readall This will work regardless of the Pi make, model or version you have.
e.g. on the Raspberry Pi B+ on the weather station I have:
$ gpio readall +-----+-----+---------+------+---+--B Plus--+---+------+---------+-----+-----+ | BCM | wPi | Name | Mode | V | Physical | V | Mode | Name | wPi | BCM | +-----+-----+---------+------+---+----++----+---+------+---------+-----+-----+ | | | 3.3v | | | 1 || 2 | | | 5v | | | | 2 | 8 | SDA.1 | ALT0 | 1 | 3 || 4 | | | 5V | | | | 3 | 9 | SCL.1 | ALT0 | 1 | 5 || 6 | | | 0v | | | | 4 | 7 | GPIO. 7 | IN | 1 | 7 || 8 | 1 | ALT0 | TxD | 15 | 14 | | | | 0v | | | 9 || 10 | 1 | ALT0 | RxD | 16 | 15 | | 17 | 0 | GPIO. 0 | IN | 0 | 11 || 12 | 0 | IN | GPIO. 1 | 1 | 18 | | 27 | 2 | GPIO. 2 | IN | 0 | 13 || 14 | | | 0v | | | | 22 | 3 | GPIO. 3 | IN | 0 | 15 || 16 | 0 | IN | GPIO. 4 | 4 | 23 | | | | 3.3v | | | 17 || 18 | 0 | IN | GPIO. 5 | 5 | 24 | | 10 | 12 | MOSI | IN | 0 | 19 || 20 | | | 0v | | | | 9 | 13 | MISO | IN | 0 | 21 || 22 | 0 | IN | GPIO. 6 | 6 | 25 | | 11 | 14 | SCLK | IN | 0 | 23 || 24 | 1 | IN | CE0 | 10 | 8 | | | | 0v | | | 25 || 26 | 1 | IN | CE1 | 11 | 7 | | 0 | 30 | SDA.0 | IN | 1 | 27 || 28 | 1 | IN | SCL.0 | 31 | 1 | | 5 | 21 | GPIO.21 | IN | 1 | 29 || 30 | | | 0v | | | | 6 | 22 | GPIO.22 | IN | 1 | 31 || 32 | 0 | IN | GPIO.26 | 26 | 12 | | 13 | 23 | GPIO.23 | IN | 0 | 33 || 34 | | | 0v | | | | 19 | 24 | GPIO.24 | IN | 0 | 35 || 36 | 0 | IN | GPIO.27 | 27 | 16 | | 26 | 25 | GPIO.25 | IN | 0 | 37 || 38 | 0 | IN | GPIO.28 | 28 | 20 | | | | 0v | | | 39 || 40 | 0 | IN | GPIO.29 | 29 | 21 | +-----+-----+---------+------+---+----++----+---+------+---------+-----+-----+ | BCM | wPi | Name | Mode | V | Physical | V | Mode | Name | wPi | BCM | +-----+-----+---------+------+---+--B Plus--+---+------+---------+-----+-----+
The middle column is the board physical pin numbers and working out from the middle to each side you have the pins value on a read (V), the current pin mode (input, output or one of the alternative modes – e.g. physical pins 3 and 5 are shown as ALT0 – this is the I2C mode), then there is the pin name – this is taken from various sources, e.g. GPIO numbers are from the original published document about the GPIO – and these are the same as the wiringPi pin numbers, then finally the native internal Broadcom pin numbers.
wiringPi is extensible in that you can add extra devices into it’s internal pin system and these devices can be digital or analog (or a combination of both). Internally there is a linked-list and when you ask for an operation on a pin that’s not internal to the Pi, wiringPi searches this list to find the driver code for the pin you are using then calls that driver code.
We need to add additional drivers for the humidity & temperature sensor (2 in one chip), and the pressure sensor, and then the analog to digital chip which has 2 connections; one to the air quality sensor and one to the wind direction indicator.
Lets start with the pressure sensor – the BMP180.
So google for the manual and I find that it has a temperature sensor as well as pressure. This isn’t unusual as it may be using the temperature as some sort of compensation for the pressure sensor. Page 11 of the manual has the sensor read flowchart.
So, since it can read pressure as well as temperature, let’s pretend this is a 2-pin analog input device. That way in our code we can simply
temp = analogRead (base + 0) ; press = analogRead (base + 1) ;
where base is the base pin we tell it to use when we initialise it.
Reading the manual more – it appears to be a register addressed device. It has a number of registers, each is 8-bits wide. There is calibration data which you read once then there is an algorithm which you follow to read and calculate the temperature and pressure.
More reading of the manual indicates that the BMP180 is the successor to the BMP085 and it just happens that I have code written for the BMP085, but not for the Raspberry Pi – it’s for an ATmega microcontroller system I did for someone else, so all I need to do it port the code…
But before that, we need to get the source code for wiringPi and understand how to build and install it and add stuff into it… But before that we really should un-install the packaged version…
$ sudo apt-get purge wiringpi $ cd ~ $ git clone git://git.drogon.net/wiringPi
this failed as Raspbian Lite doesn’t have GIT installed, so:
$ sudo apt-get install git
then re-do the git clone operation above. (Note capitalisation here too)
to build and install wiringPi:
$ cd wiringPi $ ./build
It will take a minute or 2 to complete. Test with:
$ which gpio /usr/local/bin/gpio
and that should be it. Run gpio -v and gpio readall to make doubly sure.
Now for the BMP180 code. Start by copying something that’s similar. The pcf8591 is an I2C analog to digital converter device, so we’ll use that as a template.
$ cp pcf8591.c bmp180.c $ cp pcf8581.h bmp180.h
and start editing the files… Not going to bore you with the details here the code will be in the next release of wiringPi, however some notes – the bmp180 has a lot of static calibration data. It’s inefficient to read it more than once, however the wiringPi device node structure doesn’t have enough space in it to store this data, so I could change the node structure, but this would waste space and require others to re-compile, or keep static data in the driver with the limit that only one BMP180 can be used in the system at any one time. I’m going to go with the latter limit for now… Which isn’t as bad as it might seem as the BMP180 has a fixed I2C address, so you can only use one per I2C bus – which really does mean one per Pi.
A Makefile is nothing more than a set of rules and actions. The rules are dependencies on files – or rather the last time a file was updated/created. If a file is deemed newer than the result of the action, then the action is performed.
It can be a lot more complex than that, but that’s the essence.
So why Makefiles? … wiringPi has 29 C files and a similar number of header files. If you change one of these files, then you really only need to compile that file – it’s a waste of file compiling all the files every time, so the Makefile lets you do that. It can scan the files, see while C source code files are newer than their corresponding object file, and if it finds such, then it can compile just that file, then perform a final linking operation. This lets you build large projects on relatively slow processors – such as the Raspberry Pi.
So – to add a new module into wiringPi, write the code, then update the Makefile to compile it, then it’s a matter of simply typing
and if that’s OK, then
sudo make install
to install the new library.
Are we there yet?
So this has taken me an hour or 2 – I started with my original code which I wrote for a microcontroller platform a few years back, then decided to see what else was about and a few google searches found a newer implementation for reading the BMP180 (and older BMP085) in a slightly more accurate manner, so I implemented it. The down-side of this code is that it requires the standard math library linking in to any programs that use it. In the process I added it into the Makefile and the wiringPi extensions system (see below) and used the existing gpio program to test it. Debugging? A few well placed printf statements when I needed them…
wiringPi has a utility program called simply gpio. It also has a run-time extension ability, so as I started to write the BMP180 code, I added it into the extensions facility – this is in the file wpiExtensions.c in the wiringPi directory. That way, I can call it from the gpio program without writing any more code (or re-compiling the gpio program)
$ gpio -x bmp180:100 aread 100 176 $ gpio -x bmp180:100 aread 101 10299
this tells the gpio program to use the bmp180 extension with a new pin base number of 100, then call the analog read function in that extension. (Reading pin 100 will read the first pin on the device. The bmp180 doesn’t have pins as such, but I’ve arranged the code such that reading pin 0 will read the temperature and return the temperature times 10. Reading pin 1 returns the pressure times 10 which is the absolute pressure in millibars and reading pin 2 returns the air pressure after compensation for your altitude – however you need to write to pin 0 to set your altitude in meters above sea level. It’s not really worth it unless you want high accuracy.
The gpio program is stateless, so trying to write the altitude isn’t going to do anything as it will be forgotten on the next gpio command execution. This can only be done within a program and will only be remembered for the duration of that programs run.
Incidentally, testing – is that the real temperature? I put the 1-wire sensor next to the air sensor board, left it a while then read them again:
$ cat /sys/bus/w1/devices/28-0000053af458/w1_slave 08 01 4b 46 7f ff 08 10 e4 : crc=e4 YES 08 01 4b 46 7f ff 08 10 e4 t=16500
So the 1-wire sensor is telling me 16.5°C and the Bosch BMP180 sensor is telling me 17.6°C. That’s close enough to give me confidence that they’re either both totally wrong or reasonably right.
The other sensors
The other sensors on the Air board are the humidity sensor (which also has a temperature sensor) and the air quality sensor. The air quality sensor has an analog output so to make that work I need to write code for the ADC. The humidity sensor is the HTU21D device. It has registers but also supports direct reading. I’ll give it 2 “pins” just like the BMP180 – the first being to read its temperature sensor and the 2nd to read the relative humidity. There are different modes and resolutions that can be set, but I’ll leave it at its defaults.
So I’ve basically copied the BMP180 code into htu21d.c, edited the wpiExtensions.c and Makefile then written the code for the HTU21D device.Like the BMP180, I am treating this chips as a 2-pin device and reading the first pin is the temperature and the 2nd pin gives the relative humidity.
Testing and it looks good – especially when I compare it to the bmp180 and the 1-wire sensor:
$ gpio -x htu21d:100 aread 100 162 $ gpio -x bmp180:100 aread 100 160 $ cat /sys/bus/w1/devices/28-0000053af458/w1_slave ee 00 4b 46 7f ff 02 10 de : crc=de YES ee 00 4b 46 7f ff 02 10 de t=14875
So the HTU21D gives me 16.2°C, the BMP180 gives 16.0°C and the 1-wire sensor gives 14.9°C. All close enough to satisfy. (And yes, it’s a little chilly here today!)
Reading the humidity the same way:
$ gpio -x htu21d:100 aread 101 495
This represents 49.5% Relative humidity. Right now, I’ve no way of verifying this, but it’s in the right range and today is a crisp, cold, sunny and dry day, so it’s a plausible result. I did breathe on it and it went way up – probably not something to be recommended, but at least it’s changing in response to moisture. Putting it back on the floor where it’s currently living and it’s dropping again.
That’s enough for now. Next time I’ll look at the ADC and see if I can get something sensible out of the air quality sensor and the wind direction indicator.