Introduction: Designing a Custom Automotive Controller

If you're like me, you probably invest too much time into projects that others may think have a very distant payoff, or little payoff at all! Over the last several months, I've been working on such a project. The main project is a Head Up Display (HUD) for my car that will show basic vehicle information (such as speed) alongside in depth data relevant to diagnostics and off road driving. I was dragged into a bit of a rabbit hole when I got hooked on the idea of including a graphic of the vehicle's drivetrain that displays, in real time, exactly which wheels are slipping when traction is poor (this is an AWD vehicle). This rabbit hole lead me to design a control board I'm calling Telemetry Master.

Wheel Speed Sensors

To create the functionality I wanted, I decided using my vehicle's existing wheel speed sensors was the logical choice. Wheel speed sensors can vary in design and operation. My particular sensors are mounted in each wheel hub assembly and are permanently magnetic so they induce a magnetic field in an exciter ring that rotates with the output flange of the hub. The exciter ring is a metal disc with 60 teeth on its periphery. The intensity of the magnetic field varies by how much ferrous material it is permeating, so as the teeth of the exciter ring pass by the sensor, a sine wave of alternating current is induced back across a coil within the sensor. The frequency of the output wave is proportional to the speed at that wheel.

For my vehicle, wheel speed sensor data informs how the Anti-Lock Brake System(ABS) needs to manage traction control and braking (see note below). My goal when designing Telemetry Master was to access this same data without interrupting the ABS computer's access to it.

Producing the Board
To create the Telemetry Master board, I will first discuss a method referred to in the RepRap community as “Scratch n’ Etch.” This method involves using a CNC platform like a 3D printer to scrape away a mask coating from the copper board with a sharp scribe point. The exposed copper can then be etched away, leaving isolated traces. In later boards, I experimented with the traditional Toner Transfer method.

A Note on Vehicle Safety
Tinkering with electrical systems related to your vehicle's ABS may not be a great idea on daily drivers or high performance vehicles where traction control is important to handling.

Step 1: Testing the Concept

Before investing the time in designing and prototyping an entire printed circuit board, I wanted to determine if the critical functionality I was looking for in my board was achievable. I tested whether my board could accurately read low frequency waves by using an Arduino Uno and a library called FreqMeasure. The library’s configurability is somewhat limited depending on the hardware used, so I could only read frequencies on the number 8 IO pin.

I used an example program from the FreqMeasure library and modified it to read for a specific frequency range and flash an LED only when those frequencies are read. I also had success with reporting the frequencies over serial to my Raspberry Pi and my main computer.

#include <FreqMeasure.h>

void setup() 
  pinMode(13, OUTPUT);

unsigned long sum = 0;
int count = 0;
float frequency = 0;

void loop() 

  if (FreqMeasure.available()) 
    // average several reading together
    sum = sum +;
    count = count + 1;
    if (count > 30) 
      frequency = FreqMeasure.countToFrequency(sum / count);
      if(frequency > 225 && frequency < 235)
        digitalWrite(13, HIGH);
        digitalWrite(13, LOW); 
      sum = 0;
      count = 0;    


To create the low frequency test waveforms, I used a well known audio program called Audacity. Here's how I generated and read the waveforms:

  • Connect a 3.5mm audio cable to your computer’s audio port.
  • Within Audacity, select the ‘Tone’ option from the ‘Generate’ drop down and choose the waveform and frequency you want.
  • Unless you have a separate audio amplifier or other means of amplifying the waveform, make sure to set the amplitude at the maximum so the voltage is high enough to be read by the Arduino.
  • Generate the tone then connect a jumper wire from a signal line of the audio jack to the digital input pin on your microcontroller.
  • Connect another jumper between your microcontroller’s ground and the ground line of the audio jack.
  • Press play on your audacity track; if your microcontroller doesn’t signal to you the frequency was detected, you may still need to set your playback volume higher within audacity. Keep in mind the maximum input voltage your controller can take if you try to amplify the signal externally!

This was just a quick and dirty test, but it confirmed that the ATmega328P, the microcontroller on the Arduino board, can read frequencies reasonably accurately with the FreqMeasure library and the right hardware.

Step 2: Designing the Circuit

Knowing my idea was viable, I jumped straight into Autodesk's EAGLE to get started. I would actually suggest others build up their entire circuit idea on a breadboard before going into software, this allows you to test and make changes to your layout much more quickly.

It took a handful of board designs before I had one that I could use. From the process, I learned what I think are some important lessons for designing this kind of board and working with EAGLE:

Mind your layers

EAGLE allows the user to mirror components and switch their layer to reflect whether they will be mounted on the top or bottom of the PCB. When using both through hole and surface mount components on only a single layer, figuring out how footprints need to be mirrored and laid out can be a pain. The different layers are a tool to help manage this sort of complexity. Make consistent use of them and, unlike me, you won't have to scrap a board after having a surface mount component incorrectly mirrored.

Find Existing Hardware

One early version of my board used transistors to switch the 4 sensor signals into a common input line to read each signal individually. This arrangement wasn't very space effective, not to mention it didn't work at all in the way I set it up. After some basic research, I realized a common component called a multiplexer was designed for exactly that application. Dropping in an existing IC like this multiplexer saved me a lot of development time and board space.

Think Ahead

Developing your own electronics can be tedious, so make it worthwhile by leaving open options for upgrades. For my design, I routed all available IO to pin headers to allow for future sensor additions. Unfortunately, thinking ahead also means expecting the occasional error. If you have a long string of components connected in series, I would suggest placing test pins or pads at each stage in the circuit so you you can pinpoint exactly where things are going awry when a malfunction does occur.

Keep Manufacturing in Mind

For particularly finicky production methods, expect to spend lots of time refining your circuit layout to get the best results. If you plan to use prototype board services or conventional hobbyist production methods like toner transfer or photo etching, you'll probably have more freedom when it comes to deciding trace widths and routing.

For boards designed for the scribing method I will describe later, I tried to remember the following:

  • Maximize trace width. Traces 0.012” wide or wider scribe consistently accurately- traces as thin as 0.008” wide are achievable, but typically not reliably.
  • For especially narrow traces, paths making 90° turns maintain width more accurately than 45° paths.

Step 3: Creating G-Code in FlatCAM

If you want to use the “Scratch n’ Etch” method of making your PCB, you'll have to turn its geometry into the motions for the CNC platform to run. FlatCAM is a great free program for this purpose. It's actively maintained and more than capable of generating the G-Code the simple scribing process needs.

Exporting a Gerber from EAGLE

  • In your board layout window, select "CAM Processor"
  • With the CAM Processor open, preview the layer you want to scribe to make sure all the geometry you need is included. Remember this scribing process requires you to manually drill holes, so you won't need to export any hole locations.
  • If geometry is missing from your layout, you may still have some items in the wrong layer. You can change object layers by using the "Change" wrench and selecting the layer you want to move objects to.
  • When you're certain you have all the traces and solder pads you need, ensure your units are set to millimeters.
  • Choose "Export File" to determine what path to save the Gerber to and then click "Process Job"
  • You should be greeted by a message telling you the export was a success.

Generating G-Code in FlatCAM

Loading the Gerber

  • Select "Open GERBER" and navigate to the folder you exported your Gerbers to, then select the specific layer you need to scribe. The layout will then be displayed in green in the plot area.
  • To move the layout to the coordinates where you want it to scribe from, press "M" on your keyboard and click on the corner of your layout. You can now drag your layout and click again to place it. I would suggest placing the layout starting at the origin, you'll be able to adjust the start location on the printer later.

Creating Isolation Parameters

  • Select your Gerber's name in the "Project" pane, and click on the "Selected" pane. The top Isolation Routing settings are relevant to us:
    • Tool Diameter - Since I used a scribe having a theoretically very small point, this measure determines how close that scribe point is brought to the exterior of board geometry. I used 0.2mm, but you may need to adjust this number and observe the output to ensure this won't leave messy islands between traces.
    • Passes - The number of passes you want performed. I find traces are typically well isolated after 3 passes.
    • Pass Overlap - How much each of the passes will overlap the previous, as expressed by a fraction of the tool diameter. I would recommend keeping this near or above 0.90.
  • I choose to Combine all the passes for simplicity.
  • Once you press "Full Geo," isolation passes will be generated and outlined in red.

Generating G-Code

  • After generating isolation geometry, you should be faced with new options for generating the G-Code. We're concerned with the following settings:
    • Cut Z: Most printers won't allow the Z axis to move below their 0 position, so this option doesn't really matter as long as it isn't positive. I'd still set it to a very small negative value or 0 just in case your printer firmware does feel like crashing into the build plate.
    • Travel Z: How high the print head will move away from the build plate when traversing to new positions on the board. This will depend on how much spring travel your scribe has.
    • Feed Rate: The velocity at which motions are performed for the X-Y and Z axes. This is measured in mm/sec so values around 300-500 are pretty slow but produce clean results with no ghosting.
    • Post Processor: This will affect the format your G-Code is generated in. My particular printer runs Marlin firmware so I use the "marlin" option.
  • With your settings made, click "Generate." It may take a few moments before the screen changes.
  • The layout should now be outlined in blue, and the pane on the left will display "CNC Job Object"
  • Click "Save CNC Code," choose the path to save to, and make sure to save as the file format ".g-code".

Step 4: Scribing Isolations

Setting up the Printer

I made my spring loaded scribe on a lathe using a cutoff piece of shaft, a ballpoint pen spring, and a set screw I had on hand. It's rough, but it gets the job done. To make a scribe without access to machine tools or excessive "fabricobbling", I'd imagine a decent quality automatic center punch could be adapted to the task.

I 3D printed an arm that bolts to the side of my Printrbot Simple Metal and allows me to slide the scribe into a slot and clamp it in place. It's important that the arm is firmly mounted to the printer, otherwise vibration and ghosting can occur as the scribe drags across the board.

Preparing the Board

  • Clean your copper clad board with a strong solvent and let dry.
  • Coat the copper of the board with an even layer of Dykem layout fluid, and let dry until a hard, glossy coating is left.
  • Attach the copper clad board to your printer's build plate with the Dykem coated side facing up. I use double stick tape for this.

We have G-Code, a setup printer, and a blank board ready. Now we just need a way to interface with the printer. I use the standalone Pronterface application for this purpose.

There are 2 commands that can very useful for setting up to scribe:

  • G92 - Allows you to set a temporary reference location to start G-Code from on each axis. It can be used like G92 X... Y… Z… with the locations following each axis label
  • M114 - Get current position, useful for ensuring you have your start location set correctly

A note on G92

Be careful when setting new reference locations; your printer may not have any means of detecting if its new home will cause it to ultimately crash into the build plate or exceed its intended travel.

Putting the G-Code to Use

  • Load your g.code with the “Load File” menu. You may need to allow Pronterface to look for all file types in order to find it. You should then see gray outlines of the motions your machine will perform. Sometimes certain motions won't be visualized, but they still should be performed by the printer.
  • Connect to your printer through serial.
  • Use the arrow controls in the top left to home all axes of your printer and then jog to the position you want your G-Code to start from.
  • Make sure your scribe has good contact with the board, then use G92 X0 Y0 Z0 to set the start point.
  • Use M114 to ensure your printer accepted the new reference coordinate.
  • Start the "print"

When the scribing is complete, inspect every trace on your board closely for possible shorts or poor geometry. If you catch an incomplete isolation before removing the board from the build plate, you may be able to rerun the G-Code without losing any accuracy.

Reconditioning boards

If you set your scribe depth to just scratch away the Dykem mask, each board can be re-scribed several times without any noticeable effect on the function of the completed board. Of course, deep gouges in the copper can ruin a board blank.

  • Use a solvent like Acetone to remove the Dykem from the copper board.
  • Polish light scratches out with fine sandpaper and steel wool.
  • Clean the board thoroughly.
  • Recoat with Dykem.
  • Scribe again.

I made countless tweaks to reduce end play on my scribe and to get the depth of scribing just right. My results still have plenty of room for improvement, but the board has the capacity to fully function -- should its design allow.

Step 5: Etching and Inspecting

Etching is one of the less involved parts of this process, but there are some precautions that need to be taken.

Be sure to wear gloves, etch in a well ventilated area, and follow all other safety precautions on the etchant's safety data sheet. If you aren't certain how to dilute or neutralize waste etchant, keep it in a closed plastic container until you can take it to a designated disposal facility.


  • Place your board in a shallow plastic container and pour ferric chloride (or your etchant of choice) in until it covers the board's surface.
  • Agitate the container gently for several minutes. 10 minutes is usually long enough for my 4"x3" boards to etch, but the temperature of the etchant can effect this.
  • Pull the board out of the etchant and hold up to light to inspect for sections that still need etching.
  • If necessary, replace the board in the etchant and continue to agitate.
  • With etching complete, thoroughly rinse the board in water to remove any remaining etchant.
  • Close your etchant container and keep it in a dark place. It can typically be used multiple times before losing effectiveness.
  • Use a solvent to wipe off the remaining Dykem coating and expose the raw copper.

Basic Testing

It's obviously important the completed board doesn't have any shorts caused by incomplete isolation. I like to place my boards over a lit stage to highlight the isolations and quickly look for interruptions in the lit outlines of each trace. Not all shorts are easily tracked down visually, though.

Using a multimeter on its ohmmeter setting is the safest bet. If your design includes a ground plane around all traces, testing for total resistance between each trace and that ground plane can eliminate a lot of possible paths a short circuit could follow. Make sure to check for total resistance between adjacent traces as well.

If you do identify minor shorts between traces, you can sometimes scratch out the remaining copper bridges using a utility knife.

Step 6: Assembly

It's important to remember each board design can easily become a forgotten prototype on the way to your final product. For this reason I only ever installed the components necessary to read a single wheel speed signal. Unfortunately, this didn't get me out of a lot of drilling and soldering.

Drilling Through Holes

I made up my own PCB drill using an old Dremel and some machined and 3D printed parts. I visually approximated the center of each pad and used the drill to peck each hole through. I had to drill all the board's holes at once because it wouldn't lay flat under the drill once any of the components had been soldered in. Miraculously, all the holes wound up close enough to the center of the pads for my boards to function.


Soldering was another tedious step because many of my traces were very narrowly isolated from each other. Globbing too much solder on close traces can result in the solder wicking out across the traces and creating a short. A polymer solder mask, the green coating many commercial boards have, controls where solder can flow and prevents shorts during the soldering process. Creating a solder mask can be tedious as a hobbyist, so I just temporarily masked off neighboring traces using Kapton tape. With some care, I got all the needed components installed with only the occasional ugly solder joint.

Step 7: Vehicle Testing

To test the main functionality of Telemetry Master, I connected the board to a wheel speed sensor in my car and gave it a USB connection to a laptop. I then drove the vehicle while a patient passenger read off frequencies as they were displayed in the Arduino IDE's serial monitor. I programmed my board with another modified example from the FreqMeasure library.

#include <FreqMeasure.h>

void setup() { Serial.begin(9600); FreqMeasure.begin(); pinMode(13, OUTPUT); pinMode(7, OUTPUT); pinMode(6,OUTPUT); digitalWrite(13, HIGH); delay(1000); digitalWrite(13,LOW); digitalWrite(6, LOW); digitalWrite(7, HIGH); //pins 6 and 7 are selection lines for the MUX }

unsigned long sum = 0; int count = 0; float frequency = 0;

void loop() {

if (FreqMeasure.available()) { // average several reading together sum = sum +; count = count + 1; if (count > 30) { frequency = FreqMeasure.countToFrequency(sum / count); Serial.println(frequency,3); // Prints the frequency to 3 decimal places

sum = 0; count = 0; } } }

When I first started testing my board designs, they didn't even function well enough to begin transmitting data over the serial connection. In these cases, I used an oscilloscope and probed at the circuit's test pins to see how the waveform was being transformed.

One early finding I made with the oscilloscope was that my wheel speed sensors weren't outputting waveforms with high enough amplitudes to be registered by my initial board. This realization lead to a major redesign.

Step 8: Making Revisions

To resolve my low signal amplitude problem, I decided to incorporate an operational amplifier into my board before the signal regulation components. I first built up an entirely new circuit on a breadboard so making changes to the layout wasn't as demoralizing as wasting an entire piece of copperclad. I tweaked this circuit until I had the amplification and frequency reading working, then I brought the components and layout into a new design in EAGLE.


The new design gave me the opportunity to make some improvements like adding onboard voltage regulation, adjustable amplification, as well as an additional microcontroller and several I2C connections for easy sensor upgrades.

Toner Transfer Time

By the time I made this redesign, I was already about 3 boards into the project and had wound up stumbling upon a cheap laminator. I decided to give the toner transfer method a shot to make my final few prototype boards. This method has been documented pretty extensively online, but I'll give a brief overview of how it is accomplished.

Printing the Traces

A laser printer is needed to make this work, as the toner used is a plastic that can be melted onto a surface.

  • Tape a section of glossy magazine paper to a piece of regular paper and place it in the printer's paper tray.
  • Print the layout from your EDA software onto the magazine paper, making sure the orientation is correct.

Transferring the Traces

  • Lightly rough the copper surface with steel wool or fine sandpaper and thoroughly clean it.
  • Tape the magazine paper to the board with the layout facing the copper.
  • Feed the copper and paper through a laminator to heat the assembly. I did this around 10 times in order to build up heat in the board.
  • Allow the copper and paper to cool. This allows the toner to adhere to both surfaces.
  • Place the cooled board in water and allow it to soak for about 20 minutes to soften the paper.
  • Gently peel away the paper. Hopefully, all the toner will remain on the copper.

Preventing Pitting

With toner transfer, I've found my etchant often begins to eat through tiny pinholes in the toner mask before all the etching is complete. To reduce the pitting this creates in the surface, I would recommend drawing over any large sections of toner with a permanent marker to offer some extra masking against the etchant.

Step 9: Going Further

It took a several months and a few new(to me) techniques, but I finally have a board that works. I tested the final board in my car, saved all the frequencies it measured, and converted them to vehicle speed to show in the chart above. With frequency reading working well enough, I'm excited to see how the board handles the new functions I'll begin to add on.

You may have noticed I only wound up using "Scratch n' Etch" to make my first few boards, then finished off the later versions using toner transfer. Despite the preference I developed for the toner transfer method, I enjoyed experimenting with and documenting the scribing method. I think it's a creative way to put a 3D printer to use as a more versatile CNC platform, especially if you're interested in the "Replicating" part of the RepRap project. Now as interesting as these different PCB production methods have been, I'll still be happy to take a break from them for a little while. Of course, there is still other work to do to refine the board I have:

Wiring harnesses - Pretty soon I'll be putting together some custom wiring harnesses to connect each wheel speed sensor to Telemetry Master and on to the ABS computer. I think seeing some real automotive connectors and signal wires in use will make Telemetry Master look right at home alongside the professionally made computers in my car.

Creating a custom enclosure - EAGLE has the ability to generate a 3D model of your entire board in Fusion 360. This makes designing an enclosure a breeze. I'll just have to make sure the enclosure design protects the time investment I've made in the electronics of this little board.

Protecting the copper - I'd like to try my hand at making a solder mask to make soldering faster and prevent oxidation on the copper of future boards. Tin plating doesn't prevent shorts like a solder mask, but I might also try it out as a way to at least prevent oxidation.

Some resources I found useful:

Falstad's Circuit Applet

RepRap's Scratch n' Etch Page

Electronics Tutorials


Programming ATmegas with Arduino

Sensors Contest

Participated in the
Sensors Contest