My 6502 system

A 6502 SBC: Working prototype

To mark my own use and knowledge of 40 years of the 6502, I decided to build my own little single board computer (SBC) based on it.

I had a few ideas in-mind – one was to make it a little faster than the old ones (but I really didn’t mind), one was to make it suitably “retro” and try to use the sorts of components available round about my target date – so this meant through hole components rather than surface mount. I also wanted to try to keep the hardware fairly simple.

My initial aim was for a stand-alone system with video output and keyboard input. Also a serial port for software download, blinkenlights, of-course and as much RAM as possible.

And then I hit a snag. Several, actually. The first one was that the old video controller chips are no-longer being made. Plenty on ebay, but…  Next was the keyboard interface – but PS/2 type keyboards are easy to come-by and relatively easy to use. Memory, and ROM/EEPROM… The 6502 has a unique (that I’m aware of!) way of accessing memory – it only accesses it on half a clock cycle rather than use a full clock cycle. The effect of this is that you actually need memory that’s twice as fast as you might first imagine. So for a 1Mhz 6502, you might think that memory with a 1 microsecond access time is OK, however the reality is that you need 500ns (or better) memory. The current 6502 chips from Western Design Centre run at speeds up to 14Mhz, so we’re looking at 70ns or better RAM. The issue there is that chips like RAM chips are moving more and more to using surface mount technology which I was trying to avoid. Fortunately I found some suitable memory chips – 32KB x 8bits, so I just needed 2 of them.

(And yes, I did say current up there – The 6502 is still being manufactured and you can buy them brand spanking new!)

EEPROMs were an issue – these days they’re big – much bigger than I need, both physically and capacity wise. Not real issue as you don’t need to use all their capacity, however they’re also not as fast as I’d have liked, so this means either slowing the clock during access or introducing wait-states to the 6502. This adds complexity – and more TTL IC’s, so more to go wrong.

Retro?

At this point I had a look at what year I felt was right for my little SBC and settled on 1984. This was 6 years after the Apple II and 2 years after the BBC Micro. Programmable Logic Chips were in-use at that point – the BBC Micros (and others like the ZX Spectrum) used their fore-runner, the Ferranti ULA, so I felt that a PAL (actually a GAL – a reprogrammable PAL) might be appropriate.

I also looked at video, keyboard and thought about other peripherals like SPI and I2C an decided to build a system that had a separate IO processor to handle this – and I used an ATmega1284P. This generates the video, has a serial port and can interface to the 6502 using a shared memory system. This eliminated the EEPROM wait-state shenanigans as the system effectively became ROMless at that point – the ATmega poked the bootloader directly into the RAM of the 6502 and could control its reset line.

Plus modern..

The ATmega has about 50 times the transistor count of the 6502 runs faster, has more and getter IO, but sometimes you have to do something new to make something old work.

And in the best traditions of make it up as you go along, I started on breadboard – and this was the barely working prototype:

Ruby on a Breadboard

The ATmega is at the top-right. Middle of the board is 32KB of static RAM, to the left of that is the 6502 and under that is the Lattice 22v10 GAL. The LEDs are mostly for debugging – the are extra outputs from the GAL.

It was a bit unreliable – using ribbon cables (“jumper jerky”) isn’t the best as they tend to pull each other out but it did work and served to prove the concept that I was after. I ran some very small bits of 6502 code with that setup and wrote a lot of the ATmega code for the video and the shared memory interface to the 6502.

Although that worked, it wasn’t really reliable enough to enable me to do much with it, so the next step was to make something a little more sturdy and for that I went back to an old favourite of mine – stripboard, and more on that design in the next post…

 

The WozMon

In 1976, Steve Wozniac wrote what’s commonly known simply as Wozmon.

Wozmon is a machine-code monitor program and for those who may not be aware, this is essentially code that starts to run in a microprocessor at initial power on. Lets be more specific, an older microprocessor system, not usually your modern Intel, AMD or ARM based system. We’re taking the old 8-bit micros here, and I’m specifically referring to the original 6502.

The monitor would let you examine and change memory. Some even had built-in disassemblers, debuggers and a whole host of tools to help you get going in a new machine. They would include subroutines that could be used by other programs, these basic input output subroutines, or BIOS as it came to be known were essential for bigger programs as it took that burden away from the programmer and helped to make programs run on different systems, or at least easier to port from one system to another.

Before the monitor there were LEDs (or even older; filament bulbs) and switches. Before that? you might have ended up soldering diodes to circuit boards. I kid you not.

Wozmon may not have been the first, but it was compact. Wozniac had 250 bytes to squeeze it all into. (6 bytes were reserved for the reset and interrupt vectors) ROMs and the means to program them were expensive in those days. He ended up with 2 bytes spare.

By todays standards Wozmon was crude. But it was also compact – if it didn’t understand what you typed then it would just print a backslash and let you start again, but however crude it may have been, it worked and let many early programmers enter code and use the computer that Wozmon ran on – the Apple 1.

After the Apple 1, there as the Apple][ (sometimes written as II, latterly as //, but rarely 2). That had more ROM space, so Wozmon was expanded and as computer got bigger and more complex eventually (we’re into the mid 1980’s now), the monitor for the various microprocessors was replaced by nothing more than the basic input/output subroutines and the BIOS as we know it today was born, although nowadays (c2018), the BIOS is bigger and more complex than an entire Apple ][ computer.

You may be wondering why I’m writing this – well, for no reason other than this year marks my 40th year of programming the 6502, I’ve built a little 6502 computer and written a monitor for it. The monitor resembles Wozmon – mostly because I used the Apple ][ variant of it back in 1978.

More to come – details of Ruby – my 40th anniversary 6502 computer.

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…