Making PCBs at home

I’d like to start making PCBs instead of using stripboard. Stripboard is horrid.

I think the easiest approach for me will be to expose the board to UV through a transparency printed on an inkjet. Because there’s an inkjet in the house, and I think that’s how we used to do it at school.

It seems fun to make my own UV box for this, from LEDs. Useful things about that:

On the subject PCBs in general:

I will need:

I already have a printer, Sodium Hydroxide and acetone.

Controlling an LED matrix with a MAX7221

While building a project with a 4×7-segment display, I was irritated by how many pins needed to be used just for the display. I barely had enough to be able to complete the project.

So I looked into methods for controlling an LED matrix with fewer pins. I learned about Charlieplexing, which seems interesting but non-trivial. Then I found the MAX7221 IC, and an Arduino library to interface with it. Perfect. I bought some to try out and they’re very easy to use. Though the display I was using would not run on USB power — I had to connect it to 7.5V from my bench supply.

Useful things:

New project: suitcase Airsoft prop

I’ve still a few bits to do on Captain Quack, but in the meantime, I wanted to jot down some notes about the next project. I’m going to make a defusable suitcase prop to use in Airsoft games. I’d like it to be quite flexible: it should suit lots of scenarios, from being the main event in a filmsim to being an objective in a quick skirmish game.

It should look the part. This will be used in skirmish and filmsim, not milsim, so I think there’s some licence to make it look a bit “Hollywood” rather than being realistic (which would be dull). Google image search found some useful inspiration.

Of course, it also needs to play well, so I’ll make a prototype to test the basic idea. This will consist of a small box with connector for pyro, a removable lid which will expose some cabling of various colours, and a countdown display. In the game, the timer will be set to a countdown value appropriate for the scenario. Players will have to defuse the box before the timer reaches zero. When the timer reaches zero, it will detonate the pyro.

Players will defuse the box by cutting the right cable: there will be many to choose from, and their number and purpose can be selected to suit the game. Some may be dummies. Some will defuse the box. Others will immediately detonate the pyro. (Possible future enhancement: wires which add or remove time on the countdown). Wire cutters may be next to the box, or may be issued to (some or all) players.

In the game, players will need to find out which cable they need to cut. Or, if they’re nearly out of time, take a chance! Again, this can vary to suit the scenario. In some games players might just be told which is the right cable. In others, there may be a puzzle they need to figure out. Or they might need to find intelligence, which could be a separate scenario.

As before, this will be an Arduino project and I’ll build something pretty after testing the game on the prototype. Feedback and ideas welcome!

MCOM – version 1 – Captain Quack goes bang

So, the MCOM worked great. Much fun was had by all, and the box (due to its rather odd siren) was christened: Captain Quack.

It’s a 600x400x400 box built from softwood and 6mm ply, with aluminium edging and stainless steel corner covers. I was pleased with the look. Inside, I had a steel-lined chamber at the bottom of the box with vents between it and the outside. This is where the pyro was loaded, and also where the siren was mounted so that the sound could escape better.

The upper chamber contained the rest of the electronics, and enough space to put other game objectives if desired. There was a removable panel at the bottom so that the steel-lined chamber could be accessed to set the pyro. This was secured using cupboard magnets that I enhanced with some neodymium disc magnets for a more secure hold.

Unfortunately, I forgot to take a picture of it when I finished, and during its second outing, a slightly-more-powerful-than-I-expected pyro did a bit of damage.

Captain Quack took some damage

Anyway, hopefully, you can tell from that picture how it looked before its misadventure!

It performed really well. The first game day there was an annoying bug where the timer would stop itself running. I’m pretty sure that the cause was a short, or some kind of capacitance effect. The box housing the electronics is a metal one, and although I lined it with tape, I think the pins at the bottom of the boards were poking through. Adding better insulation to the boards solved the problem.

The housing for the electronics, with controls mounted

After that, the electronics have worked flawlessly: before and after the pyro that blew it to bits ­čÖé

For the next version, I’m going to make the steel lined chamber more robust by getting rid of the removable panel and securing the top of the chamber more robustly. The top was completely forced off its mounting: the force of the explosion managed to push out eight 3/4″ screws, as well as blowing the ends and bottom off the box. To secure those, I’m going to screw them to the frame rather than pinning them. I’m also going to replace the vents at each end with a mesh screen.

Panels removed to fix damage. Main siren visible at the left, and the new mesh vent at the right.

Hopefully, a more robust steel lining combined with more ventilation at each end will allow the pyrotechnic’s gasses to escape at each end without doing any damage.

The pyrotechnics are quite serious

Since seeing it in action, I’m also going to add some new safety features: there’ll be a new piezo siren to sound when the explosion is 10 seconds away, the button will be disabled at that point, and we’ll brief players to stand clear when they hear the change in tone. I’m also going to add warning notices at the ends of the box.

Airsoft M-COM station: the prototype

So, I decided to make an M-COM station for airsoft games.

The first challenge was to get the electronics working. I thought this would be a perfect opportunity to get to grips with Arduino. I’ve had a go at using PIC processors for this kind of thing but it’s hard work: no libraries and coding everything in (arcane, mind-bending, RISC) assembly makes life pretty slow.

I wanted the following behaviour:

  1. A button which, when pressed, would arm or disarm the box
  2. When entering the armed state, the box should start an internal countdown timer, flash a light, and activate a siren
  3. When entering the disarmed state, the box should stop the light and siren, and pause the timer at its current value
  4. When the timer is close to expiring, the sound emitted by the siren should change
  5. When the timer expires, the light and siren should stop, and one or more electric pyrotechnics should be detonated
  6. A potentiometer which could be used to adjust the length of the countdown timer
  7. A push-to-close switch which could be used to reset the box for the next game

In order to simplify debugging and to avoid having to make a load of noise, I also added LED status indicators to show when the power is on, when the timer is running, and when the timer has expired.

After working out that list of things, I was a bit concerned that this was too big to be a good starting project to learn how to use Arduino, but it ended up being fine. Arduinos are really lovely to use!

I ended up part-designing and part-hacking-together the following circuit:

I used a chunk of stripboard for the non-arduino components. I now hate stripboard. I’m planning to investigate kits for etching PCBs — or if I don’t get around to that, I’ll try my next project with tripad board. Though really, that looks just as fiddly and irritating.

I got the circuit up & running and coded up chunks of the software as I went along. It took a while to get it all working correctly. I got quite confused about some odd behaviour until I realised that my (signed) ints were overflowing. Obviously been too long since I’ve done any C. The full program & comments is below:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
// Configs
const int warn_pin = 5;
const int siren_pin = 3;
const int bang_pin = 2;
const int pwr_pin = 13;
const int switch_pin = 4;
const int timer_pin = A0;
 
const unsigned long min_time_needed = 1000L * 60L * 5L;
const unsigned long max_time_needed = 1000L * 60L * 20L;
const unsigned long warn_time = 1000L * 30L;
 
const int debounce_timeout = 400;
 
// Globals
unsigned long time_start = 0;
unsigned long time_needed = max_time_needed;
 
void setup() {
  // Set the modes of pins
  pinMode(warn_pin, OUTPUT);
  pinMode(siren_pin, OUTPUT);
  pinMode(bang_pin, OUTPUT);
  pinMode(pwr_pin, OUTPUT);
  pinMode(switch_pin, INPUT);
 
  // Read the state of the timer adjust pot
  time_needed = map(analogRead(timer_pin), 0, 1024, min_time_needed, max_time_needed);
 
  // Illuminate the power LED
  digitalWrite(pwr_pin, HIGH);
}
 
void start_countdown() {
  time_start = millis();
 
  digitalWrite(warn_pin, HIGH);
  digitalWrite(siren_pin, HIGH);
}
 
void stop_countdown() {
  time_needed = time_needed - (millis() - time_start);
 
  digitalWrite(warn_pin, LOW);
  digitalWrite(siren_pin, LOW);
}
 
void check_countdown() {
  // If the elapsed time has exceeded the time set by the adjust pot,
  // Turn off the light and siren and set off the pyro
  if(millis() - time_start > time_needed) {
    digitalWrite(warn_pin, LOW);
    digitalWrite(siren_pin, LOW);
 
    digitalWrite(bang_pin, HIGH);
 
    // Delay until the end of time (or reset)
    while(true) {
    }
  }
}
 
void check_finalwarning() {
  // If the timer is nearly finished, flash the warning light
  // If the timer is not nearly finished, make the siren intermittent
 
  static unsigned long last_flash = 0;
  static unsigned long last_bleep = 0;
 
  if((signed long)(millis() - time_start) > (signed long)time_needed - (signed long)warn_time) {
    digitalWrite(siren_pin, HIGH);
    if(millis() - last_flash > 100) {
      digitalWrite(warn_pin, !bitRead(PORTD, warn_pin));
      last_flash = millis();
    }
  }
  else {
    if(millis() - last_bleep > 300) {
      digitalWrite(siren_pin, !bitRead(PORTD, siren_pin));
      last_bleep = millis();
    }
  }
}
 
void loop() {
  static int prev_reading = LOW;
  static unsigned long debounce_time = 0;
  static boolean waiting = true;
 
  // Read the switch state
  int reading = digitalRead(switch_pin);
 
  // Debounce and switch state
  if(reading == HIGH && prev_reading == LOW && millis() - debounce_time > debounce_timeout) {
    debounce_time = millis(); 
 
    if(waiting) {
      waiting = false;
 
      start_countdown();
    }
    else {
      waiting = true;
 
      stop_countdown();
    }
  }
 
  prev_reading = reading;
 
  if(!waiting) {
    // This will make exciting flashy things happen when the countdown is nearly up
    check_finalwarning();
 
    // If the countdown has expired, this will not return
    check_countdown();
  }
}

After I got it all working, I raided the kitchen and mounted the whole thing in a lunchbox. I sanded it down, sprayed it with green acrylic, masked off the lid (poorly) and did some yellow stripes, and gave the whole thing a coat of varnish.

It’s a bit top-heavy but it leans against a tree well enough. I took it out for a test game and it performed pretty well. We put a thermobaric pyro in a tent. It is now an ex-tent.

I’ve started work on its big brother, which will be a more solid and durable prop, and will hopefully look better. More to come on that soon!

Introducing: the airsoft M-COM station

I’ve been playing Airsoft for about a year now. And I’ve been a gamer for a lot longer than that. I was a big fan of Battlefield 2: Bad Company. One of the game modes from BF2BC is Rush – where one team of players has to destroy a box of electronic gear (the M-COM station), and the other has to defend it.

The gameplay is pretty simple – attacking players have to approach the box and hold down a key. That activates a timer, which detonates the box when it expires. While the timer is going, the box sounds an alarm and flashes a light. If defending players can reach the box before it explodes, they can hold down the key to disarm it. This felt like an pretty good thing to take out of computers and put into an airsoft game!

I thought through some of the gameplay and chatted it over with Viking Airsoft (my regular site) and decided it would need to be a bit different from BF2BC.

The game’s timer is quite short – about 30 seconds. This wouldn’t work at all well in an airsoft game. In order to start the timer, the attacking team would have to hit all the defending players. Those players would then need to walk to their base in order to regenerate their life and come back into the game. That’s a round trip of a few minutes, at least. So, in order for them to have a chance to disarm the M-COM and win the game, the timer needs to be long enough for them to regenerate and get back into the game. 10 minutes is probably about right.

In the game, you hold the button down to arm the box. I decided that for the first version, just pushing the button should be enough. Holding the button down would make it quite hard to arm and disarm the box, as keeping good cover and avoiding being shot is rather harder in real life than in a game. This should make the game more dynamic and avoid situations where it’s so hard to arm or disarm the box that players get frustrated.

With all that in mind, I decided to make a prototype to see how hard the electronics would be. I’ll write that up in my next post.

PS: Since starting the project, I’ve discovered that Gunman Airsoft have beaten me to it — they regularly run (awesome) M-COM games. Their approach is a bit different from mine, but definitely fun. Knowing that the game works has definitely spurred me on!

CouchDB + Sinatra

Irritatingly, I can’t get on to the machine that has my nice, populated CouchDB install, and the script for importing from CSV. So I’m a bit stymied for today.

But I did find useful things:

  • A Ruby library for CouchDB which looks like exactly the right thing (Also, its API docs)
  • A bunch of other libraries on the same site which could come in handy
  • And, by just trying it out, I learned that it’s really easy to use regexps with Sinatra:
    get /data\/(.*)/ do |record|
      "I'm fetching #{record}"
    end

Progress on the PIC-powered die

Having realised that I was indulging in some horrible premature optimisation, I stopped trying to make this circuit work using a three-bit output and cracked on with a 4-bit version. The circuit just took a few minutes to build on top of the flashing LED circuit:

I added extra LEDs on RA0-4, and added a push-to-close switch on RB0/INT. I also added a capacitor across the input to even out any voltage fluctuations, and included a diode to save me from my own stupidity – I’ve already zapped more than one IC by getting the polarity wrong.

In the code, I stripped out the delay stuff, and started by just turning all the LEDs on, which was pretty simple:

       movlw	0x0F
       movwf   PORTA

I also had to add some more setup stuff:

	bsf	STATUS,5       	  ; Switch to Bank 1
        bsf     OPTION_REG, INTEDG  ; Set RB0 to generate an interrupt on a rising edge
        bcf     INTCON, INTF      ; clear interrupt flag
        bsf     INTCON, INTE      ; mask for external interrupts
        bsf     INTCON, GIE       ; enable interrupts
 
        movlw   0x01		  ; Port B to input
        movwf   TRISB

Then I turned the bulk of the old program into a sleep routine:

; Turn the LED off 
start
       	movlw	0x00              ; Turn the LED on by first putting
        movwf   PORTA             ; it into the w register and then on the port
	sleep
 
	END                       ; directive 'end of program'

And added some code to the interrupt handling routine:

; isr 
        btfss   PORTB, 0          ; Only run if RB0 is high
        goto    end_isr           ; If it's not high, branch to the end
 
        btfss   PORTA, 0          ; Invert Port A - if bit 0 of Port A is set, 
        movlw	0x0F              ; skip the instruction that makes it high, 
        btfsc   PORTA, 0          ; and vice-versa
        movlw	0x00
 
        movwf   PORTA             ; Put the selected value into PORTA
        bcf     PORTB, 0          ; Clear PORTB now that we're done with it.
                                  ; I don't know if this works or if it's necessary

The full source code is here.

This nearly works. As in, when I press the button, the LEDs do all light up. And then pressing it again turns them off. However, they also come on by themselves and flash very quickly, and then turn off. I assume this is because of some other interrupt being called, and that my starting btfss doesn’t sufficiently differentiate between interrupts caused by RB0 going high, and other sorts of interrupts.

Or, it might be because the pin is not tied to ground when the pin is open, and floating voltage is enough to trigger the interrupt.

More fiddling needed.

Useful things found while doing this:

Making an API for a dataset using CouchDB

I’ve been working on a project to republish some interesting data (more later…) and had planned to use MySQL for storage. However, after figuring out the (somewhat horrid) data structure, I realised this would require >10 tables, and be generally horrible.

So, I wondered if there might be a better option. I started by asking the lazyweb if there was an easy, no-effort option.┬áUnsurprisingly┬áthere wasn’t, so I didn’t make much progress — but then, we started using CouchDB for a work project, and it occurred to me that that might be a good fit. Queryable and efficient for a reasonably large dataset (>140k records) but with a flat, schema-free structure, and supporting revisions natively. Winning.

I installed CouchDB and managed to add some data to it, and view it. I also had a play with design documents, or views, or whatever the right term is (the nomenclature is a bit confusing). I understand MapReduce but I’m not sure how it’ll apply to this, if at all. In any case, this is my todo list:

  1. Display all the records in a gigantic paginated list
  2. Allow all the data to be downloaded in a giant, horrible CSV file
  3. Launch version 1!
  4. Allow records to be searched simply (keyword search over all the fields)
  5. Allow records to be searched complicatedly (with multiple conditions operating on fields separately)
  6. Allow records which are matched to be exported to a slightly less giant but still quite horrible CSV file
  7. [Maybe] Add other data formats
  8. [Maybe] Add email alerts and other useful bits to notify people of changes to things they care about

All of this should be easy, but since I’m finding CouchDB a bit confusing at the moment, I’m not sure it will be — at least, not until I’ve gotten over the initial hump of learning something new. This is as far as I got towards making some actual progress — the lightbulb moment being that I can pass ?key=, ?startkey= and ?endkey= straight into this query without having to do anything special:

{
  "_id" : "_design/example",
  "views" : {
    "by_gender" : {
      "map" : "function(doc) { emit(doc._id, doc.gender)}"
    }
  }
}

I did find some useful resources for CouchDB: