Pitfalls of control real-world devices from the point of view of a backend developer. Part 1. Prototype development

Rallar
16 min readMar 30, 2024

--

It all started with the fact that I wanted to increase the power of the engine in my car by using a compressor.

In the process, it was necessary to understand Arduino and write algorithms for automatic control of real-world devices, formally — control of just one motor, but in practice — several pitfalls happened that I was not thinking about at the beginning.

The development and these pitfalls from the view of a person who had never worked with Arduino before, and haven’t written code to control real-world devices, seemed interesting for writing an article.

The algorithms turned out to be more complicated than “blinking an LED,” but from the point of view of experienced developers of such systems who know all the necessary algorithms, they will probably seem naive, especially since in the auto industry all these things exist. Cars have controllers for controlling similar devices for many years.

Idea

The article is not about cars, but about development, so I will tell about the original idea briefly.

A compressor for a car engine is a simple way to increase the power of a no turbocharged engine. There are several variants for compressors (electric, turbocharger), but we are talking about supercharger that is described below.

A supercharger is a fan in a closed case, which, due to the transmission of rotation from the engine, rotates quickly and pumps more air into the car engine than the engine could “take” on its own. As a result — excess pressure is created at the “intake” of the engine. Not ~100 kPa (1 bar), but let’s say 200 kPa (2 bar). The supercharger “compacts” the air into the engine. More air = more gasoline consumed = more engine power.

There are several supercharger installation schemes; I chose the most “ideal” one. In this scheme, the supercharger is installed intake of the engine. In this config the “throttle valve” is placed between the supercharger and the engine, same as in usual engine config. The throttle is closed by default. When the driver presses the gas pedal, the throttle opens, air begins to flow into the engine, and the engine begins to rotate with bigger frequency.

Let’s take an ordinary non turbocharged engine as an example. There is normal atmospheric pressure before the throttle. When the throttle starts open, the engine “sucks” the required volume of air into itself. When the throttle is closed, the engine simply stops “sucking in” air.

When the supercharger is installed, things become a little different and the problems begin. Imagine that the throttle is open, the supercharger is running, creating a lot of overboost pressure, and the driver suddenly releases the gas pedal. The throttle closes sharply and the overboosted air has nowhere to go — it is not allowed into the engine, and there is a closed space between the supercharger and the engine, there is nowhere to go. The supercharger in this a situation may break down after some time.

There is a simple solution to this problem — for example, “blow off”. This is a valve that is placed in an area of excess pressure and, when a some pressure limit is reached, it opens and releases air, making a noisy sound.

But I wanted an “ideal”, so I chose a method that I couldn’t find on the Russian-language Internet: installing a bypass valve in the form of an additional throttle valve (hereinafter referred to as an ATV) with electronic control and implementing its control based on an Arduino controller.

A simplified operating schema is shown in the picture. Overboosted air will flow through the open ATV to the supercharger intake and the supercharger begins to circulate this air in a circle without creating overboost pressure.

In addition to remove usage of the blow-off valve, this operating scheme will allow you to drive in fully non turbocharged mode, turning the supercharger on and off as needed. When the supercharger is turned off, the ATV is completely open and air flows freely into the engine.

I’m a backender. I write mostly backend for different projects. I had no experience writing for Arduino. Also, there was no knowledge of control algorithms for “real” devices. I began to write everything from scratch, assuming that everything was simple. It turned out that everything is not simple.

The original idea (and algorithm) seemed simple: there is a manifold absolute pressure sensor (MAP1) before the main throttle valve (hereinafter referred to as MTV) and there is a manifold absolute pressure sensor after MTV (MAP2). Based on these sensors, I thought to open the ATV when there is excess air, based on MAP1, and close it when there is no excess, but it is needed, based on MAP2.

But first, a little about the components selecting.

Components

The selection of components for Arduino is simple. All the components that I needed are well described, and there are libraries for many of them.

Arduino UNO R3. The Arduino board itself. Apparently a Chinese replica, since it was bought on Aliexpress. I chose the middle one between Mini and Mega, roughly calculating the required number of inputs.

74HC4051. Multiplexer. Allows you to expand the number of analog inputs by send different combinations of digital signals to the input. I have enough digital inputs left to allow this circuit to be used.

BTS7960 43A H-bridge. Throttle motor control driver. Allows you to control the rotation of the motor in different directions at a specified speed. Selected with enough power limit.

ACS712. Ammeter. Needed to shut down the system if the current consumed by the throttle exceeds limit. Later, I discovered similar functionality in the BTS7960, but left this component as an additional limiter.

DC-DC 12v-9v and DC-DC 12v-5v. Voltage converters. The first one converts voltage for the Arduino. The second one is to monitor the shutdown of the system. This helps bring the system to a “normal” state in case of shutdown.

Regtime 3–12 60–600. Shutdown delay relay. When we turn off the system with the button, we need to continue supplying power to the system to bring throttle to a “normal” state.

Beeper. To diagnose faults without connecting a laptop.

HW-316. 4-channel relay for switching on the supercharger and cooling system components.

Manifold absolute pressure sensor with built-in temperature sensor. Temperature sensors — to control the cooling system. Manifold absolute pressure sensor — to control the throttle. Temperature sensors are resistors and method to read their values is differ from other sensors.

VAZ 21126 “electronic” throttle. In addition to the motor, the throttle includes two throttle position sensors.

Here is current sheme version

After assembling the circuit using a breadboard and a set of wires, the fun began — writing the algorithm. So, actually, the pitfalls.

Pitfalls

Pitfall No. 1. Reading data from the sensors

I need to read data from the sensors. A typical sensor consists of two inputs and one output: minus, the base voltage and output signal.

The base voltage in most cases is 5 volts. The output signal varies from 0 to 5 volts. For example, the throttle position sensor: 0 volts — the throttle is closed, 5 volts — the throttle is opened, intermediate values — intermediate throttle positions.

The principle of reading the value of a such sensor: the output signal is connected to the analog input of Arduino and in the program we get the ability to read the value as an integer value in the range 0–1023. This value “maps” either to volts or immediately to the percentage of the throttle opening.

It turned out to be a surprise that the limit values of the sensors “float” in a large range depending on:

  • temperature — values float are not much, but exists;
  • from “warming up” the system — immediately after switching on, the values are slightly different, but differ from those given later;
  • probably from the quality of the connection of the wires in the breadboard, excess resistance due to the wire length;
  • probably depends on the quality of conversion in the DC-DC board, which leads to floating of the base voltage.

I initially thought about this problem, but I didn’t think that the values range would be so large. After the circuit is permanently soldered, in order to eliminate bad contact, I will measure the voltage at the key places to understand who introduces the greatest values of “float” and, if possible, reduce it.

The solution of the problem is to calibrate the sensors. Setting the minimum and maximum values of the sensor signal in minimum and maximum positions. At his moment I use predefined constant values.

Solution No. 2 — in the future, perhaps, there will be periodic self-calibration with saving the state into non-volatile memory.

Solution No. 3 — selection of high-quality DC-DC converters, high-quality component connection.

As an example of the whole problem, the throttle position sensor is now calibrated to a voltage of 0.5–4.2 volts, instead of the ideal 0–5 volts.

Pitfall No. 2. Calibration of the “opposite” sensors

The throttle is designed in an interesting way — it contains two position sensors. Sensor 1 produces a voltage that increases the more the throttle is closed. Sensor 2 produces a voltage that lowers the more the throttle is closed.

For example: when the throttle is closed, sensor 1 produces 5 volts, sensor 2–0 volts.

Assuming that the two sensors are made for fault tolerance — I made a position measurement algorithm based on sensor 1, also with checking sensor 2. If the values diverge more than the limit value — the system goes into error mode.

In the process of programming the throttle, I needed to periodically calibrate the limit values of the sensors, eventually reducing them quite significantly.

Because of this, I changed my opinion to that two sensors are needed not for fault tolerance, but just for some kind of self-balancing and position calculation depending on their mutual values.

After assembling the system and debugging it on real hardware, I will think about revising the algorithm for using sensors in the direction of calculating position, based on two mutual values.

Also, the throttle control driver has the ability to read the current used, which will allow it to calibrate the throttle maximum and minimum positions, assuming increased current consumption when the throttle has opened to its limit positions and is trying to open further.

Pitfall No. 3. Different sensor values

It would seem that the sensors are identical, but they give out different values. Tested on all sensors: temperature sensors, pressure sensors, throttle position sensors. For example, under the same conditions, pressure sensors show a difference of 1.4 kPa.

The problem was assumed from the beginning and the obvious solution is calibration.

Pitfall No. 4. This is the real world

A feature of controlling the throttle motor through an H-bridge type driver: to move the throttle, Arduino gives a command to move in the desired direction at the required speed. The driver receives the command and begins to move the throttle. At this time, the algorithm moves on to executing another part of the code.

This is where the interesting behavior of the throttle began.

The first version of the control algorithm was very simple: we check the percentage of opening of the throttle, give a command to move and move on to other parts of the algorithm, then we come back, check the percentage of opening and everything again.

The algorithm was written and worked until I started debugging another part of it. At some point, I turn on additional debugging and the throttle begins to move jerkily: it doesn’t move, then it immediately closes with a jerk, then it starts to open and close and open again.

The problem was unexpected: a command was given to move the throttle, at the next time another part of the code began writing debugging data to the COM port, and this happens slowly if the slow data transfer rate is selected. While the data was being transmitted to the COM port, the throttle was closing much more than required.

The solution is to use timer, tie the start of actions to time, and calculate the required “forward” position at any desired moment in time.

In a simplified form, the algorithm became as follows: when the throttle begins to move, the start time of movement is saved, at any other moment in time when we received control — the required position of the damper is calculated depending on the elapsed time and a decision is made what to do next — stop, continue moving or start moving in the opposite direction.

Pitfall No. 5. Resonance

Derived from the previous feature. When we want to bring the throttle to the desired opening percentage, we give a command to move in the desired direction at a given speed. The next time we get into this control part of the algorithm, the throttle may be open more than we need. A command is given to move in the opposite direction. As a result, continuous and even increasing oscillations of the throttle may occur.

I solved this problem in a simple way — the permissible delta of the required percentage is determined and a time parameter is used that does not allow changing the direction of movement more often than required.

Pitfall No. 6. Integer overflow

Arduino recommends working with integer values. Working with real numbers is slower.

My algorithms do not have complex logic that would be slowed down by calculations with real numbers and I don’t like premature optimization (I like reasonable optimization), but I decided to use calculations with integers.

And since suitable types were chosen for the values, after mathematical operations, at some point it shot itself in the foot. The problem is understandable, easy to debug, but I haven’t worked on the backend with integer mathematics for a very long time.

Pitfall No. 7. Debugging

Testing various backends is simple and straightforward. You can write a logic test that checks the written function. You can write an integration test that tests interaction with another system.

In the case of a system that works with real devices, sensors, and that can behave differently under seemingly identical conditions, testing the written logic turns into an entertaining exercise.

Initially, I wanted to do it according to the principle “we are not looking for easy ways” — write code, install the hardware, debug it on the car (of course, not while driving).

Then someone advised me to make an emulator for all this.

At first I thought about emulator of electronic devices, when the program would be installed on an Arduino emulator and would work with fully emulated hardware. Such software exists, but as I understand it, I would have to write all these emulators for all devices. In general, this path is for automobile corporations with departments of thousands of experienced developers.

The final chosen option: a physical emulator of the gas pedal was made, an emulator of the engine, supercharger, and sensors was written, connected as a separate component to the main program, and launched on the same Arduino. Testing was carried out on a prototype, using graphs constructed from data transmitted from the device. In VSCode, the plugin for visualizing graphs from the COM port is convenient, by the way.

Algorithm

Architecturally, the algorithm is divided into four parts:

  1. Throttle control part. Purpose — receives a command to bring the throttle to the required position in percentage 0–100 and then brings the throttle to the desired position using position sensors, current consumption, timeouts, and so on.
  2. Part of the main control. Purpose — based on the values of the pressure sensors, it understands whether boost is needed or not, whether the driver has now pressed the gas pedal or released it, or pressed and held it in the middle position so that the car moves at a constant speed. Calculates the required position of the throttle, gives a command to the first component to set the desired position.
  3. Part of the extra control. Purpose — turning on/off the supercharger, turning on the cooling system (water pump, fan) depending on temperature sensors, collecting debugging information, processing errors and limit values.
  4. Part of the engine emulator. It is included in the main program as a separate component and is enabled by a flag in the code.

Throttle control part

The first part of the algorithm receives the required position, remembering it. Next, it periodically calculates the intermediate position, passing it to the second part of the algorithm.

The calculation of the intermediate position depends on whether the throttle is opening (we need to open it quickly, make a “blow-off”) or closing (it closes slowly, gradually increasing the boost, smoothly increasing power). As I mentioned above in the “pitfalls” — the calculation of the intermediate position depends on the time of the beginning of the movement and is tied to the moment in time, so at every moment we know what position is required — regardless of whether 1 ms or 10 ms has passed since the last calculation.

The second part of the algorithm takes an intermediate position and ensures its achievement by controlling the activation of the throttle motor.

This division can be called macro and micro management — it allows for smooth movement in conditions of regular changes in the required position. The first part ensures compliance with the time value for the start of movement, this is important when changing to movement in the opposite direction.

Example: if the throttle is currently closing and the new required position means that it should continue to close — no change in timing occurs, continuing to smoothly close the throttle.

Main control part

The first part of the algorithm is responsible for determining whether the driver is pressing the gas pedal or releasing the gas pedal. The solution is made by estimating several pressure values over constant periods of time: the pressure is constantly increasing — the gas pedal is held, constantly decreasing — the gas pedal released.

The second part of the algorithm calculates the position using the relatively simple conditions (simplified):

  • if the gas pedal is pressed and the pressure after the main throttle valve (MTV) increases, we want to close the additional throttle valve (ATV) completely to do overboost;
  • if the gas pedal is released and the pressure is below the limit (MTV is closed) — we want to release overboosted air and open the ATV completely (do a “blow-off”);
  • in other cases, we maintain the pressure before the MTV a little more than the pressure after the MTV by slightly opening or closing the ATV — a stable position when the gas pedal is slightly pressed and the car simply moves evenly at approximately the same speed.

The third part brings the calculated position to the required one, taking into account timeouts, and transmits a control command to the throttle control part.

Extra control part

This part doesn’t contain any interesting algorithmic logic. Basically, simple conditions of the form are used here: the air temperature has risen to a limit value — turn on the pump; it has increased even more — turn on the fan.

Also, here it is possible to turn off the supercharger in case of prolonged idling — we are standing at a traffic light for a long time or the car is parked with the engine on.

Engine emulator part

The emulator has a physical gas pedal in the form of a rheostat (variable resistor) — this is a slider that can move between two positions, in the code we know how much it is shifted — by 0% or 100%. No different from an electronic gas pedal.

The emulator contains the value of the current engine speed. We press the gas pedal a little — the engine speed begins to increase. We press the gas pedal hard — the engine speed begins to increase, but faster.

Then there is the value of the supercharger operating efficiency. The supercharger compresses air with different efficiency at different engine speeds — at low speeds compression is less effective. For the calculation, I took the efficiency graph of a roughly similar supercharger and simplified it: we calculate the efficiency using a linear formula, plus the inflection point is taken into account — when the graph greatly changes the slope. For this point I took 2000 rpm.

Next, the net pressure in front of MTV is calculated, which the supercharger could produce if ATV is closed. It depends on efficiency and indirectly on engine speed.

Next comes the calculation of the pressure in front of MTV, but taking into account the position of ATV. The position of ATV is taken from the real value. ATV is closed — the maximum output pressure calculated. ATV is open — the pressure is minimal (atmospheric), the air “moves in a circle”.

The last calculated value is the pressure after MTV (directly at the engine intake) — the previous value is taken and the position of MTV is applied to it using a similar formula, and it is taken equal to the position of the gas pedal. Gas pedal fully pressed = MTV fully open — pressure = calculated pressure in front of MTV.

Thus, a system is obtained with direct connection from the gas pedal and with feedback from the real value of the position of the ATV, which itself depends on the gas pedal. But it works. Roughly similar to a real engine, although I understand that it will behave differently in a car.

Demo

Demonstration of the operation of the resulting prototype.

The same thing, but on graphs.

Explanation of the video with graphs. After recording the video, I noticed that at one point the video graphs contradict my comments. When the gas pedal is pressed without turning on the supercharger, the boost pressure increases to the main throttle valve. This happens because the emulator does not take into account the supercharger enable flag. This did not interfere with the tests of opening the additional throttle valve, which is why I noticed it only now. If the flag were taken into account, then it would be correct as I say in the comments — the graphs would show normal atmospheric pressure.

After installing the “hardware” part of the system and debugging it on real sensors, real gas pedal, real motor, supercharger, I will continue describing the new problems in the next article: Part 2. Debugging on a car (there will be a link here).

Project source codes.

--

--