Complete dashboard for an old car

What are you developing?
magflip
Posts: 13
Joined: Sat May 20, 2017 3:22 pm

Complete dashboard for an old car

Post by 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.

Code: Select all

#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;
  }
}


User avatar
RogerClark
Posts: 7447
Joined: Mon Apr 27, 2015 10:36 am
Location: Melbourne, Australia
Contact:

Re: Complete dashboard for an old car

Post by 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
Posts: 1813
Joined: Mon Oct 19, 2015 12:06 am
Location: Munich, Germany

Re: Complete dashboard for an old car

Post by 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
Posts: 13
Joined: Sat May 20, 2017 3:22 pm

Re: Complete dashboard for an old car

Post by magflip » Tue Nov 21, 2017 7:26 pm

stevestrong wrote:
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?

User avatar
ahull
Posts: 1650
Joined: Mon Apr 27, 2015 11:04 pm
Location: Sunny Scotland
Contact:

Re: Complete dashboard for an old car

Post by 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.
- Andy Hull -

magflip
Posts: 13
Joined: Sat May 20, 2017 3:22 pm

Re: Complete dashboard for an old car

Post by magflip » Wed Nov 22, 2017 5:04 pm

ahull wrote:
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!

User avatar
RogerClark
Posts: 7447
Joined: Mon Apr 27, 2015 10:36 am
Location: Melbourne, Australia
Contact:

Re: Complete dashboard for an old car

Post by 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
Posts: 13
Joined: Sat May 20, 2017 3:22 pm

Re: Complete dashboard for an old car

Post by magflip » Wed Nov 22, 2017 8:34 pm

RogerClark wrote:
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?

User avatar
RogerClark
Posts: 7447
Joined: Mon Apr 27, 2015 10:36 am
Location: Melbourne, Australia
Contact:

Re: Complete dashboard for an old car

Post by 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

User avatar
ahull
Posts: 1650
Joined: Mon Apr 27, 2015 11:04 pm
Location: Sunny Scotland
Contact:

Re: Complete dashboard for an old car

Post by 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.
- Andy Hull -

Post Reply