Complete dashboard for an old car

magflip
Fri Nov 17, 2017 5:54 pm
Hello guys!
My name is Felipe, I’m from Brazil and my project consists of a new dashboard panel for my old Volkswagen Gol 1989.
The original dashboard has only some information and now that car it has a turbo, I thought about creating a new dashboard that would provide more information. The original engine used carburetor but next to the installation of the turbo was adapted an electronic injection Megasquirt MS1.
I already partially succeeded by connecting an Arduino UNO (and later a Mega2560) to Megasquirt using Bluetooth modules HC05 and HC06. I was able to receive information from the Megasquirt and view them on LCD displays without any difficulties.
However, my intention is to make a complete dashboard, including analog gauges (for speedometer and RPM). These gauges will work with Switec step motors, suitable for this purpose.
Due to the size and complexity of the code, it became unfeasible to use Arduino (it could not process the data received from the electronic injection module, control the 3 step motors simultaneously and send them to a Nextion display) . It was at that time that I started for STM32 (I currently use a STM32F103C Generic “blue pill”).
I was able to successfully communicate the STM32 with Megasquirt. I was also able to make the data available on the Nextion display, but at that point my problems started. Some variables sent to the display are of the float type (which I do not think are supported by the display) so I have to perform some “maneuvers” in the code to be able to make the information available as a string and this process causes the execution of the code become slower than I would like.
In addition, I did not succeed in using Switec’s own libraries for the Arduino in STM32.
The final project will consist of a dashboard with:
1. Analog speed gauge: VSS connected to STM32 to determine speed; (code not yet implemented)
2. Analog Gauge RPM: information from Megasquirt that will be connected to STM32; (code not yet implemented)
3. Analog fuel level gauge: resistive sensor in fuel tank connected to STM32; (code not yet implemented)
4. Coolant temperature analogue gauge: information from Megasquirt that will be connected to STM32; (code not yet implemented)
5. LCD Display Nextion: will display some information such as value of turbo pressure, battery voltage, nozzle pulse size, etc., all sent by Megasquirt to STM32.
If I can not use the step motor library in STM32, I thought I would connect an Arduino UNO to the STM32 (via I2c) and control the stepper engines through the Arduino. What do you think?
As I am not a native programmer, I know that my code is far from ideal and I would therefore like help to improve it. Any other suggestions about the project are also welcome.

#define Nextion Serial

int kpa, clt, afr, maxkpa, maxclt, lolambda, hilambda, lobatt, hibatt, lopulse, hipulse, loboost, hiboost, lopw, hipw, percentrpm2;

int stt = 0;
int resetcounter = 0;

String boost2;
String batt2;
String pw2;
String lambda2;

int i;
char buff[10];

float lambda, kmh, boost, batt, pw, percentrpm;

unsigned int iTimefull, RpmHitmp, RpmHiRes , iTimeX, iTime, rpm2;
unsigned long lastupdate = millis();

bool lambdast, cltst;

byte dados[42]; //calling megasquirt array of 42 bytes

int cltadc[255] = {435.4, 369.4, 334.8, 311.7, 294.7, 281.2, 270.1, 260.7, 252.6, 245.4, 239.0, 233.2, 228.0, 223.2, 218.7, 214.6, 210.7, 207.1, 203.7, 200.5, 197.5, 194.6, 191.8, 189.2, 186.7, 184.3, 182.0, 179.8, 177.6, 175.6, 173.6, 171.7, 169.8, 168.0, 166.2, 164.5, 162.9, 161.3, 159.7, 158.2, 156.7, 155.2, 153.8, 152.4, 151.0, 149.7, 148.4, 147.1, 145.8, 144.6, 143.4, 142.2, 141.0, 139.9, 138.8, 137.7, 136.6, 135.5, 134.4, 133.4, 132.4, 131.4, 130.4, 129.4, 128.4, 127.4, 126.5, 125.6, 124.6, 123.7, 122.8, 121.9, 121.0, 120.2, 119.3, 118.5, 117.6, 116.8, 115.9, 115.1, 114.3, 113.5, 112.7, 111.9, 111.1, 110.3, 109.6, 108.8, 108.0, 107.3, 106.5, 105.8, 105.0, 104.3, 103.6, 102.9, 102.1, 101.4, 100.7, 100.0, 99.3, 98.6, 97.9, 97.2, 96.5, 95.8, 95.2, 94.5, 93.8, 93.1, 92.5, 91.8, 91.1, 90.5, 89.8, 89.1, 88.5, 87.8, 87.2, 86.5, 85.9, 85.2, 84.6, 84.0, 83.3, 82.7, 82.0, 81.4, 80.8, 80.1, 79.5, 78.9, 78.3, 77.6, 77.0, 76.4, 75.7, 75.1, 74.5, 73.9, 73.2, 72.6, 72.0, 71.4, 70.7, 70.1, 69.5, 68.9, 68.2, 67.6, 67.0, 66.3, 65.7, 65.1, 64.5, 63.8, 63.2, 62.6, 61.9, 61.3, 60.6, 60.0, 59.4, 58.7, 58.1, 57.4, 56.8, 56.1, 55.5, 54.8, 54.2, 53.5, 52.9, 52.2, 51.5, 50.8, 50.2, 49.5, 48.8, 48.1, 47.4, 46.7, 46.0, 45.3, 44.6, 43.9, 43.2, 42.5, 41.8, 41.0, 40.3, 39.6, 38.8, 38.1, 37.3, 36.5, 35.8, 35.0, 34.2, 33.4, 32.6, 31.8, 30.9, 30.1, 29.3, 28.4, 27.5, 26.7, 25.8, 24.9, 23.9, 23.0, 22.1, 21.1, 20.1, 19.2, 18.1, 17.1, 16.1, 15.0, 13.9, 12.8, 11.7, 10.5, 9.3, 8.1, 6.8, 5.6, 4.2, 2.9, 1.5, 0.0, -1.5, -3.1, -4.7, -6.4, -8.1, -10.0, -11.9, -13.9, -16.1, -18.3, -20.8, -23.4, -26.2, -29.2, -32.6, -36.4, -40.6, -45.6, -51.5, -58.9, -68.9, -85.1, 9999};

void setup() {
Serial1.begin(9600); //conexão bluetooth com a mega
Serial2.begin(9600); //conexão bluetooth com a mega

Nextion.begin(9600); //conexão com o display nextion

printtext("alarm.txt=", "CONNECTING");
delay(3000);

printtext("alarm.txt=", "PAIRING");
delay(6000);

}

void loop() {
Serial1.print("R");

delay(150);

if (Serial1.available() >= 41) //verifica a disponibilidade de dados enviados pela mega
{
for (int i = 0; i < 41; i++) {
dados[i] = Serial1.read();
}

// tratando os dados recebidos da mega
//* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ** ** * * * * * * * * * * * * * * * * * * ** * *
kpa = (dados[4] + 2.147) * 1.6197783; // para sensor map mpxh6400
if (kpa > maxkpa) maxkpa = kpa;
if (maxkpa <= 94) {
boost = 0;
}
else {
boost = ((maxkpa - 94) / 100);
}

//* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ** ** * * * * * * * * * * * * * * * * * * ** * *
pw = dados[14]; // in segundos

//* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ** ** * * * * * * * * * * * * * * * * * * ** * *
iTimeX = dados[39];
iTime = dados[24];

//* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ** ** * * * * * * * * * * * * * * * * * * ** * *
lambda = ((dados[9] * 0.002770) + 0.6217);

//* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ** ** * * * * * * * * * * * * * * * * * * ** * *
if (millis() > lastupdate + 5000) {
batt = (3 * (dados[8]) / 25.5);

clt = (cltadc[dados[6] - 1] - 32) / 1.8;
if (clt > maxclt) {
maxclt = clt;
}
lastupdate = millis();
}
//* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ** ** * * * * * * * * * * * * * * * * * * ** * *
}

//* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ** ** * * * * * * * * * * * * * * * * * * ** * *
rpmfunction();
//* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ** ** * * * * * * * * * * * * * * * * * * ** * *
printvariables();
//* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ** ** * * * * * * * * * * * * * * * * * * ** * *
pw2 = floattostring7(pw);
boost2 = floattostring(boost);
batt2 = floattostring(batt);
lambda2 = floattostring(lambda);
/* if (RpmHiRes < 1500){
rpm2 = map(RpmHiRes,0,1499,328,359);
}
else{
rpm2 = map(RpmHiRes,1500,8000,0,212);
}*/
percentrpm = int(RpmHiRes * 100 / 7000);
percentrpm2 = int(percentrpm);
Serial1.flush();
}

int printvariables() {

printval("speed.txt=", 000);
printval("kpa.txt=", kpa);
printval("clt.txt=", clt);
printtext("batt.txt=", batt2);
printtext("max_boost.txt=", boost2);
printval("max_clt.txt=", maxclt);
printtext("pw.txt=", pw2);
printval("rpm2.txt=", RpmHiRes);
// printval("rpm.val=", rpm2);
Nextion.print("rpm.val=");
//Nextion.write(0x22);
Nextion.print(percentrpm2);
//Nextion.write(0x22);
Nextion.write(0xff);
Nextion.write(0xff);
Nextion.write(0xff);

if (lambda > 0.65) {
printtext("lambda.txt=", lambda2);

}
else {
printtext("lambda.txt=", "heating");
}
}

void printval(const char* str, int val) {
Nextion.print(str);
Nextion.write(0x22);
Nextion.print(String(val));
Nextion.write(0x22);
Nextion.write(0xff);
Nextion.write(0xff);
Nextion.write(0xff);

}

void printtext(const char* str, String val) {
Nextion.print(str);
Nextion.write(0x22);
Nextion.print(val);
Nextion.write(0x22);
Nextion.write(0xff);
Nextion.write(0xff);
Nextion.write(0xff);

}

long rpmfunction() {
iTimefull = iTimeX + iTime;
if (iTimefull > 0) {
RpmHitmp = (120000 / (iTimefull));
}
else {
RpmHitmp = 0;
}
if (RpmHitmp > 20) {
RpmHiRes = RpmHitmp;
}
else {
RpmHiRes = 0;
}
return RpmHiRes ;
}

String floattostring(float var) {
for (i = 0; i < 10; i++) {
dtostrf(var, 4, 2, buff); //4 is mininum width, 6 is precision
return buff;
}
}

String floattostring7(float var) {
for (i = 0; i < 10; i++) {
dtostrf(var, 4, 5, buff); //4 is mininum width, 6 is precision
return buff;
}
}


RogerClark
Fri Nov 17, 2017 8:29 pm
Thanks for posting.

There are 4 or 5 people on the forum who are building car displays.

At least one other person was trying to use a Nexion display, but I think the also has problems with it:-(


stevestrong
Sat Nov 18, 2017 11:13 am
You should first have basic things clear like different variable types (int, uint8_t, float,…).

You use a lot of int arrays/variable and do math with float values, :shock:
So all those float values will be reduced to integers.

I personally recommend to always specify variable types by the bit width, like:
uint8_t, uint16_t, uint32_t, float.
Then you can see at once where the values can under/over-flow or limited in resolution.


magflip
Tue Nov 21, 2017 7:26 pm
[stevestrong – Sat Nov 18, 2017 11:13 am] –
You should first have basic things clear like different variable types (int, uint8_t, float,…).

You use a lot of int arrays/variable and do math with float values, :shock:
So all those float values will be reduced to integers.

I personally recommend to always specify variable types by the bit width, like:
uint8_t, uint16_t, uint32_t, float.
Then you can see at once where the values can under/over-flow or limited in resolution.

Thank you for the tips. I’ll review this part of the code.

In these last days I worked a little more on the project and got some evolution.
In the next few days, I intend to install the car in the following way: the STM32 will be powered by a voltage regulator that will pick up the power directly from the battery (12v). The Nextion display will have its own power supply in the same way, using an appropriate voltage regulator.
The power will only occur when the ignition key is on. With the key completely off, the system will be “completely” turned off. In this way, with the car stationary, the RPM gauge will be indicating, for example 950 RPM and if the key is turned off, the gauge will continue to indicate ~ 950 RPM, even with the car off.
That said, I thought of a system that keeps the STM32 running for 1 or 2 seconds after the ignition is turned off until it returns the stepper motors and servo motors to the starting position (0 km / h, 0 RPM, etc.). Could a circuit with a capacitor of 0.1f be able to power the STM32 to perform these tasks? What would the connection scheme look like? Any other way around this?


ahull
Wed Nov 22, 2017 2:43 am
I suspect you will need a capacitor of a few hundred uF to run the STM for sufficient time after the 12V goes down.
You will also need some form of brownout detection on the 12V line (you may get away with polling a simple voltage divider from an analog input) so you know the power has gone. The size of your capacitor will depend on how long you need to keep the STM running to update the display.

You could also allow the STM to control its own power, either by using a latching relay, or by using the STM32 low power modes and keeping it permanently powered, but sleeping when not required. In low power mode, the STM can sip a few uA, so it wont drain the 12V battery.


magflip
Wed Nov 22, 2017 5:04 pm
[ahull – Wed Nov 22, 2017 2:43 am] –
I suspect you will need a capacitor of a few hundred uF to run the STM for sufficient time after the 12V goes down.
You will also need some form of brownout detection on the 12V line (you may get away with polling a simple voltage divider from an analog input) so you know the power has gone. The size of your capacitor will depend on how long you need to keep the STM running to update the display.

You could also allow the STM to control its own power, either by using a latching relay, or by using the STM32 low power modes and keeping it permanently powered, but sleeping when not required. In low power mode, the STM can sip a few uA, so it wont drain the 12V battery.

Thanks for the suggestions ahull!
I researched a little more about the possibilities you suggested and I found it interesting to leave the STM32 always on, but in “economy mode” when the car is off.
I then thought of making the following connection: STM32 always powered by the car’s 12v battery (using a voltage regulator). A direct connection (through a voltage divider) from the “ignition key” to the PB1 pin, for example, to monitor when the ignition key is on. With an attachinterrupt function on that pin, if the signal becomes LOW, I return the step motors and servo motors to the initial position and active the STANDBY mode. An extension of this connection to the PB1 pin will be made to the PA0 pin (WKUP) to reset the STM32 when the ignition key is turned on.
Should this scheme work or am I wrong?

Another thing I saw during the searches was the STM32sleep library of the chacal. From what I understand it disables some functions of the board to save more energy, correct? Does it optimize the power saving of the standby mode or for my case is it dispensable?

Thank you!


RogerClark
Wed Nov 22, 2017 7:50 pm
I am not sure if you are using and OBD adapter, but I left one of those Bluetooth OBD adapters plugged into my car for several months and found that one day the car would not start because the battery was flat

Initially I thought the alternator was faulty, but eventually realised that the OBD had been draining the battery all time time for months and months, and because I don’t do much freeway driving and all the speed limits keep getting reduced, the car was not charging the battery enough.

This was compounded because I drive a manual and put the car into top gear (5th) as soon as I get to 40kmh, and a lot of the roads around here only have a 50kmh speed limit, hence the rpm on the engine is less than 1500 and the alternator was not kicking out much charge.
And to further compound things, we have a several level crossings near me, where you have to wait ages for the boom gates, so I had been turning the engine off to save fuel ( like all modern cars do automatically)

Unfortunately Subaru was not designed to keep the battery charged under these conditions


magflip
Wed Nov 22, 2017 8:34 pm
[RogerClark – Wed Nov 22, 2017 7:50 pm] –
I am not sure if you are using and OBD adapter, but I left one of those Bluetooth OBD adapters plugged into my car for several months and found that one day the car would not start because the battery was flat

Initially I thought the alternator was faulty, but eventually realised that the OBD had been draining the battery all time time for months and months, and because I don’t do much freeway driving and all the speed limits keep getting reduced, the car was not charging the battery enough.

This was compounded because I drive a manual and put the car into top gear (5th) as soon as I get to 40kmh, and a lot of the roads around here only have a 50kmh speed limit, hence the rpm on the engine is less than 1500 and the alternator was not kicking out much charge.
And to further compound things, we have a several level crossings near me, where you have to wait ages for the boom gates, so I had been turning the engine off to save fuel ( like all modern cars do automatically)

Unfortunately Subaru was not designed to keep the battery charged under these conditions

Thanks for the collaboration, Roger!
In my case the connection is not by ODB … the STM32 is directly connected to the serial communication pins of the central injection processor.
Its placement was interesting because my fear in leaving the STM32 turned on all the time (even in standby most of the time) is precisely this, exhausting the battery of the car, further because the car is of secondary use and rarely leaves the garage. In this sense, the idea of using the “backup” capacitor just to return the pointers to the starting position was perhaps the most feasible possibility.
One more possibility is to use a rechargeable 2700mah 18650 Li-ion battery, which would power the system when the main source was turned off …
What do you think? It’s viable?


RogerClark
Wed Nov 22, 2017 8:42 pm
I forgot to say, my problem was compounded by leaving the car at the airport for a month, but..

Yes. do not leave the STM32 running all the time.

Lipo sounds good idea, as in its Low power / sleep mode, it will run for many months on a 2000mAH battery.

Probably run it from tha battery all the time, and charge the battery when the car is running..and use one GPIO pin as a sense for when the car is running.
I.e trigger wake from sleep when that line goes high, and then in you code set an interrupt for that line going low and go into sleep mode when that happens


ahull
Wed Nov 22, 2017 9:01 pm
You could use a relay and a couple of optoisolators in parallel. The ignition switches on one optoisolator, this provides power to the relay coil, the relay then provides power to the 5V PSU/regulator for the STM, which in turn on power up, switches on optoisolator 2 which is parallel with optoisolator 1 (both being in series with the relay coil). (You may need a couple of diodes to stop the two optoisolator transistors from frying each other, I’m building this circuit in my head as i type.. so you will need your proof of concept needs to be breadboarded).

When the ignition goes off, so does optoisolator 1, but this doesn’t kill power to the STM, since it is now keeping itself powered with optoisolator 2 controlling the 5V power supply relay.. When the STM has done its post “ignition off” housekeeping, then it switches off the second optoisolator an kills its own power. At this point, the system is completely disconnected from power. It only wakes up again when power is re-applied and optoisolator 1 fires up once more.

You can do the same trick with a 12V and a 5V or 3V3 relay with their normally open contacts in parallel. On ignition, the 12V relay closes contacts and provides power, PSU for the 5V for the STM and the 5V for the relay the STM wakes up, when the 5V comes on, the STM switches on the 5V relay and its contacts provide the alternative STM and 5V relay power route, since the 5V relay normally open contacts are in parallel with the 12V relay normally open contacts. Both the 12V and the 5V relays need to open before power is completely cut, and since the STM controls the 5V relay, it only kills its own power once its work is done.

If you can’t get your head round my description, then let me know and if I have a bit of time, I’ll attempt a schematic.


RogerClark
Wed Nov 22, 2017 9:09 pm
Andy

Why does the power have to be physically removed from the STM32 by a relay ?

Won’t just putting it into low power mode, reduce the current consumption considerably ?


magflip
Wed Nov 22, 2017 9:14 pm
[ahull – Wed Nov 22, 2017 9:01 pm] –
You could use a relay and a couple of optoisolators in parallel. The ignition switches on one optoisolator, this provides power to the relay coil, the relay then provides power to the 5V PSU/regulator for the STM, which in turn on power up, switches on optoisolator 2 which is parallel with optoisolator 1 (both being in series with the relay coil). (You may need a couple of diodes to stop the two optoisolator transistors from frying each other, I’m building this circuit in my head as i type.. so you will need your proof of concept needs to be breadboarded).

When the ignition goes off, so does optoisolator 1, but this doesn’t kill power to the STM, since it is now keeping itself powered with optoisolator 2 controlling the 5V power supply relay.. When the STM has done its post “ignition off” housekeeping, then it switches off the second optoisolator an kills its own power. At this point, the system is completely disconnected from power. It only wakes up again when power is re-applied and optoisolator 1 fires up once more.

You can do the same trick with a 12V and a 5V or 3V3 relay with their normally open contacts in parallel. On ignition, the 12V relay closes contacts and provides power, PSU for the 5V for the STM and the 5V for the relay the STM wakes up, when the 5V comes on, the STM switches on the 5V relay and its contacts provide the alternative STM and 5V relay power route, since the 5V relay normally open contacts are in parallel with the 12V relay normally open contacts. Both the 12V and the 5V relays need to open before power is completely cut, and since the STM controls the 5V relay, it only kills its own power once its work is done.

If you can’t get your head round my description, then let me know and if I have a bit of time, I’ll attempt a schematic.

Once again, thank you for the collaboration ahull !

I even started to follow the reasoning but in the middle of the way I ended up losing myself and however much I have read a few times, I could not understand what that scheme would be like. I would appreciate it if you could help me.


ahull
Thu Nov 23, 2017 1:06 am
Here is a similar (arguably better) idea using two mosfets.

http://www.mosaic-industries.com/embedd … ing-on-off

The description of how it works should give you a few ideas.

My idea is a little simpler, one detail I forgot to clarify the 12v feed for the 5v supply is not switched by the ignition, the 12v feed for the 12v relay is.

I may get a chance to sketch up a schematic tomorrow, but it is looking like a busy day, so we will see how that goes.


Nutsy
Fri Dec 22, 2017 12:59 am
I think Roger may have been thinking of me when he mentioned people doing good a motor gauge… Typing on my phone so bare with me…

Currently my projects stalled… Getting getting a third party to look at my display design. See if he can get it working…

Now. I don’t recommend the nextion… I did get it working on my project, but it has some really severe limitations if you want to get decent graphics working.

There is no kids go of frame buffer. So fast changing info will tare badly. No proper alpha support either. So complex over lapping elements won’t work. A terrible cumbersome library. That’s a nightmare to build with.

What I recommend is, go with a ftdi eve display. It’s more complex to build with but you’ll get far better graphic results and more control. You also have access to a whole host of image manipulation code that can really push the fidelity of your work. Also proper rotation Controls unlike nextion.

The downside to going with eve is, it costs more. What’s stalled my project is I made a custom display board with an eve controller chip, but I’m struggling to get the lid display I chose to function properly on it. It would be wise to buy a prepare board… A few are available. Bridge tech do a few and hotmcu as well… It’s a bit more of a learning curve but it’s worth it.


Nutsy
Fri Dec 22, 2017 1:04 am
It took a moment but I understood what Andy said… Yay me I’m start…

That’s a rather cool idea. I might have to nick it :p


BennehBoy
Thu Dec 28, 2017 6:46 am
@ahull presumably using low power mode will require a psu that can work with such a low power draw… ie the OP needs to be careful what he chooses if he goes down this route.

ahull
Fri Dec 29, 2017 11:28 am
[BennehBoy – Thu Dec 28, 2017 6:46 am] –
@ahull presumably using low power mode will require a psu that can work with such a low power draw… ie the OP needs to be careful what he chooses if he goes down this route.

That’s a good point. Things like powerbanks give up and cut the power when the current draw goes below a certain level, so you may find yourself literally powerless if the source is “intelligent” in any sense.


Leave a Reply

Your email address will not be published. Required fields are marked *