Tuesday, 25 February 2014

Testing an audio-based toy

It's baby Elvis' birthday soon, so I promised I'd make a personalised toy for him. I thought it would be a doddle, as we've already got all the component parts for a really cool "talking toy" from other projects, it'd just be a matter of sticking them together.

As it turns out, the 16F1825 mcu used to play 8-bit wav files is running at pretty much capacity, meaning there aren't enough cycles left to get a good/consistent analogue read in between grabbing chunks of audio off the sd card. It took a few hours to work this one out, so in the end, the original "alarm clock" SD-reading code has ended up butchered into a dedicated "serial in" audio file playing device, while a second PIC is used to take analogue readings to monitor when the resistance value across two terminals has changed (a brief description of this idea is here)

The end result is pretty encouraging - we've now got a general purpose audio playing circuit, to which you simply send a filename over UART/serial (in DOS 8.3 format) and it finds the file in the root of the (FAT16 formatted) SD card, and simply plays it.

So our secondary chip can spend as long as it likes taking analogue inputs, averaging them out, comparing to previous results and so on - and when it's decided the input value has changed noticeably, it simply generates the filename of a sound effect to play and sends it over serial.

Because the audio playing PIC is using interrupts to keep the audio data arriving just in time, we can use a PORTB change interrupt to tell it to stop playing and listen to the incoming filename. Once a full filename has been received, the audio playing PIC simply opens the file on the SD card and starts to play the new audio sample.


Unfortunately for testing we had only two prepared audio files (the audio has to be turned into 8-bit, mono, 22khz, headerless, raw unsigned PCM data) so the demo sounds like a nasty, tinny, cracked church bell, and a mess of static (it's supposed to be a thunderstorm during the opening bars of NTrance's Set You Free).

Here's the breadboarded circuit on it's own custom PCB, with the two PIC microcontrollers side-by-side, ready to fit inside a nice plastic enclosure:


The "closed version" includes a micro USB socket so that pretty much any phone charger (I've tried it with both iPhone and Android chargers) can be used to provide the 5v power supply (which in turn is fed into an LV33CD 3.3V voltage regulator so the SD card doesn't get upset about running at too high a voltage).



Once the audio starts playing properly, it's not too bad!
Perhaps there's some kind of low-pass filter we can put on the speaker to get  rid of the PWM carrier signal....

Tuesday, 18 February 2014

I got Windows 8.1 to bluescreen

I've had my laptop with Windows 8 (later upgraded to 8.1 with the first release of updates) for a few years now. I don't understand all the anti-Microsoft hate. True, they seem to be following the good-rubbish pattern of old (XP good, Vista rubbish, Windows 7 good, Windows 8 a bit rubbish) but it's not as bad an operating system as the nay-sayers and Linux-lovers would have us believe.

In fact, in two years of owning it, I've never crashed Windows 8. And I do some pretty weird low-level coding, messing about with intercepting Windows messages and sub-classing. Well, it's pretty low-level to me, someone brought up with VB6 and Visual Studio!

The tired old joke about having to reboot every half-day is as irrelevant as it is dated. I never shut the thing down. At the end of the day, simply fold the lid down and the next morning, open it up and start coding again. I can literally go for weeks without rebooting (even when it moans about updates being ready, I usually put them off until I absolutely have to install them).

So it was quite a surprise today, that I had my first Windows 8 BSOD.


It's a much nice shade of blue this time around, but it's still as annoying as days of old - crapping out just as you're about to hit the S-key in a Ctrl+S combination.

From nearly two years without a crash, I've had about three today - ever since I rebooted last night and the latest Windows update was installed. Maybe that's just a coincidence. But it happens every now and again, when I try to burn some code onto an Arduino.

Maybe it's the FTDI driver. Maybe it's Java or Processing (or whatever the Arduino IDE is coded in). Maybe it's the latest Windows update. Maybe it's always been like this and I've just never programmed an Arduino often enough between reboots to have this problems surface before.

But it's getting bloody annoying (not enough to even consider moved to Linux, but annoying all the same!)

433Mhz RF communication between two PIC microcontrollers

The Arduino VirtualWire makes communication between two microcontrollers really easy - simply send some data onto the TX module and it magically appears on the on RX module. We've already built most of this functionality using our Manchester encoding/decoding routines (in both Oshonsoft and SourceBoostC) and can send data over a single wire - all that remains is to replace the wire with our RF modules.

On paper, they're easy enough to work with.
When the TX output line goes high, the RX input goes high. When the TX output line goes low, the RX output line goes low. Simples!




Many years ago we played with RF modules, and they had to be bought (and used) in "matched pairs" - a transmitter and receiver module were "laser tuned" to be exactly on the same frequency. These days, with a flood of cheap RF modules coming out of China via websites like Alibaba and eBay, they are a little more forgiving. There are receiver modules which work with a variety of transmitters - allowing "broadcast" networks to be created - where multiple transmitters can all talk to a single receiver (or multiple receivers can listen to a single transmitter).

Our receiver module is an RR3 and can work across a range of frequencies (the Low Power Device 433Mhz radio range is actually 69 specific frequencies, at 0.025Mhz intervals, between 433.075Mhz and 434.775Mhz)

After hooking up a test rig, it seems that getting rid of our single connecting wire between the two mcus is not quite as simple as sticking a TX module on one end and an RX module on the other!
For testing, we programmed two PICs - one which sent an output pin high when a button was pressed and another which toggled an RGB LED from red to green as the incoming signal went from low to high (and vice versa).

With each press of the button, we were expecting to see a green light, but that doesn't happen. The output from the receiver module stays high, even when we've explicitly turned off the pull-up resistors on our input pins - and even if we stick a 100k pull-down resistor on the input! Yet, interestingly enough, if we press the button often enough, eventually the signal starts to appear:




So what's going on?
We can only surmise from the description of how the 433Mhz receivers work, the idea of having 69 channels in the 433Mhz range, and the fact that one receiver works across a range of frequencies (and the datasheet tells us that the receiver automatically "locks on" to the strongest signal) that something like this is happening:

Firstly, the receiver needs to scan all 69 channels to see which has the strongest signal strength. If we leave our output signal high for a long time, the receiver does not see this not as data activity on the 433Mhz spectrum (since the signal is not alternating) - in the same way running a continuous current through a speaker does not result in sound, it is the change in current which generates a signal (whether audio or data).
We've no idea at what speed the receiver  may be scanning the channels, but at some point, we assume that it's detecting a change on whichever channel (specific frequency) that our transmitter is tuned to. Once this has happened, the receiver knows which frequency to monitor for incoming data, and the response starts to appear on the LED.

This is all supposition, but would explain why there's a delay between us starting to pulse the output on the TX line and it appearing on the RX input pin - and also why, when we send data in tiny, short bursts, it doesn't get picked up on the receiver (which appears to be non-responsive).

With this in mind, we went back to our Manchester encoding example and made a few changes, so that it sends the data repeatedly, with an "out-of-phase" signal between data bursts (to keep the receiver "listening" on the channel our transmitter is receiving):

With these changes in place, the data appears on the receiver module so that it can be read back over UART/serial. We're still losing about 1-in-20 messages but apparently that's about the average for these kinds of cheap TX/RX modules - I guess we'll just have to make sure we send every message twice (with a few milliseconds delay between them) and parse out the duplicates on our receiver end.

But it's nice to be able to have worked something through, from start to finish, and understand (with a few assumptions) what's going on at every step along the way! Next, we'll be shoving these modules into our electronic board game so we can place the board on a table top and send the data wirelessly to a listening computer....


Tuesday, 11 February 2014

Receiving Manchester-encoded data over a single wire using Oshonsoft

Having built a single-wire transmitter, it's time to receive the encoded data.
We're running this code on a PIC16F883 on it's own internal oscillator at 8Mhz. The code includes error checking, but not correction- i.e. if a message received is junk, it's simply discarded. To increase stability, it may be preferable to run the mcu off a crystal, to ensure accurate timing:


Define CONFIG = 0x20c4
Define CONFIG2 = 0x3eff
Define CLOCK_FREQUENCY = 8
OSCCON = OSCCON Or 01110000b
AllDigital

declarations:
     Dim buttondown As Bit
     Symbol button = PORTB.0
     Symbol led_red = PORTC.4
     Symbol led_green = PORTC.2
     Symbol led_blue = PORTC.3
     Symbol data_pin = PORTB.5
     Symbol tx = PORTB.7
   
     ConfigPin PORTC = Output
     ConfigPin button = Input
     ConfigPin data_pin = Input
               
     Dim prev_pin_state As Bit
     Dim received_bit As Bit
     Dim last_received_bit As Bit
     Dim received_byte As Byte
     Dim bitflag As Bit
     Dim bits_received As Byte
     Dim bytes_received As Byte
     Dim preamble_count As Byte
   
     Dim receive_buffer(6) As Byte
     Dim state As Byte
     Dim k As Byte
         
init:
     PORTC = 0
     OPTION_REG.7 = 0 'enable pull ups on portb (RBPU)
   
     'start up serial output
     '(we'll use software serial for ease when running with
     'the PicKit2 programmer)
     ConfigPin tx = Output

     High led_blue
     WaitMs 255
     WaitMs 255
     Low led_blue

     Gosub setuptimer1
     Serout tx, 9600, "Ready to receive..."
         
     'start up the state machine
     state = 0
     'our inputs have pull-ups on them so the prev_pin_state
     'for the dataPin should be high be default
     prev_pin_state = 0
   
loop:

     Select Case state
   
           '---------------------------------------------
           'just idling to see if we've had any data yet
           Case 0
           '---------------------------------------------
           If data_pin = 0 And prev_pin_state = 1 Then
                 'hey up! something's pulled the data pin low,
                 'it could be the start of a preamble
                 state = 1
                 bitflag = 0
                 received_byte = 0
                 bits_received = 0
                 bytes_received = 0
                 last_received_bit = 0
                 preamble_count = 0
                 Gosub ledsoff
                 Gosub starttimer1
                           
           Else
                 'I suppose we could see if the user has pressed a button
                 'for testing
                 Gosub getinput
                       If buttondown = 1 Then
                       Gosub flashled
                 Endif
           Endif
           prev_pin_state = data_pin
   
           '----------------------------------------------------
           'something's twitched on the data pin line, so better
           'receive some data in
           Case 1
           '----------------------------------------------------
           'this is handled with the timer1 interrupt handler

           '--------------------------------------------
           'end of transmission or data receive time out
           Case 3
           '--------------------------------------------
           'validate the output
           k = 0
           k = k Xor receive_buffer(0)
           k = k Xor receive_buffer(1)
           k = k Xor receive_buffer(2)
           If k = receive_buffer(3) Then
                 'cool, checksums add up
                 Serout tx, 9600, receive_buffer(0)
                 Serout tx, 9600, receive_buffer(1)
                 Serout tx, 9600, receive_buffer(2)
                 Serout tx, 9600, receive_buffer(3)
           Else
                 High led_red
           Endif
         
           state = 4
               
           Case 4
           prev_pin_state = 0
           state = 0

     EndSelect
Goto loop
End

getinput:
     buttondown = 0
     If button = 0 Then
           'debounce the button input
           WaitMs 10
           If button = 0 Then
                 While button = 0
                       'do nothing
                 Wend
                 buttondown = 1
           Endif
     Endif
Return

ledsoff:
     Low led_red
     Low led_green
     Low led_blue
Return

flashled:
     Gosub ledsoff
     WaitMs 50
     High led_red
     WaitMs 200

     Gosub ledsoff
     WaitMs 50
     High led_green
     WaitMs 200
   
     Gosub ledsoff
     WaitMs 50
     High led_blue
     WaitMs 200

     Gosub ledsoff
Return

setuptimer1:
     T1CON = 00001000b 'set bit zero to start the timer

     INTCON.GIE = 1
     INTCON.PEIE = 1

     PIE1.TMR1IE = 1
     PIR1.TMR1IF = 0
     T1CON.TMR1ON = 0
Return

preloadtimer1:
     'at 8mhz, fosc/4=2Mhz (2,000,000)
     'our radio modules are rated up to 4khz
     'so let's use about half that, just to be safe
     'so 2m/2k = 1,000
     'we want our counter to go up to 1,000 then
     'send another pulse
     '65535-2000 = 63535 (0xF82F)
     '65535-1000 = 64535 (0xFC17)
     TMR1H = 0xf8
     TMR1L = 0x2f
Return

starttimer1:
     'in case the timer is already running, stop it
     T1CON.TMR1ON = 0
     'reset the timer1 interrupt flag
     PIR1.TMR1IF = 0
     'preload with the correct value
     Gosub preloadtimer1
     'start the timer
     T1CON.TMR1ON = 1
Return

stoptimer1:
     'stop timer1
     T1CON.TMR1ON = 0
     'reset the timer1 interrupt flag
     PIR1.TMR1IF = 0
Return

getbit:
     If bitflag = 0 Then
           'this is fine
           prev_pin_state = data_pin
           bitflag = 1
     Else
           'in the second half of any bit, there
           'should be a transition on the data line
           '(if there's not, we're out of sync)
           received_bit = 0
         
           If prev_pin_state <> data_pin Then
                 'this is awesome. We've had a transition
                 'on the second half of the bit, just as we expect
                 If data_pin = 0 Then
                       'this is a one bit
                       received_bit = 1
                 Endif
           Else
                 'something has gone weird
                 'maybe we're just out of sync?
                 High led_red
           Endif
         
           prev_pin_state = data_pin
           bitflag = 0
         
           'now decode the bit that's just come in
           If preamble_count < 32 Then
                 '--------------------------------------------------
                 'we're still waiting for 1010101 etc followed by 11
                 '--------------------------------------------------
                 If last_received_bit = 1 And received_bit = 1 And preamble_count > 8 Then
                       'here's our 11 at the end of the preamble!
                       preamble_count = 99
                       received_byte = 0
                       bits_received = 0
                       bytes_received = 0
                       High led_blue
                 Else
                       'we're looking for a transition from 01 or 10
                       If last_received_bit <> received_bit Then
                             preamble_count = preamble_count + 1
                       Else
                             'we've had two bits of the same value but not
                             'preceded by 010101 etc so reset the preamble count
                             preamble_count = 0
                       Endif
                 Endif
           Else
                 '-------------------------------------------------------
                 'we're into the meaty stuff now, actually receiving data
                 '-------------------------------------------------------
                 If received_bit = 1 Then
                       'write this bit to the received byte value
                       received_byte = received_byte Or 0x01
                 Endif
               
                 bits_received = bits_received + 1
                 If bits_received >= 8 Then
                       'put the received_byte into the bytebuffer
                       receive_buffer(bytes_received) = received_byte
                       bytes_received = bytes_received + 1
                       received_byte = 0
                       bits_received = 0
                     
                       If bytes_received >= 4 Then
                             Gosub stoptimer1
                             state = 3
                       Endif
                 Else
                       'prepare for the next incoming bit
                       'shunt the received_byte value along
                       '(whether we received a one or a zero)
                       received_byte = ShiftLeft(received_byte, 1)
                 Endif
           Endif
         
           last_received_bit = received_bit
         
     Endif
Return

On Interrupt
     Save System

     If PIR1.TMR1IF = 1 Then
           'clear the interrupt flag for timer1
           PIR1.TMR1IF = 0
           Gosub preloadtimer1
           Gosub getbit
     Endif
Resume

We've tested the receiver a few times and get consistently good results.
However, the RC internal oscillator in a PIC is accurate to about 1% but can drift over time and with changes in temperature. If you're likely to have the sender and receiver in two different places (we're planning on using these routines to send data via RF link between two controllers in different rooms in the house) it might be worthwhile adding a precision cut crystal, just to ensure exact timing between the two mcus.


Manchester Encoded Transmission using Oshonsoft

We've been playing about with one-wire Manchester-encoded data again, only this time using (our preferred) Oshonsoft PIC compiler. We've been using pretty much the same techniques as before, only this time, in BASIC rather than C.

Here's the first part, transmitting manchester-encoded data over a single  wire. It's based on our earlier SouceBoost code which we've already proven to work.

This code has been set up to run on a 16F882 (it was just one of the PIC microcontrollers we had lying around) and the only unusal thing of note is that we're using the internal oscillator at 8Mhz, not the default 4Mhz (see the OSCCON command at the start of the code). Other than that, this code should be useable by pretty much any of the 16F range of PICs (you may need to change a few pin assignments



Define CONFIG = 0x20c4
Define CONFIG2 = 0x3eff
Define CLOCK_FREQUENCY = 8
OSCCON = OSCCON Or 01110000b
AllDigital

declarations:
     Dim buttondown As Bit
     Symbol button = PORTB.0
     Symbol led_red = PORTC.4
     Symbol led_green = PORTC.2
     Symbol led_blue = PORTC.3
     Symbol data_pin = PORTC.5
   
     ConfigPin PORTC = Output
     ConfigPin button = Input
   
     Dim testcount As Byte
     Dim heartbeat As Bit
     Dim databuffer(6) As Byte
   
     Dim byteindex As Byte
     Dim bytetosend As Byte
     Dim bitssent As Byte
     Dim bitflag As Bit
     Dim sendnextbyte As Bit
     Dim sending As Bit
     Dim pinstate As Bit
   
     Dim i As Byte
     Dim k As Byte
   
init:
     PORTC = 0
     OPTION_REG.7 = 0 'enable pull ups on portb (RBPU)
     Gosub setuptimer1
   
     High led_blue
     WaitMs 255
     WaitMs 255
     Low led_blue
   
loop:
     Gosub getinput
     If buttondown = 1 Then
           databuffer(2) = 0x26
           databuffer(3) = 0x11
           databuffer(4) = 0x75
           Gosub senddata
     Endif
   
Goto loop
End

getinput:
     buttondown = 0
     If button = 0 Then
           'debounce the button input
           WaitMs 10
           If button = 0 Then
                 While button = 0
                       'do nothing
                 Wend
                 buttondown = 1
           Endif
     Endif
Return

ledsoff:
     Low led_red
     Low led_green
     Low led_blue
Return

flashled:
     Gosub ledsoff
     WaitMs 50
     High led_red
     WaitMs 200

     Gosub ledsoff
     WaitMs 50
     High led_green
     WaitMs 200
   
     Gosub ledsoff
     WaitMs 50
     High led_blue
     WaitMs 200

     Gosub ledsoff
Return

setuptimer1:
     'T1CON = 0x30
     T1CON = 00001000b 'set bit zero to start the timer

     INTCON.GIE = 1
     INTCON.PEIE = 1

     PIE1.TMR1IE = 1
     PIR1.TMR1IF = 0
Return

preloadtimer1:
     'at 8mhz, fosc/4=2Mhz (2,000,000)
     'our radio modules are rated up to 4khz
     'so let's use about half that, just to be safe
     'so 2m/2k = 1,000
     'we want our counter to go up to 1,000 then
     'send another pulse
     '65535-2000 = 63535 (0xF82F)
     '65535-1000 = 64535 (0xFC17)
     TMR1H = 0xfc
     TMR1L = 0x17
Return

starttimer1:
     'in case the timer is already running, stop it
     T1CON.TMR1ON = 0
     'reset the timer1 interrupt flag
     PIR1.TMR1IF = 0
     'preload with the correct value
     Gosub preloadtimer1
     'start the timer
     T1CON.TMR1ON = 1
Return

stoptimer1:
     'stop timer1
     T1CON.TMR1ON = 0
     'reset the timer1 interrupt flag
     PIR1.TMR1IF = 0
Return

senddata:
     sending = 1
     testcount = 0
     High led_blue
   
     databuffer(0) = 10101010b
     databuffer(1) = 10101011b
   
     'databuffer 2,3,4 should already be populated
     'so create a checksum for the three values
     databuffer(5) = 0
     databuffer(5) = databuffer(5) Xor databuffer(2)
     databuffer(5) = databuffer(5) Xor databuffer(3)
     databuffer(5) = databuffer(5) Xor databuffer(4)

     'send the data: the interrupts will take care of
     'moving this (blocking) function along
     Low data_pin
   
     byteindex = 0
     bytetosend = databuffer(0)
     bitssent = 0
     bitflag = 0
     sendnextbyte = 0
   
     Gosub starttimer1

     'this is a blocking Function
     While sending = 1
           'do nothing
     Wend
     Low led_blue

Return

sendbit:
     'this is where we wiggle the pin
     If bitflag = 0 Then
           'this is the first half of sending:
           'work out which way the Bit should be sent
           '(start low For Bit value 1, start high For bit value zero)

           k = bytetosend And 10000000b
           If k = 0 Then pinstate = 0 Else pinstate = 1

           'set the pin according to the bitState value
           If pinstate = 0 Then Low data_pin Else High data_pin
         
           'change the bit-flag so we will invert the output on the
           'next timer1 interrupt
           bitflag = 1
         
     Else
   
           'it doesn't matter which way the data pin is, just toggle it
           pinstate = Not pinstate
           If pinstate = 0 Then Low data_pin Else High data_pin

           'keep track of the total number of bits sent
           bitssent = bitssent + 1
           If bitssent >= 8 Then
                 sendnextbyte = 1
           Else
                 'move the value in the byte to send buffer
                 '(shift right)
                 bytetosend = ShiftLeft(bytetosend, 1)
                 sendnextbyte = 0
           Endif
         
           'change the bit-flag for the next timer1 interrupt
           bitflag = 0

     Endif
   
     If sendnextbyte = 1 Then
           byteindex = byteindex + 1
           If byteindex >= 6 Then
                 'we've sent our six bytes from buffer(0-5)
                 'so chill out and have a brew now
                 Gosub endtransmission
           Else
                 'prepare the next byte for transmission
                 bytetosend = databuffer(byteindex)
                 bitssent = 0
                 bitflag = 0
           Endif
           sendnextbyte = 0
     Endif
         
Return

endtransmission:
     'first things first, kill the timer
     Gosub stoptimer1
   
     'now wait a timer-width before pulling the line low
     WaitMs 2
     Low data_pin
   
     'kill the sending flag
     sending = 0
   
Return

On Interrupt
     Save System

     If PIR1.TMR1IF = 1 Then
           'clear the interrupt flag for timer1
           PIR1.TMR1IF = 0
           Gosub preloadtimer1
           Gosub sendbit
     Endif
Resume

By running the clock at 8Mhz instead of 4Mhz, we've got about 1,000 clock instructions between Timer1 intervals. But even at the default 4Mhz, there would be 500 instructions between Timer1 intervals, which should be plenty to do the processing required between "ticks".

As before, we ran the single wire into pin4 of our PicKit2 programmer and ran the built-in Logic Analyser tool. This time, we wrote our data values in hex, so that they are easier to read back at the other end.



Amazingly, it worked first time!
Let's hope that converting the receiver code from SourceBoost C to Oshonsoft BASIC goes as smoothly!

Sunday, 9 February 2014

Espruino - javascript on a microcontroller?

I'd forgotten all about this, until a parcel arrived yesterday, from Singapore or somewhere like that. I've been hitting eBay quite hard lately, so it could have been any number of things. But it turned out to be my Kickstarter reward for a project I backed in October last year.


Espruino is a fancy microcontroller which supposedly lets you code in Javascript and use a web-based editor to compile code. "Burning" the code to the chip is no more complex than copying it to a micro sd card and shoving it in the slot.

This is likely to be one of those one-off, shove it in a drawer and we'll come back to it sort of things (like the old mbed platform of a few years ago). But when there's another rainy afternoon with nothing much going on, maybe it'll get taken out for a test-drive for a few hours (unless, like today, there's international rugby on the telly - then it'll just have to sit in the drawer and wait 'til another day!)

Hall sensor tester

One of our board sections seems to have a fault.
No matter how many times we introduce a playing piece over just one square of just one board section, there's no data coming back from it.

It's not a design fault, because other, identical, board sections work just fine - it's a problem particular to this board. We thought it was going to be an easy fix: after all, there are only three pins on each SOT-23 hall effect sensor. Simply reflow the solder on each of the pins, and we're done!

Except it didn't quite work.
We tested continuity between the ground pin and our ground plane - that was fine. We had continuity between the power trace and pin 1 on the  sensor; there was even continuity between the output lead and the PIC input pin. Everything looked fine, it just didn't work!

Removing tiny little SMT components from homebrew boards can easily result in knackered traces (even with professionally made boards, it's easy to lift the pads by overheating them, but homebrew boards are particularly susceptible). So before we even take the "broken" sensor off, we need to make sure that the sensor we replace it with is working.

So we knocked up this quick hall sensor tester:


It's nothing more than a 3v battery supply and an LED. The "output" pin of the hall sensor is connected to the cathode (ground) pin of the LED and pulled up to 3v through a 1K surface mount resistor. When a hall sensor is held down on the SOT-23 pads and a magnet introduced, the hall effect sensor pulls the output pin low, and the LED lights up.


Here's our LED lighting up as the magnet on the tweezers triggers the hall effect sensor. These are super-bright LEDs - it's just the flash from the camera severely dampens the effect!

So at least now, when we come to replace our "broken" hall effect sensor, we can be sure that we're replacing it with a known working one (and we also have a way of testing to see if the other one really is broken, or there's some other problem stopping it from working) .

Wednesday, 5 February 2014

Soldering SMT components by hand

A few people have asked about how we soldered up our working board game sections a short while back. Specifically, they asked about our pick-n-place machine and reflow oven (as it was assumed that this is the only way to successfully use SMT components in a homebrew/hobby project).

The truth is, we hand-soldered all of the components on the board.
Although some people have had success with tiny 0806 sized components (we've soldered a few in the past) generally we try to stick to 1206 sizes for resistors and capacitors, SOIC for ICs and microcontrollers and SOT-23 (or SC59) for other silicons; these sizes are perfectly manageable (if not a little fiddly) for soldering by hand.

If you're working with home-made boards with no solder resist layer (as we do) you might find tinning the board with some plating powder helps with getting the solder to flow just where you want it to. We used to do this with every SMT board, but over time, just got into the habit of brightening up the copper with a bit of wire wool and soldering the entire board in one go, before it has time to tarnish.

The first trick with hand-soldering SMT components is flux. Lots of it. Get it all over the pads, inbetween the pads and everywhere. We use a non-clean, non-corrosive flux dispensing pen from Maplin. It's quite expensive, but ours has been going for a few years now and we use flux on everything so it does last a good long while.


Our next secret weapon for hand-soldering tiny components is solder paste. Put your 0.2mm reels of solder and your super-fine iron tips away: a regular pointy iron tip and solder paste is all you need!

You can get solder paste easily from eBay, but be careful you don't end up with a tub of solid flux-jelly (it's often also described as soldering paste). The stuff to look out for is a silvery grey colour - if it's an off-white or orangey-yellow colour, it's flux. Solder paste is a mix of solder and flux combined. You can buy both leaded and lead-free versions of solder paste. For one-offs and prototypes, leaded solder paste is ok. We try to use lead free where possible.

Beware of misleading adverts like this one:



The description clearly states it is lead-free - and then also lists the contents as Sn63/Pb37!
For those of you who never listened during chemistry at school (and don't play along to the science rounds during Pointless on BBC1) Sn is the chemical symbol for tin, and Pb is the chemical symbol for..... lead.
So here's some amazing stuff: lead-free solder paste, with 37% lead. Excellent!

Make sure all the pads (and if you're using a homebrew board, between the traces) get a good coating of flux. If you're using professionally made PCBs, you can apply the solder paste quickly and easily as a "slug-trail" across all the pads


If you're using homebrew boards, it's very easy to create solder-bridges across the tiny gaps between the SMT pads (or even across traces, if you've routed any between the pads of your components. For homebrew boards, we tent to put the paste only where we want it to end up

Here you can see we use a small jewellery screwdriver to deposit tiny dots of paste, just onto the pads we want to solder, working just a few components at a time

You can use a stencil to apply the paste onto just the pads (some people do this, even for professionally made PCBs) but for hand-soldering, it increases the chance of you resting your hand (or arm) into some paste and smearing it across the rest of the board! We tend to work on three or four components at a time.

Using some small nosed tweezers, carefully place the component into the solder paste on the board. This is the really time consuming bit. One twitch of a shaky hand, and you can easily spread the solder paste from the pads onto the surrounding traces (if you do this, remove the component, clean up the paste, reapply flux and start again!)

With the component in place, it's ready to start soldering.


Hold the component down using the tip of the tweezers. This is important. In a reflow oven, all of the solder melts at pretty much the same time, and the surface tension in the liquid solder can realign badly placed components, and they end up perfectly in the middle of the pads. When hand-soldering, we're melting the solder one pad at a time - which will try to pull the component towards the molten solder. Always make sure the component is held firmly before soldering.

With the component in place, and held down by the tweezers, touch the iron between the pads and any surrounding traces. This performs two tasks - firstly it melts the solder and gets the component to stick, but it also clears away any excess solder paste from between the pads/traces. If you've plenty of flux on the board, the excess solder will simply be burnt away.


A regular sized iron tip will be fine, even though the components are relatively small. In fact, using a tiny SMT-specific tip might actually be more difficult - a simple, conical tip that is wide enough to heat both traces as you run it between them is going to be much more efficient than a tiny little tip which can only heat one trace at a time.

For our homebrew boards, we use 0.5mm traces (it helps when etching in "old" ferric chloride or when etching times are prolonged) and occasionally go as low as 0.38mm when running traces between SMT pads. Even with relatively chunky traces, we can use this method to successfully solder 1206 resistors over the top of traces without any bridging.

Once you've done a few small components, it's just a case of getting into a rhythm - but take your time on each one!


With practice, we found that a board like the one above, with 12 x sot-23 and 14 x 1206 resistors can be soldered up by hand in about 20-30 minutes. Obviously this is nowhere near production speeds - but it does prove that SMT components can be used for home-made, one off prototypes.

We've got quite a few boards to solder up over the next few nights, so we're going to be perfecting our hand-soldering technique, using this very method!

Tuesday, 4 February 2014

Sourcing components

Here at Nerd Towers, we love Farnell.
They're not always the cheapest, but their website is easy to use, very rarely do we fail to find the component we're looking for - even if there's no exact match, their website is intelligent enough to offer alternatives. Compared to something like the Rapid website, it's miles ahead (Rapid have this amazing ability to drag in the most irrelevant results into your search, offering 500+ components to choose from instead of five or six!).

Farnell also offer amazing service. Order something as late as 7pm and it'll almost certainly be on your doormat the next morning. But yesterday we were seduced by their main rival RS Components. It's only a dalliance - maybe nothing more than a fling, or even a one-night-stand, but it did make us re-consider how we source our components.

It started with hall sensors.
Farnell sell the AH180 for 61p each, or 50p each if you buy a few.
RS sell the same thing for 26p, or 22p each if you go for a hundred or more.
That's less than half the price of Farnell! Wow.

For our digital board game, we're also using some PIC microcontrollers. We chose the 16F570 because it's the cheapest 28-pin PIC on the Farnell website (trust us, Nick spent hours going through just about every one our compiler supported, to find the cheapest one that still did everything we needed it to!).

The 16F570 from Farnell is 95p, or 68p if you buy in bulk.
But since we're already buying from RS (and they offer next day delivery up to 8.30pm) we thought we'd see if they offer these PIC chips at a lower price too. They don't.

A 16F570 (quite a low-end microcontroller in PIC world) costs an incredible £9.80 from RS Components! More than ten times the most expensive price from Farnell! Double-wow.

So today's lesson is, simply, to shop around.
Sure, you can buy components in bulk from China, but they take six-to-eight weeks to arrive and you need to buy a bucketful at a time (plus the failure rate is higher, and there's no real chance of returning faulty items). But we tend to stick to "local" suppliers - or at least, UK-based, who can get our goods to us the next day - before we've had time to get bored with what we're doing and move onto something else.

So the trick is - much like supermarket shopping - to not buy everything all from the same place.
After all, that's how the supermarkets make their money. The more astute shopper might buy their bread from one, milk from another and so on, to make sure they get the best deals. For most of us it's simply impractical to drive from one to the other, to save a few quid on the weekly shop - and that's what supermarkets rely on, to rake in their profits.
What is quite alarming is the extent to which online shops seem to be doing the same thing. And given that they deliver your goods right to your door, there's no real cost in doing some comparison shopping.: an alternative supplier is only a click away, not on the other side of the city, or in the next town.

So, for many of you who are  already doing this, well done. But if, like us, you tend to stick to one known supplier, because it's who you know (and they make the checkout process super-easy) maybe it's time to consider alternatives. We certainly will, from now on!



Electronic board game- pieces etched

Steve challenged us to have a demonstrable working board game by the next BuildBrighton Open Night. While we probably won't have the full RF-to-UART-to-Ethernet part working, it would be nice to at least build an interesting looking board from a few different sections. So, last night, we set about etching and cutting a load of copper board:


We didn't get everything finished (there are still quite a few mdf shapes left to cut) but there's an interesting selection of just about every type of board game section ready for soldering. So far we've got

  • straight corridor bits (6 across, 2 down - or 2 across, 6 down!)
  • right angled corners (4 across, 4 down)
  • 4 x 4 room with one exit
  • 4 x 4 room with two exits
  • 4 x 4 room with four exits (not  sure why we missed out a 3-exit version)
  • crossroads (6 across, 6 down)
  • t-junction (6 across, 4 down)

And, of course, our two original corner sections (which just need new mdf tops cutting for them, to make them compatible with this slightly wider range of rooms and corridors).

Hand-etching so many sections (and drilling, we've yet to drill the boards) is a pain. And they've yet to be populated with tiny little SMT components. We knew it would be and, when challenged, immediately thought about CNC drilling the boards and building some kind of rig for printing the PCBs. In time, that's probably how we're going to make these boards, particularly if they need to be made in any kind of volume. But for now, we've enough to get ready for next Thursday evening!

Populating them is a scary thought too. We've got a load of hall sensors on their way from RS (less than half the price than from Farnell) and there are about 160 to solder onto various boards. We did wonder about building an x/y frame to assist (even if it was manually controlled using a joystick or computer keyboard, it would be easier than trying to place all these tiny SOT-23 components using just tweezers and a steady hand!) but it's unlikely that we'll have time to build that, and use it to solder the components in time for  next Thursday.

So it looks like we're in for a few late nights.
Pass the solder paste and let's get going!