Arduino Gauges

ifrythings

Full Access Member
Joined
Dec 17, 2011
Posts
734
Reaction score
485
Location
BC
I figured I should start my own thread for this, here’s my version of an arduino and gauges. I wanted something that would go into the factory location and replace those 4 useless gauges with something that is more accurate. These little 0.96” OLED displays fit nicely, have great light output and they don’t get washed out in direct sunlight like lcd displays do. In the pictures there are no sensors hooked up (that entire part of the circuit is missing right now) and it’s just displaying whatever.

You must be registered for see images attach
You must be registered for see images attach


I’m going to move the whole set of displays down about 1/4” so they look centered on the left side of the cluster, need to sand the lines out of the 3D print so it’s smooth. The order of the gauges will be changed also.
 

Jesus Freak

Full Access Member
Joined
Apr 3, 2022
Posts
3,293
Reaction score
3,746
Location
Crestview, FL
It's almost like Knight Rider. This might figure in to a discussion about "Could KITT have been anything different than a 3rd gen firebird" that my wife and I have. Your dash might convince us.
 

Dirtleg

Full Access Member
Joined
Aug 11, 2007
Posts
1,319
Reaction score
577
Location
Cloverdale VA
Any more updates on this? I am going to copy your work here on some level. Just ordered 3 uno's and 12, .96" displays. Plan on making a controller for my in bed tank and displaying it's level along with other data. Would likely get rid of my A-pillar 4 gauge cluster as well.
 

ifrythings

Full Access Member
Joined
Dec 17, 2011
Posts
734
Reaction score
485
Location
BC
Been busy with a new job so I haven’t been working on it a ton but I have found that the I2C (IIC) interface is the bottle neck running 9 displays, I have the I2C interface running at 1Mhz instead of 400Khz which helps and I’m only updating displays that have changed in valve which helps a lot. I’m going to change it over to SPI which can run way higher (8-10Mhz) and doesn’t have the added overhead of the I2C protocol.

Few things I have notice is order all your displays at the same time from the same seller as they have small variations in brightness and color shade, 9 displays draws nothing for current (1-2mA for all), fonts take up your biggest amount of program memory so try not to put more than one in your project, I’m using 2 but the bigger font set is just the numbers only and the smaller fonts is the reduced set so all the weird shaped fonts aren’t included which saves space.

Not sure why you need 3 unos as one can do it or a mega can easily do what you want, also if your displays are I2C you can only put one (if they don’t have the address jumper) or two (if they have the address jumper) on one I2C bus, I got around this unsung an I2C expander which allows you to individually put each display on a selectable port when they all have the same address. In my case I could have 8 displays with the same address controlled by the expander and the 9th one I had to change the address by modifying the board it came on as it didn’t have the address selector jumper.

I’m sure I’m missing some details but any questions I’ll try and answer.
 

SkylabTech86IDI

Full Access Member
Joined
Aug 31, 2022
Posts
262
Reaction score
162
Location
Southeast US
Been busy with a new job so I haven’t been working on it a ton but I have found that the I2C (IIC) interface is the bottle neck running 9 displays, I have the I2C interface running at 1Mhz instead of 400Khz which helps and I’m only updating displays that have changed in valve which helps a lot. I’m going to change it over to SPI which can run way higher (8-10Mhz) and doesn’t have the added overhead of the I2C protocol.

Few things I have notice is order all your displays at the same time from the same seller as they have small variations in brightness and color shade, 9 displays draws nothing for current (1-2mA for all), fonts take up your biggest amount of program memory so try not to put more than one in your project, I’m using 2 but the bigger font set is just the numbers only and the smaller fonts is the reduced set so all the weird shaped fonts aren’t included which saves space.

Not sure why you need 3 unos as one can do it or a mega can easily do what you want, also if your displays are I2C you can only put one (if they don’t have the address jumper) or two (if they have the address jumper) on one I2C bus, I got around this unsung an I2C expander which allows you to individually put each display on a selectable port when they all have the same address. In my case I could have 8 displays with the same address controlled by the expander and the 9th one I had to change the address by modifying the board it came on as it didn’t have the address selector jumper.

I’m sure I’m missing some details but any questions I’ll try and answer.
This is a fascinating read, and now I’m lost in the sauce. The extent of my experience with arduino is using an Uno to measure the lux in a room to turn a lamp on and off automatically, and I followed a tutorial for that.

How do you convert the analog electrical signals (coolant temp, battery voltage,oil pressure, fuel level, etc) into a digital output on the display? Are you able to share any of the code that you used?

Is the arduino running off of the truck’s 12 volt DC system, and do you use some sort of converter to make the board run on 12v DC versus 120hertz AC?
 
Last edited:

Dirtleg

Full Access Member
Joined
Aug 11, 2007
Posts
1,319
Reaction score
577
Location
Cloverdale VA
Thanks for the update.

3 Unos because it was a package deal.
I'm thinking I might run all the fuel related stuff off 1 and the truck related stuff off another.

And yes I did get all my displays from the same vendor on 1 order.

Thanks for the info about the I2C issues and solution. I am sure I will have to knock a few walls down with my head before I have a working setup.

When I do get something going I'll be sure to share it with the collective.
 

Dirtleg

Full Access Member
Joined
Aug 11, 2007
Posts
1,319
Reaction score
577
Location
Cloverdale VA
Okay my Arduinos and screens showed up about 2 hours ago. It didn't take long to get to this point.
You must be registered for see images attach


And down the rabbit hole we go.

@ifrythings, I was shocked by how much memory just adding the libraries consumed. But you called it.

I wonder if there is a way to streamline/minimize the library content. My guess is no as they don't appear to be universal amongst screen types.

I wonder if a final version would be better on a raspberry pi due to memory limitations? Or a bigger arduino than a nano might help.

I am at a good starting point anyway.
 

ifrythings

Full Access Member
Joined
Dec 17, 2011
Posts
734
Reaction score
485
Location
BC
For some reason I didn't get notified about your earlier post, maybe because you tagged me I got notified this time?;Poke

I'm using the u8x8 library, when you call
Code:
 u8x8.setFont(u8x8_font_8x13_1x2_r);
the "r" at the end sets the reduced front set, "f" uses the full set and "n" only has numbers. I'm using the "8x13_1x2_r" font for the yellow lettering and the "inr33_3x6_n" for the numbers.

I'm using a CAN bus so I don't have to drag 20 wires into the cab and up to the cluster, with CAN I have 2 wires.

Here's my code which uses CAN but can be easily changed to suite your needs.
Note: there is 2 files below, one is the main code and the second is the variables, you can copy the variables into the main code if you don't want to have a separate file.

Sketch uses 17896 bytes (58%) of program storage space. Maximum is 30720 bytes.
Global variables use 800 bytes (39%) of dynamic memory, leaving 1248 bytes for local variables. Maximum is 2048 bytes.

Code:
#include <U8x8lib.h>                                                                
#include <mcp_can.h>                                              
#include <SparkFun_I2C_Mux_Arduino_Library.h>                              
#include "Variables.h"

U8X8_SSD1306_128X64_NONAME_HW_I2C u8x8(U8X8_PIN_NONE);
QWIICMUX I2Cmux;                                      
                                                       
/*                                                    
    OLED Screen  Layout                                
   _____   _____   _____                              
  |     | |     | |     |                                          
  |  1  | |  2  | |  3  |                                          
  |_____| |_____| |_____|                                          
   _____   _____   _____                                          
  |     | |     | |     |                                          
  |  0  | |  5  | |  4  |                                          
  |_____| |_____| |_____|                                          
   _____   _____   _____                                          
  |     | |     | |     |                                          
  |  8  | |  7  | |  6  |                                          
  |_____| |_____| |_____|                                          
*/                                                                
                                      // Analog                    
#define BAT 1                                                         // Battery Voltgae Sensor
#define ECT 2                                                         // Engine Coolant Temperatur Sensor
#define EOT 5                                                         // Enigne Oil Temperature Sensor
#define FTL 8                                                         // Fuel Tank Level Sensor
#define EGT 4                                                         // Exhaust Gas Temperature Sensor
#define TOT 7                                                         // Transmission Oil Temperature Sensor
#define MGP 6                                                         // Manifold Gauge Pressure Sensor
#define EOP 3                                                         // Engine Oil Pressure Sensor
#define FPS 0                                                         // Fuel Pressure Sensor
#define TRS 9                                                         // Transmission Range Sensor
#define HS_CAN_INT 2                                                  // Set CAN INT Input
MCP_CAN HS_CAN(10);                                                   // Set CS to pin 10

void setup(){                                                      
//  Serial.begin(500000);                                          
  pinMode(HS_CAN_INT, INPUT);                                         // Configuring pin for /INT input
  // Initialize MCP2515 running at 16MHz with a baudrate of 500kb/s, masks and filters disabled.
  HS_CAN.begin(MCP_ANY, CAN_500KBPS, MCP_16MHZ);                  
  HS_CAN.setMode(MCP_NORMAL);                                         // Set operation mode to normal so the MCP2515 sends acks to received data.
  Wire.begin();                                                      
  I2Cmux.begin();                                                    
  I2Cmux.setPortState(0xFF);                                          // Turn on all I2Cmux ports
  u8x8.begin();                                                       // Initialize 8 OLED displays at once on I2C Mux
  u8x8.setI2CAddress(0x7A);                                           // Change I2C address to 0x7A
  u8x8.begin();                                                       // Initialize OLED display on address 0x7A
  u8x8.setBusClock(1000000);                                          // Set I2C Bus to 1Mhz (overclocked from 400Khz)
  u8x8.setFont(u8x8_font_8x13_1x2_r);                                 // Set Font to 8x13 pixels
  u8x8.drawString(2,0,"FUEL LEVEL %");                                // Print characters
  u8x8.setI2CAddress(0x78);                                           // Change I2C address to 0x78
  I2Cmux.setPort(ECT);                                                // Switch I2Cmux port
  u8x8.drawString(3,0,"COOLANT C");                                   // Print characters
  I2Cmux.setPort(EOT);                                                // Switch I2Cmux port
  u8x8.drawString(6,0,"OIL C");                                       // Print characters
  I2Cmux.setPort(EOP);                                                // Switch I2Cmux port
  u8x8.drawString(5,0,"OIL PSI");                                     // Print characters
  I2Cmux.setPort(FTL);                                                // Switch I2Cmux port
  u8x8.drawString(2,0,"FUEL LEVEL %");                                // Print characters
  I2Cmux.setPort(BAT);                                                // Switch I2Cmux port
  u8x8.drawString(4,0,"BATTERY V");                                   // Print characters
  I2Cmux.setPort(EGT);                                                // Switch I2Cmux port
  u8x8.drawString(2,0,"EXHUAST GAS F");                               // Print characters
  I2Cmux.setPort(MGP);                                                // Switch I2Cmux port
  u8x8.drawString(4,0,"BOOST PSI");                                   // Print characters
  I2Cmux.setPort(TOT);                                                // Switch I2Cmux port
  u8x8.drawString(1,0,"TRANSMISSION C");                              // Print characters
  I2Cmux.setPort(FPS);                                                // Switch I2Cmux port
  u8x8.drawString(4,0,"FUEL PSI");                                    // Print characters
  u8x8.setFont(u8x8_font_inr33_3x6_n);                                // Set Font to inr33 3x6 numbers
 
//                                        Not implemented yet
//  I2Cmux.setPort(TRS);                                              // Switch I2Cmux port
//  u8x8.setFont(u8x8_font_inr21_2x4_r);                              // Set Font to inr21 2x4 reduced
//  u8x8.drawString(0,4,"P R N D 2 1");                               // Print characters
}                                                                                                        

void loop(){                                                      

  if(!digitalRead(HS_CAN_INT)){                                       // If HS_CAN_INT pin is low, read receive buffer
    HS_CAN.readMsgBuf(&rxId, &len, rxBuf);                            // Read data: len = data length, buf = data byte(s)  
    if(rxId == 0x422){                                                // From PCM packet ID:422
      while(len--) * (data422 + len) =* (rxBuf + len);                // Move data from CAN packet to memory
    }
    if(rxId == 0x423){                                                // From PCM packet ID:423
      while(len--) * (data423 + len) =* (rxBuf + len);                // Move data from CAN packet to memory
    }                                                              
  } // End of CAN Receive                                          

  bat = can_bat_h << 8 | can_bat_l;                                   // Recombine data (8B + 8B = 16B)
  bat /= 10;                                                          // Remove offset (123 -> 12.3)
  if(bat != old_bat){                                                 // If value hasn't changed, don't update display
    old_bat = bat;                                                    // Move new value to old value for compare next loop
    I2Cmux.setPort(BAT);                                              // Switch I2Cmux port
    drawScreen(bat);                                                  // Print characters
  }                                                                

  ect = can_ect - 40;                                                 // Remove offset (140 -> 100)
  if(ect != old_ect){                                                 // If value hasn't changed, don't update display
    old_ect = ect;                                                    // Move new value to old value for compare next loop
    I2Cmux.setPort(ECT);                                              // Switch I2Cmux port
    drawScreen(ect);                                                  // Print characters
  }                                                                

  eot = can_eot - 40;                                                 // Remove offset (140 -> 100)
  if(eot != old_eot){                                                 // If value hasn't changed, don't update display
    old_eot = eot;                                                    // Move new value to old value for compare next loop
    I2Cmux.setPort(EOT);                                              // Switch I2Cmux port
    drawScreen(eot);                                                  // Print characters
  }                                                                

  eop = can_eop;                                                    
  if(eop != old_eop){                                                 // If value hasn't changed, don't update display
    old_eop = eop;                                                    // Move new value to old value for compare next loop
    I2Cmux.setPort(EOP);                                              // Switch I2Cmux port
    drawScreen(eop);                                                  // Print characters
  }                                                                

  tot = can_tot - 40;                                                 // Remove offset (140 -> 100)
  if(tot != old_tot){                                                 // If value hasn't changed, don't update display
    old_tot = tot;                                                    // Move new value to old value for compare next loop
    I2Cmux.setPort(TOT);                                              // Switch I2Cmux port
    drawScreen(tot);                                                  // Print characters
  }                                                                

//                                  Not implemented yet
  trs = can_trs;                                                  
  if(trs != old_trs){                                                 // If value hasn't changed, don't update display
    old_trs = trs;                                                    // Move new value to old value for compare next loop
    I2Cmux.setPort(TRS);                                              // Switch I2Cmux port
//    drawScreen(trs);                                                // Print characters
  }                                                                

  ftls = can_ftls / 2.55;                                             // Remove offset (255 -> 100%)
  if(ftls != old_ftls){                                               // If value hasn't changed, don't update display
    old_ftls = ftls;                                                  // Move new value to old value for compare next loop
    I2Cmux.setPort(255);                                              // Turn off MUX
    u8x8.setI2CAddress(0x7A);                                         // Change I2C address to 0x7A
    drawScreen(ftls);                                                 // Print characters
    u8x8.setI2CAddress(0x78);                                         // Change I2C address to 0x78
  }                                                                

  fps = can_fps_h << 8 | can_fps_l;                                   // Recombine data (8B + 8B = 16B)
  fps /= 10;                                                          // Remove offset (123 -> 12.3)
  if(fps != old_fps){                                                 // If value hasn't changed, don't update display
    old_fps = fps;                                                    // Move new value to old value for compare next loop
    I2Cmux.setPort(FPS);                                              // Switch I2Cmux port
    drawScreen(fps);                                                  // Print characters
  }                                                                

  egt = can_egt_h << 8 | can_egt_l;                                   // Recombine data (8B + 8B = 16B)
  if(egt != old_egt){                                                 // If value hasn't changed, don't update display
    old_egt = egt;                                                    // Move new value to old value for compare next loop
    I2Cmux.setPort(EGT);                                              // Switch I2Cmux port
    drawScreen(egt);                                                  // Print characters
  }                                                                

  mgp = can_mgp_h << 8 | can_mgp_l;                                   // Recombine data (8B + 8B = 16B)
  mgp /= 10;                                                          // Remove offset (123 -> 12.3)
  if(mgp != old_mgp){                                                 // If value hasn't changed, don't update display
    old_mgp = mgp;                                                    // Move new value to old value for compare next loop
    I2Cmux.setPort(MGP);                                              // Switch I2Cmux port
    drawScreen(mgp);                                                  // Print characters
  }                                                                

}// End of Loop                                                    

void drawScreen(int i){                                            
  byte j = I2Cmux.getPort();                                          // Move current selected I2C mux port for later evaluation
  char abc[4];                                                        // variable array
  sprintf(abc, "%04d", i);                                            // convert int to char
  if(i < -99);                                                        // do nothing
  else if(i < -9)abc[0] = 0x20, abc[1] = 0x2D;                        // Blank digit 1 spot
  else if(i < 0)abc[0] = 0x20, abc[1] = 0x20, abc[2] = 0x2D;          // Blank digit 1 spot
  else if(i < 10)abc[0] = 0x20, abc[1] = 0x20, abc[2] = 0x20;         // Blank digit 2 spot
  else if(i < 100)abc[0] = 0x20, abc[1] = 0x20;                       // Blank digit 1 spot
  else if(i < 1000)abc[0] = 0x20;                                     // Blank digit 1 spot
  if(j == MGP || j == BAT || j == FPS)abc[4] = abc[3], abc[3] = 0x2E; // Insert decimal (123 = 12.3)
  u8x8.drawString(0,2,abc);                                           // Print characters

}// End of drawScreen function                                    


//********************************Variables.h file**************************************

// Variables:                                                          
long unsigned int rxId;                                                 // CAN Bus Receive Message ID (0x00 - 0x7FF)
unsigned char len;                                                      // CAN Bus Receive Message Length (8 bytes)
unsigned char rxBuf[8];                                                 // CAN Bus Receive Buffer array (8 bytes deep)
int ect;                                                                // Engine Coolant Temperature
int eot;                                                                // Engine Oil Temperature
int tot;                                                                // Transmission Oil Temperature
int egt;                                                                // Exhaust Gas Temperature
int mgp;                                                                // Manifold Guage Pressure (Boost)
int eop;                                                                // Engine Oil Pressure
int fps;                                                                // Fuel Pressure Sensor
int bat;                                                                // Battery Voltage
int ftls;                                                               // Fuel Tank Level Sensor
int trs;                                                                // Transmission Range Sensor

int old_ect = 1;                                                        // Store old Engine Coolant Temperature value, set to 1 so it updates at power on
int old_eot = 1;                                                        // Store old Engine Oil Temperature, set to 1 so it updates at power on
int old_tot = 1;                                                        // Store old Transmission Oil Temperature, set to 1 so it updates at power on
int old_egt = 1;                                                        // Store old Exhaust Gas Temperature, set to 1 so it updates at power on
int old_mgp = 1;                                                        // Store old Manifold Guage Pressure (Boost), set to 1 so it updates at power on
int old_eop = 1;                                                        // Store old Engine Oil Pressure, set to 1 so it updates at power on
int old_fps = 1;                                                        // Store old Fuel Pressure Sensor, set to 1 so it updates at power on
int old_bat = 1;                                                        // Store old Battery Voltage value, set to 1 so it updates at power on
int old_ftls = 1;                                                       // Store old Fuel Tank Level Sensor, set to 1 so it updates at power on
int old_trs = 1;                                                        // Store old Transmission Range Sensor, set to 1 so it updates at power on

// CAN Bus Packets Decode                                              
//                                   EGT1  EGT1    MAP   MAP                        
// Rx               ECT   EOT   TOT  HIGH   LOW   HIGH   LOW   EOP    
byte data422[8] = {0x28, 0x28, 0x28, 0x00, 0x00,  0x00, 0x00, 0x00};    // should receive every 100mS
//byte data422[8] = {0x81, 0x81, 0x64, 0x03, 0x52,  0x04, 0xE2, 0x2D};  // for testing without CAN network

//                  FPS   FPS  BATT  BATT                  
// Rx              HIGH   LOW  HIGH   LOW  FTLS   TRS      
byte data423[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};     // should receive every 100mS
//byte data423[8] = {0x01, 0x5E, 0x05, 0xA0, 0xBF, 0x00, 0x00, 0x00};   // for testing without CAN network
                                                                                   
#define can_ect data422[0]                                              // Engine Coolant Temperature
#define can_eot data422[1]                                              // Engine Oil Temperature
#define can_tot data422[2]                                              // Transmission Oil Temperature
#define can_egt_h data422[3]                                            // Exhaust Gas Temperature High Byte
#define can_egt_l data422[4]                                            // Exhaust Gas Temperature Low Byte
#define can_mgp_h data422[5]                                            // Manifold Gauge Pressure High Byte
#define can_mgp_l data422[6]                                            // Manifold Gauge Pressure Low Byte
#define can_eop data422[7]                                              // Engine Oil Pressure
                                                                       
#define can_fps_h data423[0]                                            // Fuel Pressure High Byte
#define can_fps_l data423[1]                                            // Fuel Pressure Low Byte
#define can_bat_h data423[2]                                            // Battery Voltage High Byte
#define can_bat_l data423[3]                                            // Battery Voltage Low Byte
#define can_ftls data423[4]                                             // Fuel Tank Level Percent
#define can_trs data423[5]                                              // Transmission Range Sensor

// End of Variables.h file
 

Dirtleg

Full Access Member
Joined
Aug 11, 2007
Posts
1,319
Reaction score
577
Location
Cloverdale VA
Wow. Thanks for all of that code. Very generous of you to make that available.

Now I need to grab some sensors and start experimenting. I'm thinking V1 will end up on the Massey Ferguson as it's gauges are maybe not so reliable and I've just finished rebuilding the engine and IP for it.
 

Forum statistics

Threads
91,284
Posts
1,129,796
Members
24,099
Latest member
IDIBronco86

Members online

Top