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) .