A Practical Overview of Variable Reluctance Sensors (and associated hardware)

That is quite a title, but I’m hoping that it will help others find this post some day when they run in to the same problem that I’ve been running in to for a while – finding a single source of information that provides enough detail about how variable reluctance (VR) sensors work, how to interpret the waveforms generated by them and how to configure and understand the output of the hardware associated with them (specifically the MAX9924).

How VR Sensors Work

Disclaimer: I am not an electrical engineer. Don’t judge me.

In a sentence: VR sensors generate a voltage proportional to the speed of the moving object and the magnetic flux in front of the VR sensor pole. A cutaway shot of one will make that make more sense.

The voltage output is proportional to the speed of the moving object (in the automotive world, a toothed trigger wheel) so the output can range from millivolts to nearly a hundred volts depending on coil configuration and RPM. The output shape is essentially a sine wave – the magnetic flux varies from a minimum when the tooth is farthest away (largest air gap) to maximum when the tooth is centered with the pole piece (smallest air gap). Here’s the theorhetical output of a VR sensor.

And here’s a sample of me waving a screwdriver in front of a VR sensor I have.

Pretty simliar, eh? The key point here is that the slope increases as the “tooth” gets closer to the center of the pole, then swings through zero in to the negative voltage region and then returns to zero – this is the pattern you will see for all VR sensors (when properly connected, if you see the opposite then you’ve probably switched the positive and negative lines of the sensor).

How to Interpret a VR Signal

The most important part of the VR sensor output signal is the zero-crossing from positive to negative voltage. This point is almost exactly (there’s a slight electrical delay resulting from the combination of the sensor and the processing circuitry we’ll talk about in a bit), and for our purposes is, the center of the tooth lining up with the center of the pole piece. Here’s a nifty graph I drew on my whiteboard to illustrate this point.

VR Sensor Signal Conditioning

So now you’ve got this sine wave coming from your VR sensor and you want to use it as an input to your microcontroller or ECU. How do you go about doing that? You go out and buy a chip that does everything for you like the LM1815 or the MAX9924. I’ve used both, they both do the job. ThumperFI has the MAX9924 because after researching the topic on the forums, people seem to like the MAX9924 more than the LM1815 – it seems that it’s better at detecting small signals among other benefits.

I’m not going to go in to the specifics of how the MAX9924 does what it does – not only because I only have a marginally decent knowledge of how it works but because you don’t really care (well, I guess I can’t know that but I’m going to assume that you want to know THAT it works, and how to configure it, rather than the technical details. If you want to know that, go read the datasheet).

The MAX9924 datasheet agrees with what I’ve told you above:

The zero-crossing signal provides true timing information
for engine-control applications. The zero-voltage
level in the VR sensor signal corresponds to the center
of the gear-tooth and is the most reliable marker for
position/angle-sensing applications.

Basically, the MAX9924 generates a square wave that your microcontroller/ECU can read. The falling edge corresponds to the VR signal crossing the adaptive peak threshold (i.e. the voltage goes above a certain voltage) and the rising edge corresponds to the zero-crossing point. Pay attention to that, the rising edge of the MAX9924 signal is the edge that you need to time off of. If you go back and look at the signal diagram I drew above you’ll see that the rising edge lines up with the zero-crossing which lines up with the center of the tooth. Here’s a scope capture of me swinging that screwdriver in front of the VR sensor again only how I have the output of the MAX9924 on ThumperFI overlaid on the VR signal.

You can better understand the adaptive threshold with this picture – notice how the trigger lines up with the signal when it’s slightly offset from zero? That’s the trigger threshold happening – it’s this magic that makes the MAX9924 so great at what it does.


That’s basically what I wanted to outline in this post – how a VR sensor works, what kind of signal it generates and how to process that signal to generate something useful. Any questions you have can be directed below to the comments section.

ThumperTuner – Talking to ThumperFI

ThumperTuner v0.1 (or so it’s called right now) is my software interface to ThumperFI. I’ve gotten to the point where I want to be able to not only test all aspects of ThumperFI (and that includes reading/writing parameters, tables, etc.) but I need an easier way to change parameters while it’s running – and that means utilizing the UART interface that I’m putting the finishing touches on (well, the v0.1 touches).

I decided to use Python, along with wxPython and PySerial, for the software. Why?

As always, there’s an XKCD for that.

On a more serious note, because it’s cross-platform (wxPython and PySerial, too), easy to write and I already sort of know Python. In short, I’m lazy and don’t have the time to learn OS-specific languages right now. AND I’m wanting to spend the least amount of time possible right now developing this so that I can get back to actually testing and developing ThumperFI.

So far I’ve made a good amount of progress for the small amount of Python knowledge I have. I’ve already talked to ThumperFI and have a basic, really terrible looking GUI that functions.

I swear it will look at least a little better than this.

I swear it will look at least a little better than this.

It’s been tough not expanding my scope like crazy, like I always do, but I want to make sure that the program at least functions and has decent error handling (and flexibility for future development – I don’t want to write myself in to a corner and have to refactor everything in the next minor version).

I’ve compromised with myself, I’m not going to let the command interface get too out of hand but I’m writing it in the most flexible way I can think of – there will be a class that interfaces with my program that handles all of the talking, error-handling (ThumperFI uses an ACK/ErrorCode scheme that I might detail in a later post) and data conditioning, and allows me to add more commands later.

Since starting this post I’ve solidified how I’m going to write the command interface, I’ve got the “Hello” and “Engine Parameter” (“H” and “E”) commands working, so I’ll post a snippet of how that works below:

The real magic of this becomes more evident in the command interface class I wrote.

class CommandInterface:
	commands_dict = {
		"H": StringCommand,
		"E": EngineStatusCommand,
		"R": ReadParameterCommand,
		"W": WriteParameterCommand,
		"P": AllParametersCommand,
		"T": TimeCommand

	def SendCommand( self, command, parameter='', data = [] ):
		# Check that the command exists
		self.command = command
		if self.command in self.commands_dict:
			self.Interface = self.commands_dict[command]()

			# Send command string
				self._serial.write( self.Interface.CommandString() )
				# Command sent, time to get the response
				if self.CheckResponseCode():
					return self.CollectData()
					print "Response code failed (not ACK)"
					return False

So far it’s working really well. I want to write and test the more complex methods soon – like WriteParameter and ReadParameter. The cool part is that if I need to add a new command in the future (which I do, BurnEEPROM, and a few others) I just have to add a new sub-class assuming it works similar to the rest of the commands.

I’m also working on the GUI – that’s been interesting. I haven’t really built anything on the front-end for applications before. I’ve done a lot of web programming when I was still building websites for people but that was all HTML/CSS with a PHP backend and some JavaScript/jQuery magic in the front. I’m using wxPython so I’ve just been doing a lot of Googling for “wxPython” + [something related to it] (+[tutorial/example]). That wonderful looking table you see is a small class I wrote to use the FlexGridSizer to build a table based on inserting a row. It keeps track of the value column so you can modify later by just calling something like:

self.parameterPanel.ChangeValue( "map", self._eparam.MAP )

I tested it out and it works really well so far. I need to add the rest of the parameters to that table but I don’t have simulated inputs for most of the rest yet – I’m also working on that right now (whole lot to this whole building-a-fuel-injection-system thing). Once I get the VR simulator finished (along with a MAP simulator) I’ll be in business for firmware development.

To be continued! (whenever the GUI doesn’t look as awful)

Hardware Testing – Update

USB is alive again! After fiddling with the FT232RL and the USB cable some combination of factors brought it back to life. I suspect that the cable may have been the problem all along (goes to show that you should always double check what you think works). I ended up using the clips from my USBee and the AVR ISP to test the cable and it passed…after I swapped USBDP/DM (this was after I’d already cut the end off and soldered on new connections, along with heat-shrinking the more sensitive 26/28 gauge cables – I imagine if I had tested it before it would have failed).

Testing the USB cable for continuity/functionality

One thing to note – every time I tested the USB cable with my multimeter for continuity it worked (and that’s from the male plug contacts to the output pin on the wiring harness). What this tells me is a continuity test of a USB cable is not enough to verify it’s functionality (unless there was some freak event where it would have failed continuity if I’d checked at a different time). I’ll have to ask one of my EE friends or a forum about this but it’s something I’m going to keep in mind in the future when dealing with USB.

I didn’t end up connecting the RESET pin to anything, it’s just floating. Hopefully this won’t cause me any issues with enumeration but if I start having issues that’ll be where I start. In v1.1 that will be fixed so let’s hope it holds up until then.

So, this means that all of the hardware on ThumperFI v1.0 is fully functional! There were only two minor modifications  which will also be fixed in v1.1.

Hardware Testing

ThumperFI v1.0 PCB arrived two weeks ago (2013/11/26). I spent most of that week soldering it together – once I learned how to solder most of the components, that is. Before putting it together, I had soldered a 44-TQFP and 10-uMAX package (0.65mm pitch MAX9924). Now I got to experience soldering SMD resistors, caps, LEDs, etc. Overall it went well, I only bridged two pins and that was on the MAX9924 (as I expected). Here’s the final result:

Kind of poor quality photo but shows the components the best.

While I was at home two weeks ago I was only able to able to test that it powered on – all of my other equipment was at school: my AVR ISP, oscilloscope, etc. So I attached a 9V across 12V_RAW and GND and the PWR LED lit up, so that was exciting.

When I got to school pretty much the first thing I did was set my work bench back up and hook ThumperFI up to my ThinkPad (i.e. “science” computer). It passed the visual inspection, the status LED on the ISP turned green which means my layout was correct. Then I opened up AVR Studio and checked if I could read the voltage and signature of my ATmega644A – which I could!

Now, there’s a decent amount of hardware to test on this thing and I was really busy last week so I spaced out the testing – I’ve since tested functionality of all major components (I haven’t done any intense testing like over-voltage, reverse-voltage, injector flyback, etc.) so I’ll summarize the results here before going in to detail about all of them:

  • ATmega644A – functional, able to load programs, behaves as expected
  • EEPROM – functional after modification
  • FT232RL (USB) – non-functional, “device not recognized”, currently debugging
  • ADC inputs – functional, including conditioned inputs like IAT, CLT and battery
  • Injector, fuel pump and spark signal output – functional, behave as expected
  • VR input – functional, converted 200mA sine wave to square at zero-crossings perfectly
  • Kill switch input – functional, hardware debouncing works well

So other than USB, everything works! Now for the details.


EEPROM did not work, at all, right off the bat. So, I pulled up the schematic for the PCB and drew up a schematic for the EEPROM on my old prototype and noticed immediately that I had put a pull-down resistor on CS (Chip Select) instead of a pull-up (SPI CS is active low, so my chip was active all of the time). To fix this I cut the trace to GND and bridged the 10k resistor to the +5V cap terminal right next to it. Thinking I had fixed the problem I reloaded my test program and ran it again – no dice. I broke out my USBee DSA and carefully tapped off of the SPI lines and logged some data – everything was coming in to the chip correctly but there was no activity on the MISO line – strange. After examining and re-examining my circuits to make sure everything was the same (my test program worked on my prototype) I decided to ask my friend Mike for some help. He went through a number of checks with me based on what I gave him until he asked me what I had done with the ~HOLD pin. I told him it was just soldered to a pad connected to nothing. He said that the ~HOLD pin can tri-state if not pulled up and that can cause all sorts of issues – like the missing MISO signal I had. The SO-8 pitch is just big enough that I was able to bridge a 10k resistor across Vcc and ~HOLD and that immediately fixed the problem.


The second thing I tested after the ATmega644A was the USB capability. And it worked. Perfectly. So I assumed it was going to continue working and moved on to testing the EEPROM which, as described above, didn’t go so hot the first few times. Once I got the EEPROM working I wanted to see if I could output the values I had stored in the EEPROM on the screen using the USB connection. I hooked everything back up and it didn’t work. At all. I got the “device not recognized” message on Windows. I re-examined my FT232RL circuit and compared it to SparkFun’s circuit and didn’t really see anything wrong. Then I read FTDIs debugging tips and took note of the self-powered vs. VBUS powered section. I hadn’t really done either of those, more of some hybrid. And, even better, I had connected VBUS to +5V which makes no sense. So, I left VBUS disconnected and tried it again. No dice.

I ended up e-mailing FTDIs support and a very helpful guy pointed out the self-powered circuit (see Section 6.2 in this document) in the data sheet. So, I painstakingly soldered a 30 gauge wire to the FT232RL chip, made the 3.3V resistor bridge and tested it out. Still no luck. I have tried all of his suggestions as well as anything else I can think of:

  • Make sure your USB cable is good – check
  • Make sure your drivers are installed properly – check
  • Restart – check
  • Make sure all of your solder joints are good – check
  • Plug in USB first, then power on and vice versa – check

So, after exhausting all of the options I could think of I ordered a new FT232RL chip from DigiKey and am waiting patiently to test that out (in case I roasted this one somehow – though the RXLED/TXLEDs still function…). I’m going to try the self-powered circuit first then if that doesn’t work maybe try the VBUS powered. I’ll need to make that decision before I get PCB v1.1 made.

ADC Inputs

There are 6 different analog inputs to ThumperFI:

  1. Battery (BATT)
  2. Intake Air Temperature (IAT)
  3. CooLant Temperature (CLT)
  4. Manifold Air Pressure (MAP)
  5. Throttle Position Sensor (TPS)
  6. Air-Fuel Ratio (O2)

TPS, O2 and MAP are simple inputs with no special conditioning (aside from current limiting resistors, Schottky protection and 0.1uF cap). The BATT, IAT and CLT all have a voltage divider giving me a known voltage or resistance. I tested the BATT and got 9V (which is what I was powering the system with at the time, minus the diode voltage drop). I tested IAT and CLT with a 10k resistor and 2.5V which is also what I was hoping for. All good on that front.

Injector, Fuel Pump and Spark

There are three outputs from ThumperFI (listed above) and all three of them work. Of course, I chased non-existent problems with the injector output for roughly an hour (turns out it was a programming issue, though I don’t really know what caused the issue because the changes I made in the code shouldn’t have actually done anything) – the pin wasn’t set as an output so it’s voltage was kind of floating around and wouldn’t drive the MOSFET. One interesting thing to note that I didn’t expect while debugging was that I would see the transistor base-collector voltage at the 10k pull-down resistor (makes sense, considering I was measuring at a trace connected to the base…). Regardless, it all functions on a basic level – I still need to test the Fuel Pump and Injector outputs under load conditions but it at least drives an LED.

Variable Reluctance Input

The VR input is kind of the most important part of this whole deal – if it doesn’t work I don’t have engine position and speed and can’t manage spark and injections. I saved this guy for last. I knew that the circuit worked before I made the PCB, I had built it using a DIP breakout for the MAX9924 and an actual trigger wheel (attached to a drill and holding the VR sensor). However, I was still a little nervous that for some reason it wouldn’t work BUT it did work, at least it works with a sine wave generated by my MyDAQ. I have a lawn mower engine in the garage with a trigger wheel mounted to it that I’m planning on testing it with (at speed) to confirm full functionality.

100 Hz, 400mA sine wave produces expected output. The MAX9924 is an active-low device so the falling edge indicates a tooth. Photo courtesy of TDS320 over RS232.

On a related note, I also figured out that I can just generate zero-crossings with any waveform and it will work – what that means is I can use the VR signal generator I’ve been working on with my Arduino and somehow level-shift the wave down and BAM I’ve got a functional, adjustable VR generator.

Kill Switch Input

Aside from the USB hardware, the Kill Switch input was the only untested portion of the hardware on this PCB. I grabbed a debouncing circuit from this guide. Works perfectly. It’s got a little longer RC time constant than I anticipated (honestly, I didn’t do the math so it’s probably exactly what it’s supposed to be) but I mean, hopefully you’re not just clicking the kill switch quickly wanting it to stop.

I might adjust this a bit in v1.1 but I’ll have to do some testing with it.

Other Lessons

I haven’t done a lot of extreme case testing yet (over/under-voltage, transients, etc.) and honestly I don’t really want to until I have an easier way of producing this board. In theory everything will function like it should, all of my inputs have Schottky diodes for transients, my regulator has a TVS with a clamping voltage of less than what my regulator can take, as well as a diode for reverse protection (and my regulator actually has quite a few built-in protections as well). My 22uF cap is rated for up to 100V which should pretty well cover any transients. Overall I’m hoping it’s a pretty solid design and I guess testing over the next few months will either prove or disprove that.

Now for some more pictures.

After multiple hours of soldering, it was born.

I mean, come on, I had to put my name somewhere on it.