Gigatron

The Gigatron is a TTL computer and when I first heard about it, I thought that with that name, I had to get one…

The Gigatron

The Gigatron

… and there it is. https://gigatron.io/

So what is it?

It’s the brainchild of Marcel van Kervinck and Walter Belgers.

In essence, it’s an 8-bit computer (I’d struggle to say “micro” here as it’s not that small!) made almost entirely from TTL chips with a few discrete components thrown in for good measure. It has a reduced instruction set of just 8 instructions with 32KB of RAM and 64KW EEPROM (16-bit wide EEPROM, so 128KW of EEPROM in total) It comes in a very nice case with a game controller and sockets for power in (mini USB), 3.5mm audio jack output and VGA for colour video output. The game controller is a clone of the Nintendo Famicom controller.

TTL?

TTL is Transistor-Transistor Logic. Essentially it’s a way to encapsulate many transistors into a single integrated circuit, or IC. The ICs are typically digital in nature and featured simple logic gates – AND/OR, etc. to more complex shift registers, latches and so on. The technology dates back to the early 1960’s but wasn’t standardised until Texas Instruments created their line of 74xx ICs in about 1966. Until then computers and other digital devices were made up from discrete components, transistors, diodes, resistors, etc. on large (or small) circuit boards. These new ICs allowed increased transistor density and lower power consumption.

The idea of making a computer of out of TTL is not new. DEC created the PDP-8/i computer in 1968 which was a successor to their PDP-8 computer (c1965) and was the first one they made almost entirely from TTL chips – the ‘i’ in PDP-8/i means Integrated Circuits.

And there are similarities between the Gigatron and the PDP-8 – I suspect Marcel and Walter are both PDP-8 enthusiasts in some way…

It’s a kit

Gigatron is supplied as a self-solder kit. I think it’s the biggest self-solder kit I’ve had in a very long time – if ever. However the (real, printed and bound) instruction manual is one of the best I’ve seen. Colour pictures and step by step instructions. Being a veteran solderer I assembled it in a slightly different order, but the supplied instructions do take you through some trouble shooting and tests along the way which for novice solderers will add confidence when you get going. Doing the multimeter checks and early power checks will be very worthwhile if you’re relatively new to this. (Also if you’re not – I’m a real fan of incremental testing rather than build a huge thing then try to debug it) The only thing I’d add to the instructions would be to get yourself a big lump of blue tack or something similar – I prefer using that to keep components on the PCB when turning it over to bending the leads out.

No Sockets?

The RAM and EEPROM chips are in sockets – the TTL chips not. Is this a big deal? I don’t think so. The anti-static foam sheet that all the chips come on is populated with the chips in the right locations and the right way round, however you should still check before putting a chip in and soldering it in. It is possible to de-solder and extract a TTL chip, but my experience is that it’s easier to simply snip the legs off, take the pins out one by one then solder in a new one – which is fine if you have spares, so just take your time and you shouldn’t have any issues.

Architecture

The native instruction set is very RISC at just 8 instructions with a handful of addressing modes. It’s an 8-bit Harvard architecture machine with effectively five separate data paths, one of which is the ROM where the code lives and that’s a 16-bit data bus. Instructions are effectively 16-bits wide – one byte is the instruction (which is 3 bits for the instruction, 3 for the addressing mode and 2 to select the bus), and one byte for data. It features a single 8-bit accumulator and a 16-bit memory pointer register accessible as 2 x 8-bit registers, X and Y which allows for different addressing modes. The 16-bit instruction bus allows for operations like “load accumulator with an 8-bit value” to execute in one cycle. However for the most part you are not expected to write in this low-level code, but to write in code which they have designed as a virtual 16-bit CPU (vCPU) which comes with a high (ish) level compiled language to go with it.

So here you have a 3-level system – the native code which is almost like a microcode in todays terms, the virtual CPU (vCPU) interpreter which is written in the native code and the high level language; GCL – Gigatron Control Language. GCL runs from RAM.

Programming directly in the native micro code is possible, however, it’s a tightly constrained system as it is not just the code for your programs, but is also controlling the video and audio output and other IO such as the game controller and “blinkenlights”. This all happens behind the scenes as it were when you write programs in the vCPU or GCL codes. Also – the native micro code can only run from ROM.

Video Output

The video is a VGA standard output with a 6.25Mhz dot-clock. 6.25 Mhz is also the clock frequency of the processor, so the video is generated one instruction at a time which fetches a byte from RAM and sends it to the 6-bit colour DAC to generate 64 colours per pixel at a QQVGA resolution of 160 pixels by 120 lines. It’s a simple RGB encoding; 2 bits of red, 2 green and 2 blue. There are some hardware optimisations/trade-offs here though – essentially it’s an 8-bit output latch which uses 6 bits for the colour and 2 bits for the H and V sync signals. To use all 8 bits for colour would require an additional output latch to drive the video sync signals.

Generating that in software uses almost all the processing power of the cpu, but there are free cycles at the end of each line during the horizontal sync period and at the end of each frame during the vertical sync period, however to make things a little easier for user programs the system skips every 4th line and that time is used to run GCL programs. You can turn off the line skipping though – the system still works, just slower.

The video system is somewhat wasteful of memory – you lose 2 bits per byte and to optimise the lines, they are arranged in RAM as 120 lines of 160 bytes each, however hose lines are aligned to a 256 byte boundary, leaving 96 bytes per line unused. It is possible to load code or data there though.

Sound and Blinkenlights

The sound system is again driven by software. There is an 8-bit latch which is split into 2 x 4-bit outputs. 4 bits for the LEDs and 4 for the DAC and audio output filter. Sound tables are held in RAM and the micro code mixes 4 channels of sound into one to send to the hardware at fixed intervals. I’m not sure if it’s possible to change the LEDs from user code though – will need to investigate further.

Software

The system comes with a 128KB EEPROM which contains the micro coded “operating system” (which is the drivers for the video, game controller, LEDs and sound), the vCPU interpreter and a selection of GCL coded programs – Classic Snake game and a racer program very much like one I played on the Apple II some 40 years ago… There is also a Mandelbrot program and some static images.

Writing your own

You can write your own code for the Gigatron, however as I type this, it’s somewhat tricky. Marcel and Walter are working on some development tools to make it easier, however following some hints I have been able to write a simple GCL program and make it run on the Gigatron. This involves pretending to write a ROM module, building the ROM, extracting the vCPU bytes for your module into a C language program – burning this program into an Arduino which is connected to the game port, then booting the Arduino – it then pretends to be a game controller to the Gigatron, enters the Loader application, then uses the game port to send the bytes of your program into the RAM of the Gigatron then starts it going. It’s not an easy process, but usable for now.

My first Gigatron program is simply a screen clear:

{
  Gigatron code to do some simple screen clearing
  -Gordon Henderson <projects@drogon.net>
}

gcl0x

0 c=

[do

{ Clear line at a time, top to bottom }

$0800 p= q=

120 l=

[do
  160 w=

  [do
    c q.
    q 1+ q=
    w 1- w=
  if>0loop]

  256 p+ p= q=

  l 1- l=
if>0loop]

c 1+ c=

{ clear column at a time, left to right }

$0800 p= q=

160 w=
[do

  [do
    c q.
    $100 q+ q=
  if>=0loop]

  p 1+ p= q=

  w 1- w=
if>0loop]

c 1+ c=

loop]

So there you have it. Right now it’s a very nice toy, if a little expensive at €160 (Currently €149.50 for early adopters) It does have a lot of potential though especially if, like me, you like esoteric little systems.

If you want to know more, hop over to their site: https://gigatron.io/

Raspberry Pi Weather Station. Software 4 – The Hard Bits

I said I’d leave the hard parts to last – well, even with these done, we’re not done yet, but it’s time to tackle the rain fall and wind speed sensors. These are slightly different from the others in that they are not external “high level” devices to read, but are digital inputs directly to the Pi so we need to write the code on the Pi to make them work. Both these inputs can trigger at any time, so we could poll for them, but if the Pi is busy then we might miss the pulse, so we will use a software interrupt mechanism to read them.

In a traditional microcontroller, e.g. PIC or ATmega, an interrupt input would cause the CPU to stop what it’s doing then execute your code with a bare minimal of delay then carry on what it was doing before.

The Raspberry Pi runs a multi-user, multi-tasking operating system, and if you want to do this on the Pi, then you need to write your own code which runs inside the Linux kernel. This is considered hard. So there is another way. The Linux kernel allows us to mimic this at the user-level by implementing a virtual file system, then posting a file-changed update in response to a GPIO pin changing state. This is no-where near as fast as code running inside the kernel, however for most cases it’s usable. This is what we’ll use here.

wiringPi provides a nice easy way to wrap this up for you. You simply specify the pin number, the trigger condition (a pin going from high to low, low to high or both) and a function to call when this event happens. When the trigger happens, Linux detect it, puts your main program to sleep, executes the named function, then wakes up your main program again.

(If you are lucky to be running this on a multi-core system, then your main program may not be put to sleep, but carry on running on one core with the called function running in parallel on another core)

This seems easy, and it is, but there are a few pitfalls. One is variable handling. The C compiler is very clever and it can keep track of variables – and if you had a loop, reading a variable, with nothing else in that loop changing it – well, it might be treated as a constant variable, after all, it never changes, so … So your program may produce the wrong result if this variable is changed in some sort of interrupt code that the compiler doesn’t know about. To get round this we need to use the C keyword volatile This tells the compiler that it might change elsewhere, so produces the appropriate code to handle it.

There are other issues to do with accessing variables in concurrent or parallel running threads/programs, but that’s left for an exercise for another time…

Back to the rain gauge… It turned out to be relatively simple to write the code in the end. The function that gets called when the pin interrupt is triggered is only a few lines long:

*
 * rainDrop:
 *	"ISR" routine to count each tip of the rain gauge sea saw.
 *	The debouce strategy is to set a timer and if we get called again before
 *	the time has expired, then we reset the timer - so rather than just wait
 *	a fixed time, we need to see /at least/ the time period without any
 *	triggers before we count the next one.
 *********************************************************************************
 */

void rainDrop (void)
{
  static struct timeval lastTime = { 0, 0 } ;

  struct timeval now, interval ;

// See if we're being called inside our debounce time

 gettimeofday (&now, NULL) ;
 timersub (&now, &lastTime, &interval) ;

 if (interval.tv_usec < (DEBOUNCE_TIME * 1000)) // Inside?
   lastTime = now ;                             // Reset for another period
 else
 {
   pthread_mutex_lock (&rainDropLock) ;
     ++rainDrops ;
   pthread_mutex_unlock (&rainDropLock) ;
 }
}

 The rainDrops variable here is a global declared with the volatile keyword. It’s used elsewhere in the file, but most of the test is handling debounce. Mechanical switches bounce and reed relays are no exception. Bounces can cause false readings, so we need to cater for them.

The rain fall and wind speed functions need a separate initialiser function: Here is the rain fall one…

void startRain (void)
{
  rainDrops = 0 ;
  pullUpDnControl (RAIN, PUD_UP) ;
  wiringPiISR (RAIN, INT_EDGE_FALLING, rainDrop) ;
}

And at this point I realised the “pins.h” file had been good so-far, but I felt it could be better, so I renamed it to setup.h and created a separate setup.h file with all the sensor initialisations in it. This meant editing all the files to change pins.h to setup.h, changing the Makefile and of-course testing it.

The setup.c file contains one function to setup all the hardware which right now looks like:

void setupHardware (void)
{
  wiringPiSetup () ;

  mcp3422Setup (ADC_PIN_BASE, 0x69, 0,0) ; // To-Do: Check error codes...
  bmp180Setup  (BMP180_PIN_BASE) ;
  ds18b20Setup (GROUND_T_PIN_BASE, "0000053af458") ;
  htu21dSetup  (HTU21D_PIN_BASE) ;

  startRain      () ;
  startWindSpeed () ;
}

Note that the DS18B20 setup has the sensor serial number hard-coded into it. That’s telling me that maybe its better off somewhere else. Not sure where right now, but I’ll probably think of something.

At this point, I’ve tested all the hardware. The rain and wind direction sensors appear to be working – they’re just returning simple counts right now – will convert that into real numbers shortly, but it’s almost all there now.

I’ve been using this site for some thoughts about presentation:

http://www.dartcom.co.uk/weather

These folks are not that far from me (about 15 miles away) although I’ve never popped in to say hello… They also have a nice webcam on Dartmoor: http://www.dartcom.co.uk/webcam which is nice to look at some days…

Anyway, one thing on that site is a dew-point gauge – this is not a real sensor but it’s a calculated value based on the relative humidity and temperature – so I implemented a little file with the calculation in it, in the same way other sensor files are implemented in the system. I did the same for a wind chill feature too. These values can be computed at any time from the existing data, but I think it’s just as easy to do it here.

At this point, I’m making plans to move it outside… I’ve identified a location near the house. It’s not going to be in an optimal location whatsoever – the wind speed and direction will be wildly wild but it’ll do for a demonstration. I’ve installed a a Wi-Fi dongle and re-adjusted the network settings (editing /etc/network/interfaces – your setup may vary) to give me a static IP address on my Wi-Fi LAN) and tested them. It’s outside, but in-range of one of my access points, so that’ll be fine and all I need to do is run power to it. The prototype I have has a separate UBEC (Universal Battery Eliminator Circuit – popular in radio control setups to provide a 5v supply off the main power battery which may be 18v or higher) so I’ll wire that in and test it. Dave supplied me with a 24v power supply – I’ll have to extend the lead on it, but that’s just a 10 minute job with the soldering iron and some heat shrink sleeving.

Raspberry Pi Weather Station. Software 3 – Getting there

At this point I have a little “main” program that sits in a loop reading most of the sensors and simply printing them to the screen. I’ve created an individual little file for each sensor and given them names – like getHumidity() ; rather than something a little lower level – even if it’s just a one-line wrapper in many cases – e.g. this is the core humidity.c file:

#include <wiringPi.h>

#include "pins.h"
#include "humidity.h"

/*
 * getHumidity:
 *	Return the relative humidity reading - result * 10
 *********************************************************************************
 */

int getHumidity (void)
{
  return analogRead (HTU21D_HUMIDITY) ;
}


/*
 * getTemperature2:
 *	Return the temperature read from the humidity sensor
 *********************************************************************************
 */

int getTemperature2 (void)
{
  return analogRead (HTU21D_TEMP) ;
}

the humidity.h file looks like:

// Function prototypes 

extern int getHumidity (void) ;
extern int getTemperature2 (void) ;

which is just definitions (prototypes) for the functions so they can be used elsewhere. The compilier will check the usage against the definitions here and tell you if there are any mis-matches.

I’ve cracked on and created little functions/files for most of the sensors now – even though some are just 1-line calls, they allow the main program to read a little better, so we can easily change the main program using readable words than sensor names. e.g. the ground temperature probe is literally:

int getTemperature0 (void)
{
  return analogRead (GROUND_T) ;
}

The main program now looks like:

#include "airq.h"
#include "groundT.h"
#include "windDirection.h"
#include "pressure.h"
#include "humidity.h"

int main (void)
{
 wiringPiSetup () ;
 mcp3422Setup (ADC_PIN_BASE, 0x69, 0,0) ;
 bmp180Setup (BMP180_PIN_BASE) ;
 ds18b20Setup (GROUND_T_PIN_BASE, "0000053af458") ;
 htu21dSetup (HTU21D_PIN_BASE) ;

 for (;;)
 {
   printf ("%4d, %10s", getAirQuality(), directions [getWindDirection ()]) ;
   printf (", %4d", getTemperature0 ()) ;
   printf (", %4d, %4d", getAirPressure (), getTemperature1 ()) ;
   printf (", %4d, %4d", getHumidity (), getTemperature2 ()) ;

   printf ("\n") ;
 }
}

So nothing desperately difficult there. It’s printing nothing more than the values separated by commas – an example output:

 40, East, 131, 10177, 156, 511, 160
 40, East, 131, 10178, 156, 511, 160
 39, East, 131, 10178, 156, 512, 160
 40, East, 131, 10178, 156, 512, 160
 40, East, 131, 10177, 156, 512, 160
 40, East, 131, 10177, 156, 512, 160
 39, East, 131, 10178, 156, 512, 160

The 3 temperature sensors are a bit all over the place this morning – 13.1, 15.6 and 16.0, however it’s really just sitting in a pile on the floor – the ground temperature probe is somewhat distant from the others.

I’ve also noticed that reading the 1-wire sensor takes almost a second. Weird.

However – as I mentioned earlier, I’ve left the hard parts to last. The wind speed and rainfall sensors… Now it’s time to code them

Raspberry Pi Weather Station. Software 2: Make

Now it’s a matter of plumbing it all together. I have created a Makefile – by copying an existing one and changing it (I used the one from the gpio program because it’s similar) then I took the weather.c program and split it out into 2 files – one is still called weather.c and is the “main” program for the system, the other is now windDirection.c – and from this I created windDirection.h. The .h file has the function prototype and some data in it.

I also needed to create a new header file; pins.h. This will contain all the pin definitions for the sensors in the system.

Once that was running, I did the same for the air quality program – I deleted it’s main() section and created airq.h from it and integrated it into the Makefile.

A word on Makefiles before you dive in and start editing them yourself. This is what mine currently looks like:

#
# Makefile:
#	Raspberry Pi weather station project
#################################################################################

ifneq ($V,1)
Q ?= @
endif

#DEBUG	= -g -O0
DEBUG	= -O2
CC	= gcc
CFLAGS	= $(DEBUG) -Wall -Wextra -Winline -pipe

LIBS    = -lwiringPi -lm


SRC	=	weather.c		\
		windDirection.c airq.c

OBJ	=	$(SRC:.c=.o)

all:		weather

weather:	$(OBJ)
	$Q echo [Link]
	$Q $(CC) -o $@ $(OBJ) $(LDFLAGS) $(LIBS)

.c.o:
	$Q echo [Compile] $<
	$Q $(CC) -c $(CFLAGS) $< -o $@

.PHONY:	clean
clean:
	$Q echo "[Clean]"
	$Q rm -f $(OBJ) weather *~ core tags *.bak

.PHONY:	tags
tags:	$(SRC)
	$Q echo [ctags]
	$Q ctags $(SRC)


.PHONY:	depend
depend:
	makedepend -Y $(SRC)

# DO NOT DELETE

weather.o: pins.h windDirection.h
windDirection.o: pins.h windDirection.h
airq.o: pins.h airq.h

There is one very important thing to note when editing Makefiles: The lines that do not start on the left-hand edge start with a TAB character. That’s a proper, hard TAB and not 8 spaces. If you use spaces then it will all go horribly wrong. Trust me on this.

Makefiles were developed in the days when processors were slow (compared to today), disks were small (compared to today) and people wanted to make it easy for themselves. So in a Makefile, a line that starts with a TAB in deemed to be a rule continuation line. The time when this will trip you up is when you copy & paste a Makefile. If you ever do that, then double-check that its got the right stuff in it before trying to run it.

A quick explanation of what this makefile does – any how…

The lines near the top define variables which can be used later. e.g. DEBUG  = -O2. This lets me quickly change a few parameters. Note the CFLAGS containing -Wall and -Wextra. These are going to give me lots of whinges from the compiler which will pick out some silly mistakes.

Note the SRC line. It’s continued over 2 lines using a backslash.

Next is the OBJ line. It’s starting to become weird here, however what that line is saying is OBJ will contain what’s in SRC but change the .c into .o. We are going to compile the files, one at a time into object files which have a .o suffix, then we’ll link them together into an executable program. That way when we change one file, only that file needs compiling and not every file. (Although there are exceptions – later!)

Now we have lines which have a target followed by a dependency rule. The first target is all. This really refers to a file – which doesn’t exist, so it checks the dependency. This is weather, so all depends on weather. (which is also a file).

Carrying on, weather depends on OBJ – or all the .o files in OBJ – so if one of these has changed, then we re-do the weather rule – which is the final linking stage.

Under that, there is a target .c.o: This says that every .o file depends on a .c file, so if the .c file changes, then make the .o file. The commands under there run the compiler on that file.

The make system works recursively – it descends the file looking for things that depend on other things then performs the actions to satisfy the dependency.

Further down there are some fake (or phony) targets. These allow us to perform actions – e.g. make clean makes the clean target which in this case removes unwanted files.

The bottom part is computer generated – that’s why it says DO NOT DELETE. It’s generated by the makedepend script – called when I type make depend This goes through all the C source files, looking for the headers that they depend on. So if I change a header file, then it re-compiles the file that includes that header. Note the pins.h file – that is included in all files, so if I change that, then all files will be re-compiled…

Here are some examples:

$ make clean
[Clean]

This runs the clean rule and removes all the compiled stuff.

$ make
[Compile] weather.c
[Compile] windDirection.c
[Compile] airq.c
[Link]

This compiles everything – needed after the clean.

$ touch airq.c
$ make
[Compile] airq.c
[Link]

Here I touched (which updates the time-stamp) the airq.c file, then ran make. It compiled that file only, then did the link stage.

$ touch pins.h
$ make
[Compile] weather.c
[Compile] windDirection.c
[Compile] airq.c
[Link]

and here I touched the pins.h file – which everything depends on, so it compiled the lot. I’ll crack on and start coding up little routines to sanitise the sensors now and see where I get…

Raspberry Pi Weather Station. Software 1

The story so-far… In the beginning the universe was created… Then after a while came weather with the inhabitants of a little blue-green planet having a great interest in this weather – presumably because there was so much of it… So people started to try to predict the weather. Seaweed, pine cones and sky colours were used by old wives until the advent of computers. Some still say that seaweed and so on are more accurate though…

But now, we have affordable little computers with affordable little weather instruments, so everyone can now be a weather forecaster! All you need to do is plumb it all together and write the software… That’s what I’m going to start doing now. I have the hardware – currently in-bits on my floor as my workbench is taken up with another project involving beer…

Start with a plan

Here’s the plan. I think I’ll write a single C program that periodically looks at the sensors and writes them into a log-file. Nothing fancy, just a simple human-readable CSV file. Probably one line per minute as that’s going to be good enough to start with. The program will need to keep running for as long as possible so it can keep track of the count of rain sensor tilts and the wind speed. Rainfall is typically measured in millimetres per square meter per unit of time. 1mm in a square meter is 1 litre… Wind speed is typically measured in kilometres per hour, so these two measurements will require some sort of program running over a period of time to perform the measurement.

So-far I’ve written some new code for wiringPi to let it read the sensors directly, and I’ve tested it using the gpio program. I also wrote a bit of code to help me get the wind vane/direction readings. I’m thinking I’ll use this code in my main program and create a little function which wraps the low-level sensor readings into something more readable for the main program. I have a view that the main program will be nothing more than a loop – running once a minute – which calls each sensors sampling code then writes those values into a file (top-down thinking), but when it comes to actual coding, I like to write the low-level codes first and test them as I go.

I also like to write the easy bits first – sometimes they’re really the hard bits in disguise, but there are 2 functions that will require interrupt handlers – I’ll class those are hard and leave them until last. At least for now.

Since I already have some code for the wind vane, I’ll start with that.

Starting a new project

What I typically do would be to create a new directory and edit files in there. So in the /home/pi directory,

$ cd
$ mkdir weather
$ cd weather
$ mv ~/wiringPi/wiringPi/vane.c .
$ mv vane.c weather.c

and off I go. I’ve renamed the vane.c file to weather.c which will be the main file. Typically I’ll start with one file, then split the functions into separate files so they can be compiled separately.

Note: Just in-case you’ve missed this, I’m doing all the work directly on the weather station Raspberry Pi. I’m logged in via SSH from my (Linux) desktop PC. I am not using an IDE (I use vim), and I am not cross-compiling on another system. This is what the Raspberry Pi was designed for – to allow you to develop programs directly on the Pi without the need for anything else. I could be doing this on the Pi if it had a screen and keyboard – but it doesn’t and I have a nice keyboard on my desktop, so remotely via ssh works just fine. FWIW: the Pi is much faster with more memory than the first Unix computer I used X windows on – a Sun 3 with 4MB of RAM. No local storage, it was all on a network file server (NFS) via 10Mb Ethernet … The first Unix computer I used only has 128KB of RAM and a 3MB hard drive… (and ran at 4Mhz) How times have changed.

In the end, I threw away most of the vane.c code – I was going to keep its low-pass filter but I don’t think I need it – also to use it, I’d need to keep sampling the wind direction sensor more or less continuously. Exercise for another day. Things I tried when writing the code – use a fuzzy match on the sensor reading – e.g. +/- 5 to compare with the readings I got with the vane.c program earlier, however that didn’t work as some of the samples were too close. In the end, I went for a list of the values I got with the vane.c program and simply compared the sampled value to each in-turn to find out which was the closest. This seems to work OK and I’ve tested it on my desk and I can reliably get 8 different compass readings. A programming rule I applied here was: “Don’t be afraid to throw away code and start again.”

If you’re doing this yourself, then you’ll need to run the original vane.c program to get the values out of your wind vane as I’m sure no 2 are going to be identical…

Things that have popped into my head while doing this in a sort of random order – need a common header file for the pin numbers, maybe need a common naming scheme, does each sensor need any initialisation code?

Here is the source of this first little module in its entirety:

/*
 * weather.c:
 *	Plan C for the Raspberry Pi weather station.
 *
 *	This is an example/demonstration of building a project (in this case
 *	the Raspberry Pi weather station) in C.
 *
 *	Copyright (c) 2016 Gordon Henderson.
 *********************************************************************************
 */


#include <stdio.h>
#include <stdlib,h>
#include <wiringPi.h>
#include <mcp3422.h>

#define	DEPTH	8

#define	DIR_N	0
#define	DIR_NE	1
#define	DIR_E	2
#define	DIR_SE	3
#define	DIR_S	4
#define	DIR_SW	5
#define	DIR_W	6
#define	DIR_NW	7

#define ADC_PIN_BASE	100
#define	WIND_PIN	  0

// Some global data

char *directions [8] = 
{
  "North",
  "North east",
  "East",
  "South east",
  "South",
  "South west",
  "West",
  "North west"
} ;


#define	NUM_READINGS	14

static int readings  [NUM_READINGS] = { 814, 204, 251, 29, 33, 23, 71, 46, 124, 100, 455, 1765, 1279, 590, } ;
static int direction [NUM_READINGS] = {   0,   1,   1,  1,  2,  3,  3,  3,   4,   5,   5,    6,    7,   7, } ;


/*
 * getWindDirection:
 *	Return the current wind direction - or at least try.
 *********************************************************************************
 */

static int getWindDirection (void)
{
  int i ;
  int sample ;
  int diff, bestDiff, candidate ;

  candidate = 0 ;
  bestDiff  = 999999 ; // a highly unlikely value

  sample = analogRead (ADC_PIN_BASE + WIND_PIN) ;

  for (i = 0 ; i < NUM_READINGS ; ++i)
  {
    diff = abs (readings [i] - sample) ;
    if (diff < bestDiff)
    {
      bestDiff = diff ;
      candidate = i ;
    }
  }

  return direction [candidate] ;
}


/*
 * main:
 *********************************************************************************
 */

int main (void)
{
  wiringPiSetup () ;
  mcp3422Setup (ADC_PIN_BASE, 0x69, 0,0) ;

  for (;;)
    printf ("%s\n", directions [getWindDirection ()]) ;
}

It’s essentially the wind vane read function with a tiny little wrapper that will let me test it.

It was compiled with:

$ gcc -Wall -Wextra -o weather weather.c -lwiringPi

I strongly encourage everyone to use -Wall and -Wextra in their code. It can pick up all sorts of simple mistakes and give you very verbose warnings. I feel there is little excuse these days to have programs that don’t compile cleanly without them.

The program compiled and ran (and was re-edited, changed, re-compiled, re-run) until I was happy with it.

Next, I’ll look at the air quality sensor….

Raspberry Pi Weather Station. 5 – Wind and Rain

Now its time to look at the rain gauge and wind speed sensors.

As far as I can tell, these are just single-bit inputs on the GPIO connector. I don’t know which pins and before I wade through the supplied Python,  I’m going to use the gpio program with the watch program and see which input bits change when I move them…

watch -n1 -d gpio readall

is what I’m running …

and nothing happened. Suspecting that the inputs need the pull-ups activating (mostly because there is nothing obvious on the PCB), I turned them on for (wiringPi) pins 0 through 6 (7 is used by the 1-wire system)

for i in `seq 0 6`; do gpio mode $i up ; done

and tried again. Success. I found that the wind speed sensor is on pin 0 and the rain gauge is on pin 2. I also checked this by taking the board out and looking… Maybe I should have done that first… (Update to note that the production board inputs are on different pins)

Rain

The rain gauge is a clever little device – basically 2 buckets on a see-saw mechanism. When one bucket fills, it tips the unit over, emptying it and sending a pulse down the wire. The other bucket then fills, it tips out and sends another pulse. The pulses are generated by a reed switch and a magnet. The reed switch is normally open and closes momentarily as the see saw passes the mid-point. The Pi foundation have a good write-up on it here. With the internal pull-up enabled, the input normally reads high, but as the device tilts over there is a pulse low then high again.

Note: Going through the Pi Foundation pages now on the rain gauge and wind speed sensor I find that they are using different pins on the production boards, so I’ll make a note of that in the code I write.

According to the manual, Each little bucket holds the equivalent of 0.011 inches of rain or 0.2794mm. We’ll stick to metric units for our code here as no-one uses imperial any more.

So each tip of the see saw gives us another 0.279mm of rain. This will be easy to code in a little interrupt driven function using a falling-edge trigger. (and maybe some debounce – need to check on this)

Anemometer

The wind speed thingy is the usual rotating 3-cups device. There is one magnet and one reed switch, but the way it’s positioned you get 2 pulses per rotation. The pulses are high to low transitions. The Pi Foundation have the calculations on their page about the anemometer, so I’ll probably just use those than working it out myself (lazy programmer principle). Like the rain gauge I’ll use an interrupt driven function to record this.

Done?

So I think that’s that with the sensors. I’ve written some new wiringPi drivers for the devices on the weather station – I’ve not really gone into detail about the C code for these as I don’t consider it quite entry-level C. This was more to do with jotting down the discovery process – a little more than I’d log in a Black & Red, but the same sort of thing.

The next thing to consider is putting it all together.

Raspberry Pi Weather Station. 4 – The ADC

The big board that the Raspberry Pi sits on in the prototype weather station I have has an on-board 2-channel MCP3426 analog to digital converter. One channel is used to read the air quality sensor, the other reads the wind direction indicator. (The production model has 2 ADCs, but only uses one channel on each one).

wiringPi supports the MCP3422 (and 3424) ADCs so I decided to compare the data sheets for the ‘3422/4 and the ‘3426/7/8 … and I’ve found that with one small difference (the ‘3422 appears to support an 18-bit conversion) they are identical, so I don’t need to do anything here as long as I don’t try to use 18-bit mode on the MCP3426/7/8.

So lets test it.. Read both channels on the ADC:

$ gpio -x mcp3422:100:0x69:0:0 aread 100
2047
$ gpio -x mcp3422:100:0x69:0:0 aread 101
1188

The syntax for using the MCP3422 here is that you need the pinBase (which you need for everything) this is 100 here, then the I2C address (0x69) then the sample rate (0 here is the default fastest to give 12 bits per sample) and the last 0 is the gain. 0 is no gain.

Reading channel 0 (pin 100 here) gives 2047. It looks like this input is pulled high…

The device can sample with 12, 14, 16 or 18 bit precision. The first zero in the command above selects the default which is 12 bits, but we’re reading 2047. This is 2^11 – 1… So why are we not seeing  then 2^12 -1 or 2047? It’s because the chip is designed to read the difference between 2 input pins and when used in single-ended mode (which it is here) you only get half the reading, so the range is 0 through 2047.

I’m guessing for now that channel 0 is connected to the wind direction device and that channel 1 is the air quality sensor… So I’ll plug in the wind direction thingy now and see what happens …

Wind direction

I ran the command:

$ while true; do gpio -x mcp3422:100:0x69:0:0 aread 100 ; done

and this printed the values up the screen in a stream which changed rapidly when I moved the wind direction indicator vane.

And so, yes, it’s verified to me that channel 0 is the wind direction indicator. These things are somewhat “creative” in their design in that they have a series of reed switches and a magnet. The switches are connected to resistors and its arranged in such a manner that you get 16 different voltage readings as the direction indicator turns.

See this page here for a good description of the wind direction indicator.

My task now is to work out each of the 16 direction readings and this my vary from system to system, so I might store it in a file for later use and tweaking…

Using the above script, I get the following reasonably unique values:

251, 814, 590, 1278, 969, 1763, 456, 100, 124, 46, 71, 23, 33

So I have 13 different values – not bad for 16 directions – however there was a lot of noise… Is it worthwhile trying to use these values I wonder … Digging a bit deeper, according to the data sheet, it should be possible to get 16 different readings for 16 directions. I am not seeing this at all.

Going to try again, but this time I’ll write some code to take the values and pass them through a low-pass filter to filter out the noise at the bottom bit and see what I get…

// vane.c:
//	Testing the Raspberry Pi Weather station wind vane

#include 
#include 
#include 

#define	DEPTH	8

int main (void)
{
  int filter [DEPTH] ;
  int i, p, value ;

  for (i = 0 ; i < DEPTH ; ++i)
    filter [i] = 0 ;

  wiringPiSetup () ;
  mcp3422Setup (100, 0x69, 0,0) ;

  for (;;)
  {
    filter [p] = analogRead (100) ;
    if (++p == DEPTH)
      p = 0 ;

    value = 0 ;
    for (i = 0 ; i < DEPTH ; ++i)
      value += filter [i] ;
    value /= DEPTH ;

    printf ("%6d\n", value) ;
    
  }
}

 I compiled it with:

gcc -Wall -o vane vane.c -lwiringPi -lm

Running and I’m getting nice stable, steady readings… I’ve also aligned the device with the north mark pointing away from me and south pointing towards me, so when I install it, I’ll know what the values are once I align it with a compass.

Now I get:

  • 814 – This is North and it’s stable either side for about 1/8th of a turn.
  • then… 204,251, 29
  • 33 – East
  • then … 23, 71, 46
  • 124 – South
  • then … 100, 455, .. missing
  • 1765 is the best “west”
  • then … 1279, 590, missing
  • then back to 814 for north.

2 readings missing from the claimed 16. So it’s not perfect – the principle is good, but I fear the mechanicals and cheapness of the magnetic reed switches are letting it down here, however it will be good enough to group the readings together to give us 8 directions which is probably going to be sufficient for this little system.

Air Quality

The other input channel to the ADC is from the air quality sensor. This is a TGS 2600 device. It’s basically a tiny little heater, warming a little semiconductor element which is acting as a resistor whose resistance changes depending on the gas surrounding it. It’s sensitive to Methane, Carbon monoxide, Iso-Butane, Ethanol and Hydrogen. The higher the concentration of these gases the lower the resistance is. (so the higher the output) It’s somewhat basic, but may be useful for something.

I used the while true; gpio … loop above to check it. On plugging in, the value dropped slowly and stabilised after a minute or 3. I suspect this is the heater warming up. To test it, as it’s sensitive to butane I got my chefs blowtorch and gave it a squirt (no flame of-course!) the sensor maxed out to 2047 almost immediately and has stayed there for a couple of minutes now… And as I type this, it’s started to drop.

If you want to play with the sensor in another setting, then have a look at these good pages provided by that family friendly Raspberry Pi foundation…

Carbon monoxide is a killer gas, so this sensor has use in domestic situations especially where you may have solid fuel stoves – not uncommon in rural areas. It’s produced due to incomplete combustion and binds to your red blood cells more readily than Oxygen – but unlike Oxygen isn’t released, so you eventually pass out due to Oxygen starvation. You’re highly unlikely to ever encounter the concentrations required in the open though. However that and the other gases mentioned can be produced as by-products of pollution. The sensor is not going to be accurately calibrated so it’s only going to give fluctuations over time and not an absolute measure of those gases.

Next we’re on to the rain gauge and wind speed indicators…

Raspberry Pi Weather Station. 3 – 1-wire again

Following on from the weather station wiringPi page

So I’ve had a thought on the one-wire temperature sensor…. 1-Wire is a well defined system and works well on the Pi, however wiringPi has a “pin” based system, so how about adding it into wiringPi? Since I’d have to write the C code to parse the data file to present back to a program, then using that code in wiringPi as an analog input pin ought to be easy – right?

So from the “how hard can it be” department, I’ll embark on making the popular DS18B20 temperature sensors a wiringPi device…

A quick recap – when working, they appear in the /sys/bus/w1/devices directory. This is a virtual filesystem that dynamically changes depending on the 1-wire sensors/devices attached. Files are identified by a family-code prefix which identifys the device type and a serial number. This is a unique 64-bit number for every 1-wire device.

Checking my setup:

$ cat /sys/bus/w1/devices/28-0000053af458/w1_slave 
f6 00 4b 46 7f ff 0a 10 d6 : crc=d6 YES
f6 00 4b 46 7f ff 0a 10 d6 t=15375

So if I write a program to open that file, then read it in, all I need to do is scan for the “YES” which indicates a good read, then scan for the t= part to extract the temperature.

And done. Testing as usual by updating wpiExtensions.c and the Makefile, then running the gpio command and comparing with the direct file output:

$ gpio -x ds18b20:100:0000053af458 aread 100
166
$ cat /sys/bus/w1/devices/28-0000053af458/w1_slave 
09 01 4b 46 7f ff 07 10 bf : crc=bf YES
09 01 4b 46 7f ff 07 10 bf t=16562

And they both read just fine and my rounding seems OK too.

Back to earlier – “How hard can it be” – it was actually quite easy to implement this in wiringPi in C – afterall, the hard work has already been done in the OWFS (One Wire File System), so all I needed to do here was parse it. wiringPi could maintain 1000’s of these devices if required too (the limit is the number of open file handles – normally set at 1024, but can be trivially increased, if required)

Next on the list is tackling the analog to digital converter chip and seeing if we can make sense of the air quality sensor and wind direction indicator…

Raspberry Pi Weather Station. 2 – wiringPi

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.

Makefiles

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

make

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…

Testing

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.

The HTU21D

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.

Raspberry Pi Weather Station. 1 – Setup

The Raspberry Pi Weather station is a hardware and software system designed by the Raspberry Pi Foundation designed to let schools, etc. record their local weather. The system was produced in conjunction with Oracle who are providing a centralised database to allow all the stations to store data.

So… Some time back I was chatting with Dave Honess of the Foundation about what sensors, etc. to put on a weather station and Dave got down to the business of designing a prototype board. I was offered one, but it came at a time when I fell ill and didn’t have the capacity to do anything much with it at the time… However now I have some time to play, so after reading some posts on Raspberry Pi forums from people asking for some C tutorials with mention of the weather station, I have decided to use my system and get it going.

My aim is to use it as a demonstration to others how to go about tackling a project from the start. I will not be using any of the Pi Foundation supplied software, but will be re-implementing parts of it in C (and possibly BASIC) using the wiringPi GPIO library. This is not intended as a means to learn C from scratch, but may give newcomers to C some insight, tips and techniques. If I get round to a fancy local graphical thing then I’ll likely do that in BASIC as it’ll be easier than doing it in C (for me, anyway). I may leave it as an exercise to others to get the data generated back up to the centralised Oracle database…

I’m writing this as I do the tasks, so it’s a sort of log or journal as I go. My own thoughts and methods might not be to everyones tastes, but choice and difference is a good thing in general.

Let me note a few differences between the one I have and the production one. The prototype has a large PCB that the Pi sits on-top upside down – this is Pi on-top (POT!) rather than HArdware on Top (HAT). The large base has an on-board real-time clock (which I probably won’t use as it’s going to be connected to my LAN and use NTP), and an on-board ADC chip and 3 sockets marked “Air”, “Wind” and “Rain”. It has a wire soldered onto it which connects to a temperature sensor – designed to be buried in the soil to measure ground temperature. The production ones have the same hardware, but have the addition of a 2nd ADC chip which I think is to make the air quality sensor a little more reliable. (The ADC is next to it rather than at the end of a long cable) I just want to stress that this is a working prototype and not the productions ones currently in-use in schools, etc. however if anyone has one of the production ones and they want to re-implement the software this this should work for them and I’ll make notes of the differences where I can.

Starting up …

To start, I need to make sure it’s all actually working and I do this before I even think about writing a single line of code. There really is no point jumping in writing code unless you know the hardware is working correctly.

So – the Raspberry Pi. The unit that came with it is a Pi model B+ Rev. 1.2. As it was quite an old SD card image, I started by copying a fresh install of Raspbian Lite to the SD card, connected it to my LAN and powered it up.

I’m doing the install “headless”, so I found its IP address and used SSH from my Linux desktop computer to login with the usual pi/raspberry credentials. There are many tutorials for doing this elsewhere, so I won’t go into detail here.

Next, I did a full upgrade, (apt-get update && apt-get upgrade), removed packages I wasn’t interested in and fixed the network and init system to the way I like having them. (Linux gives me this choice and I use it) I used raspi-config to set the overclocking to “Medium” (900Mhz) and set the timezone to Europe/London. Again, there are tutorials elsewhere for doing this and a lot of this is personal preference anyway.

Raspbian Lite doesn’t have wiringPi installed, so…

$ sudo apt-get install wiringpi

Quick test:

$ gpio -v
gpio version: 2.32
Copyright (c) 2012-2015 Gordon Henderson
This is free software with ABSOLUTELY NO WARRANTY.
For details type: gpio -warranty

Raspberry Pi Details:
 Type: Model B+, Revision: 02, Memory: 512MB, Maker: Sony 
 * Device tree is enabled.
 * This Raspberry Pi supports user-level GPIO access.
 -> See the man-page for more details
 -> ie. export WIRINGPI_GPIOMEM=1

Looks good to me.

Sensors

Ready to start plugging things in and testing them now.

First thing I’ll test is the ground temperature probe. This is soldered to a wire on the base unit of the pre-production one I have. The sensor is a 1-Wire temperature probe, the DS18B20. Linux has good support for 1-wire devices, so all that needs to be done is make sure the 1-wire subsystem is enabled at boot time. Fortunately the weather station board is the new HAT style with an on-board configuration EEPROM, so it should just plug and go…

… however it looks like the EEPROM may not be programmed, or have old/incorrect data in it, as the 1-Wire interface doesn’t seem to be enabled at boot time. Running

$ sudo raspi-config

and selecting the 1-wire option allowed it to be enabled. Testing this is as simple as a few ls commands:

$ ls /sys/bus/w1/devices
28-0000053af458 w1_bus_master1

The file (it’s actually a directory) starting 28- is the one we’re interested in.  The 28 tells us this is a DS18B20 temperature device and the other numbers is its unique 64-bit serial number. This number will be different in your system as each chip has a unique serial number.

We can read the temperature by reading the w1_slave file in this directory…

$ cat /sys/bus/w1/devices/28-0000053af458/w1_slave 
01 01 4b 46 7f ff 0f 10 e3 : crc=e3 YES
01 01 4b 46 7f ff 0f 10 e3 t=16062

We’re interested in 2 bits of information here – the first is the YES which means the device read successfully, the second is the t=16062. This is the temperature in °C times 1000, so it’s reading 16.062°C, however do not for one minute think that this sensor is any more accurate than 0.5°C. It’s not, so round appropriately when reading – ie. the temperature here is really 16.1°C give or take half a degree.

So what next. The air  sensors, I think. These are I2C devices on a separate board. Plugging it all together and I find that I need to install the i2c-tools package, then use raspi-config to enable the I2C system.

The wiringPi gpio command has a shortcut to the i2cdetect command and running it gives:

$ gpio i2cd
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- -- 
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
60: -- -- -- -- -- -- -- -- 68 69 -- -- -- -- -- -- 
70: -- -- -- -- -- -- -- --

Which is a little concerning as there are 3 devices on the Air Sensor board… Further investigation suggests that the board isn’t being read at all and those 2 devices are the main boards RTC and the analog to digital converter.

It’s possible that the cable I’m using isn’t the right one, or its too long.

  • … And an hour or so passes and I’ve found the right cable and learned a little more about the system. I initially thought the air quality sensor was I2C, it’s not; it’s analog. My output is now:
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- -- 
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
40: 40 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
60: -- -- -- -- -- -- -- -- 68 69 -- -- -- -- -- -- 
70: -- -- -- -- -- -- -- 77

and the devices are:

  • 40: HTU21D – Combined humidity and temperature sensor.
  • 68: The RTC
  • 69: MCP3427 2-channel ADC (The production board has 2 of these and on those boards you’ll see an extra 6A. My prototype board only has one).
  • 77: BMP180 – Barometric air pressure sensor.

The 2-channel ADC chip has inputs from the air quality sensor and from the wind direction sensor. The rain and wind speed sensors are connected directly to GPIO pins.

And that’s enough for part 1. I’ve setup the Pi, identified the hardware, plugged some of it in and while not reading all the sensors at least made sure that the I2C devices are all present.

wiringPi doesn’t have drivers for any of these sensor chips so the next stage will be to write code for the (in C) and test them.

Next time we’ll get the wiringPi source, write new devices for wiringPi, update the Makefile and do some testing.