HDDG22 Talk: ECUs and their sensors

Chris Gammell asked me to give a presentation at his Hardware Developers Didactic Galactic meetup in San Francisco.  I enjoy talking about things I work on so I didn’t hesitate to say yes.  I’m pretty sure he was expecting me to talk about Google’s Zaius server, an open-hardware POWER9 server design,  that I brought to a previous meetup.  Giving presentations about my day job takes some review and approvals, even for open designs.   Instead, I offered to talk about engine control, a subject I’m spending many nights on recently.

I’ve had to learn about engine tuning and EFI systems in particular to get Cobra Commander back on the track.  Since that knowledge seems to be spread in bits and pieces, I put together what I’ve learned into a presentation focusing on the sensors used and how they fit into the engine control systems.

Both slides and video are available.  Chris did a writeup for SupplyFrame’s blog as well.

MoTeC M48 Teardown

After working out the serial port pinout on my MoTeC M48 (see MoTeC PCI Cable for $20), I was left with one unidentified pin.  As the same DB9 is used with both a PCI cable and a SUU, I suspect this extra pin is how the ECU distinguishes between the two.  My M48 is already running the latest firmware so figuring out how to emulate an SUU isn’t a critical but not knowing how it works bothers me.

After trying a few things (tie pin to +5V, tie to GND), I decided I needed to open up the case and figure out where pin 12 on the ECU connector goes.

Based on the sharp, 90° bends in the traces, the whole board was definitely autorouted.  Unfortunately, the autorouter decided to route traces underneath chips which makes tracing them out much harder.  Also, the whole board is covered with conformal coating.  On the upside, a large number of components are used to protect the inputs and output from out-of-spec voltages which makes sense as incorrect wiring is bound to be a common problem in the field.

Zooming in on the main processor, I find a Motorola MC68332ACFC16 (datasheet) which is a nifty 68k variant with peripherals geared toward generating time-synchronized I/O.

Next to the main processor are a pair of Altera MAX 7000 PLDs (EPM7032LC44 datasheet).  I didn’t research enough to determine if these PLDs are used with the main processor’s time processor functions or with its expansion bus.

After a few false starts, I determined that pin 12, our extra serial pin, passes through protection circuitry before ending up at a GPIO on port E of the main processor.  There seems to be no alternate functions on that pin that make sense so next up will be reverse engineering the firmware.  Thankfully, MoTeC provides a full, unencrypted firmware as part of the M48 software on their website.  Time to buy a license for IDA.

MoTeC PCI Cable for $20

Update (2018-07-13): Preassembled cables are now available on Tindie.

After getting the MoTeC M48 EMP software installed in Dosbox as described previously, I naively assumed the M48 just connected with normal RS232 serial.  I mean, there’s a DB9 hanging under the dash and the EMP software is looking for a serial port.  Of course it couldn’t be that simple.

Coiled up grey cable with DB9 connectors.
Motec PCI Cable: Pure Unobtainium

It seems that MoTeC decided to save a dollar in the M48 by not including RS232 level shifters.  Instead, it has 5V TTL serial on non-standard pins.  MoTeC helpfully offers cables, called PCI and CIM, to bridge the gap from an RS232 serial port to the ECU.  The downside? These cables are practically unobtainium (or at least cost $220 for one).  Knowing that it’s just TTL serial, why is this cable so expensive?  Can’t I just make one?

Official wiring diagram for M48 serial port

MoTeC offers a wiring diagram for the M48 but it doesn’t tell you what signals are on the DB9 pins.  At least they were clear that pin 6 is ground.  After some time with a voltmeter and oscilloscope, I figured out the following pinout:

DB9  Signal  ECU
1     +5V     24
2     N/C
3     N/C
4     N/C
5     TXD     11
6     GND     27
7     ???
8     N/C
9     RXD      9
Black cable with USB Type-A on one end and 0.1" header with 5V TTL serial on the other
Sparkfun FTDI Cable 5V: Functional equivalent to MoTeC PCI cable

So, what’s inside that $220 cable?  Probably a MAX232.  A simpler, modern alternative is a FTDI 5V cable from Sparkfun for $20.  Either make a connector to adapt to DB9 or wire directly into the ECU connector pins.

Now the M48 EMP software running in Dosbox is talking to the ECU over the FTDI cable.  I’ve successfully downloaded the current tune and some log data.  I even discovered that the air temperature sensor was showing a fault and was able to clear it by reseating the connector on the sensor.

Next up is inventorying the sensors and actuators installed and finding their datasheets.  The goal is to get a good understanding of the tune that is currently loaded and get ready for logging data during dyno runs.  That probably means building a board that can capture the telemetry the ECU spews out the serial port during normal operation.  Then I’ll start digging into the protocol used by EMP to configure the ECU.

Drag Race Cobra and MoTeC M48 ECU

1996 Cobra modified for drag racing parking in a garage

A few month ago, a coworker decided to buy a built drag racing car and was looking for someone to help crew.  I emphatically offered my services and we started working out what exactly he had bought.

The car started as a stock 1996 Ford Mustang Cobra with a 4.6L V8 engine (in Mystic, no less).  The engine has been rebuilt with a supercharger and nitrous.  The drivetrain has been completely replaced.  Yet, it still has power windows, doors, and steering.  The story from the seller was that the original owner had some outrageous tuning that needed the nitrous for cooling, not power.  The seller, the 2nd owner, had had a professional tuner redo the tuning from scratch to get something that would work as a daily driver, an 800HP daily driver.  It’s an interesting beast to say the least.

I started looking into the ECU to figure out what we had to work with.  It’s a MoTeC M48 which is a well-respected ECU from the 90s.  MoTeC still has the manual and software available for download but that’s where this story starts to get annoying.  The M48 software is a DOS-only application that talks to the ECU over serial.  Most of the applications will work under Windows 7 but not all.  I couldn’t get it to work with Windows 10 at all.  After a bit of research, others had had luck with Dosbox.

Screenshot of MoTeC M48 EMP software
MoTeC M48 Software

I use a MacBook Air so I tried Boxer which is just a nice UI over Dosbox.  Installing the MoTeC software is…odd.  The download from MoTeC is a Windows installer that dumps a DOS-based installer into C:\Motec.  The DOS-based installer is built around floppy images and is generally quirky.  I was able to successfully get everything installed in a Windows Vista VM I had handy and then copy C:\Motec into a Dosbox disk image.  If you are looking to use MoTeC M48 EMP on a Mac and want to save a bunch of time, email me.

One last note about using Boxer: the Boxer UI doesn’t expose any settings for serial ports.  Do not despair; Boxer’s Dosbox core has all the serial port support included.  You just need to add the following to the DOSBox Preference.conf stored inside the Boxer disk image bundle (right-click on the disk image in Finder and click Show Package Contents):

serial1=directserial realport:cu.usbserial-A100P1XB

Change cu.usbserial-A100P1XB to the serial port of your choice (one of /dev/cu.*) and you’ll be good to go.

A Hash Mismatch Made in BitBake

Dear Brother Yocto,

These ‘vardeps’ variables in Yocto are entirely magic and undocumented from my best reading of the Mega Manual. There is something like one paragraph that says “you might need to change / add these to make your recipe work in some cases”.  It would be nice to have an explanation of when to use vardeps and when to vardepsexclude.  I get that they are something to do with the sstate caching hash.

–Tired of ‘Taskhash Mismatch’

Dear Tired of ‘Taskhash Mismatch’,

Task hashes and their interactions with various caching mechanisms are indeed a confusing part of Yocto.  I’ve always found “ERROR: os-release-1.0-r0 do_compile: Taskhash mismatch 2399d53cb73a44279bfdeece180c4689 verses 9298efe49a8bf60fc1862ebc05568d32 for openbmc/meta/recipes-core/os-release/os-release.bb.do_compile” to be surprisingly unhelpful in resolving the problem.  Worse is when the problem manifests as a failure to run a task when something has changed instead of an error.

As you noted, the Yocto Developer Manual and Yocto Reference Manual are sparse on this topic.  A very careful reader will catch a reference in Section 2.3.5 of the Reference Manual to the Variable Flags section of the BitBake User Manual.  While that section does describe what these magic [vardeps] and [vardepsexclude] incantations do, I suspect you, the reader, are left wondering why they are in an entirely different manual.  Even though the signs are there for the astute observer (what command do you run to build an image?), many Yocto users don’t realize Yocto is an infrastructure built upon an independent tool called BitBake.

Yocto is just BitBake metadata

Per the BitBake User Manual, “BitBake is a generic task execution engine that allows shell and Python tasks to be run efficiently and in parallel while working within complex inter-task dependency constraints.”  Yocto is a set of tasks executed by BitBake to assemble Linux distributions.  This leads to a few non-trivial insights that I’ll spare you from discovering on your own:

  • Yocto recipes are really BitBake recipes (hence why they use a .bb extension) and follow the syntax described in Chapter 3: Syntax and Operators.
  • Many of the “magic” variables used in recipes (PN, bindir, etc) are defined in meta/conf/bitbake.conf.
  • Recipe lookup is actually controlled by BBPATH, not the order of layers in bblayers.conf.  BBPATH just happens to be modified in each layer’s layer.conf (see Locating and Parsing Recipes).

Tasks and Caching

As a Yocto developer, I tend to think in terms of recipes (because they usually correspond to a package) but BitBake’s fundamental unit of work is a task.  A task is a shell or Python script that gets executed to accomplish something, usually generating some form of output.  Often a task depends on the output from some other task and BitBake offers mechanisms for specifying these dependencies (see Dependencies for details).  BitBake will execute the tasks with as much parallelism as possible while maintaining dependency order.

For smaller projects, a straightforward execution of the tasks, where each task is always run on every invocation, might be OK.  When building a full Linux distribution like Yocto does, every invocation could take hours.  Caching to the rescue!

BitBake has multiple caching mechanisms but for this discussion only the setscene (or sstate) cache is relevant.  Setscene is a BitBake mechanism for caching build artifacts and reusing them in subsequent invocations.  Conceptually, recipes can provide a setscene version of a task by creating a new task of the same name with a _setscene suffix.  This version of the task attempts to provide the build artifacts and, if successful, the main version of the task is skipped.  The above link contains a more in-depth discussion including a lot implementation details necessary for anyone writing a setscene task.

Cache Invalidation and the Task Hash

As with any caching system, the setscene cache needs a way to decide when to invalid an entry.  In the case of setscene, it needs to know when the task would generate meaningfully different output.  That’s exactly what a task hash is supposed to answer.

Note: The Yocto and BitBake manuals tend to use the terms task hash, checksum, and signature interchangeably.  I find this especially confusing when thinking about do_fetch because each source file also has a checksum associated with it.  I’ll be using task hash throughout here but keep in mind that you might see the other terms when reading the documentation.

Since a task is just a shell or Python script, taking the hash of the script should tell us when it changes, right?  Mostly, but not quite.  The task’s dependencies can easily affect the output of this task.  OK, so what if we combine the hash of the script with the hashes of all the dependencies and use that to identify the task?  Excellent! That’s more or less what a task hash is: a combined hash of the task basehash (represents the task itself) and the task hash of each dependency.

Why did I use the term “basehash” above instead of script hash?  Any variable used in the script has the potential for changing the script.  Consider a case where the current date is used as part of a build stamp.  Should the task be considered meaningfully changed whenever the date changes?   The basehash is a more flexible hash that allows specific variables to be excluded (or included) in the hash calculation.  By excluding variables such as the current date and time, the task becomes more stable and the artifacts can be reused more often.

Is that what [vardeps] and [vardepsexclude] do then?

Yup.  [vardeps] and [vardepsexclude] are Variable Flags that add or remove variables from the task’s hash.  When you get a “Taskhash mismatch” error like the one from os-release at the start of this post, BitBake is telling you that the task hash changed unexpectedly; the script didn’t change but the task hash did.  In the os-release case, the original os-release.bb from Yocto declared that the do_compile task was dependent on the VERSION, VERSION_ID, and BUILD_ID variables so those were included in its task hash.  OpenBMC’s  os-release.bbappend uses an anonymous Python function to construct the VERSION, VERSION_ID, and BUILD_ID variables by querying the metadata’s git repo.  Subsequently, the task hash will change even though the script didn’t actually change.  The fix was simple: remove those variable as task hash dependencies.

–Brother Yocto

Where do I get advice on Yocto?

Over the past three years, I’ve worked on a variety of projects that use embedded Linux distros built using Yocto Project. While I quickly realized the versatility of Yocto during the first project, learning the ins and outs of recipes, distros, and machines became a regular frustration that followed through each project. Not only is Yocto huge, the design patterns and tools change almost daily.

Yocto Project Reference Manual and BitBake User Manual contain a wealth of vital information about the core concepts and well-established tools but traditional documentation only gets you so far. As my understanding of Yocto grew, mostly by reading vast amounts of code while debugging problems, I became the local go-to expert in my teams. Instead of hacking away at the next task, I was answering questions whose answers required understanding how and why things were designed the way they were, information only available in the code itself. Yet there was nowhere to persist these conversations.

That’s how I came to start this series I’m calling “Dear Brother Yocto.” Originally I just wanted a place to share tips and tricks as I learn them. By the time I got the site up and running, I had seen enough requests for advice on the Yocto mailing list that having a write-in advice column style just made sense. Some posts will be from readers, some will be from my own questions. Regardless, I’ll dig into the code, find a solution, and explain what the heck is going on.

If you have a question about Yocto, please share it with me via Twitter (@kc8apf) or email (kc8apf at kc8apf dot net).