Methanol injection controller, oil pressure, EGT, online on phone + screen

Hej!,

I needed a device that could:
Act as a Methanol injection controller (boost sensor is connected)
See and edit the meth controller values on my phone (it connects to the phones WIFI hotspot and you can access the controller via a webpage hosted on the arduino)
Monitor the Oil pressure (displayed on a Oled screen)
Monitor the EGT. (displayed on a Oled screen)

So i made this code.

Arduino code:

// Pins used:
// A0 = Boost pressure sensor
// A1 = Oil pressure sensor
// INPUT 10, 11, 12, 13 = THERMOCOUPLE MAX31856 (EGT TEMP.)
// OUTPUT 5 = Siganl to turn on meth pump
// please edit the WIFI settings before use. (wifi name and password.
// Please edit the URL: mycarinfo.nectioon.com to enable to send the arduino IP address to a outside webserver. If you got trouble finding the ip (some phones dont show the ip of the connected devies) 


#include <SPI.h>
#include <WiFiNINA.h>
#include <Wire.h>
#include <Average.h>
#include <FlashStorage.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Adafruit_MAX31856.h>

int keyIndex = 0; // your network key Index number (needed only for WEP)
char userSSID[100] = "bmw3"; // WIFI NAME THAT THIS ARDUINO WILL TRY TO CONNECT TO!
char userSSIDpass[100] = "hahahahaha"; // WIFI PASSWORD THAT THIS ARDUINO WILL TRY TO CONNECT TO!

//SET up FLASH STORAGE
FlashStorage(BOOSTsave, int); // INITIATE FLASH STORAGE AKA EEPROM FOR TARGETBOOST.
FlashStorage(CALI, int); // INITIATE FLASH STORAGE AKA EEPROM FOR ZERO PRESSURE CALIBRATION VALUE.
FlashStorage(updateRATEsave, int); // INITIATE FLASH STORAGE AKA EEPROM FOR updateRATE.

// SET UP THE DISPLAY
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
#define OLED_RESET     -1 // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS 0x3C ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

// SET UP THERMOCOUPLE MAX31856 (EGT TEMP.)
// Use software SPI: CS, DI, DO, CLK
Adafruit_MAX31856 maxthermo = Adafruit_MAX31856(10, 11, 12, 13);

// SET UP VDO OIL PRESSURE SENDER
const int analogInPin = A1;  // Analog input for VDO OIL PRESSURE SENDER

// GLOBAL VARS.
// This is nessacery to use. Because we need to update data about boost, egt and oil pressure.
// Controle a pump x amount of time.
// Keep track on a wifi connection.
// Keep track on a website hosted on this arduino
// all at the same time.
int targetBOOST = BOOSTsave.read();  // GET target boost pressure FROM "EEPROM".
int offset = CALI.read(); // Get calibratiobn value, GET DATA FROM "EEPROM"
int updateRATE = updateRATEsave.read(); // Get update rate for js script, GET DATA FROM "EEPROM"
int COUNTER = 0; //This var. holds the amount of loop scans, used for the WIFI connection (may be reset after x scans)
float pressure; // Actual boost pressure.
int boostCOUNTER = 100; //Boost counter prevent the METH to be on for a long time (so we dont risk over fueling)
int updateSTAT = 0; // Is the meth pump turned on? 1= yes, 0= no
int offsetTMP = 0; //used ti both control the web interface (eg. pump testing) and to update the calibration value  
int updateBOOST = 0; // used to controle what part of the website to show.
int lastBOOST = 0; // Holds information about the last high boost pressure.
int lastBOOSTtmp = 0; // used to keep track of last high boost pressure.
int avg_size = 99; // #pts to average over
Average<long> ave(avg_size); //idk cant remember, but something to do with the Boost pressure sensor
int status = WL_IDLE_STATUS; //var. for the server part
WiFiServer server(80); //The Port the Webserver is listening on

void setup() {
  if(offset <= 200){
  offset = 886; // set calibration value to default 463 if "eeprom" contains no data.
  }
  pinMode(5, OUTPUT);    // sets the digital pin 5 as output PIN 5 = signal to slave (BOOST PRESSURE IS REACHED)
  digitalWrite(5, LOW); // default to high

  if(updateRATE <= 1 || updateRATE > 10000){ //if JS refresh rate is fuckt return to default
    updateRATE = 3000; // default JS refresh rate
  }                                                                                                                                                                                   

  WIFIsetup(); // Sets up the WIFI connection AND Webserver

}

void loop() {
  
  COUNTER++; // count loop scan
  WIFImaintainer(); // Keeps the WIFI connection alive, if WIFI is connected.
  MethController(); //Controls the Meth pump
  SendDisplay(OilPressure(), EGT(), pressure, targetBOOST, lastBOOST, updateSTAT); // updates the display.
  WebServer(); // Hnadles the webserver
                                                                                                                                             
}






// FUNCTIONS BELOW

void WIFIsetup(){ // Sets up the WIFI connection.
   WiFi.config(IPAddress(10, 20, 30, 40)); // We need to create a Access point first, or else the webserver wont work, due to a FUCKING LUDER bug
   status = WiFi.beginAP("MyCarInfo", "password"); // Start AP
   delay(3000);
   server.begin(); // staring webserver
   delay(3000);
   WiFi.end(); // We need to turn off the WIFI to switch from AP to WIFI client, due to a "bug"
   while (status != WL_CONNECTED) {
      COUNTER++;
      status = WiFi.begin(userSSID, userSSIDpass); //connect to wifi (ONLY 3 TRYS)
      delay(1000);
      if(COUNTER >= 3){
        COUNTER = 0;
        break;
      }
    }
   delay(1000);
   WiFiClient client;
   if (client.connect("mycarinfo.nectioon.com", 80)) { // This sends the IP and SSID to a sql server, so the user know wich IP to acces this system on.. This is due to not many phones have the ability to show the ip of connected tethering devices. 
      IPAddress ip = WiFi.localIP(); // Get the system IP
      client.print("GET /?ip="); client.print(ip); client.print("&ssid="); client.print(WiFi.SSID()); client.println(" HTTP/1.1"); //This sends the data to server
      client.println("Host: mycarinfo.nectioon.com");
      client.println("Connection: close");
      client.println();
      delay(3000);
    }
    server.begin(); //starts webserver.

   return;
}

void MethController(){
  int rawValue = 0;
  for (int x = 0; x < 10; x++) rawValue = rawValue + analogRead(A3); // read raw data from pressure sensor and add them together in a for loop, for more presice messurement.
  
  pressure = (rawValue - offset) * 700.0 / (9630 - offset) * 10; // pressure conversion
  ave.push(pressure); // Get 
  pressure = ave.mean();
  
  boostCOUNTER++; //GLOBAL VAR. Boost counter prevent the METH to be on for a long time (so we dont risk over fueling)

  if(targetBOOST <= pressure && boostCOUNTER >= 93){ 
    digitalWrite(5, HIGH); // TURNS ON METH
    updateSTAT = 1;
    
  }
  if(targetBOOST >= pressure){
    digitalWrite(5, LOW); // TURN OFF METH
    if(updateSTAT == 1){
      boostCOUNTER = 1;
    }
    updateSTAT = 0;
  }
  if(targetBOOST <= 2000){ // THIS WILL PREVENT A BUG FROM OVERFUELING WITH METH.. IF BUG IS DETECTED ACTIVATE FAIL MODE.
    targetBOOST = 9000;
  }

  if(pressure >= 2500){ // This updates the last high boost recording
    if(pressure > lastBOOSTtmp){
      lastBOOST = pressure;
      lastBOOSTtmp = pressure;
    }
  } else {
     lastBOOSTtmp = pressure;
  }

  if(offsetTMP == 101404){ // THIS WILL RUN THE PUMP FOR 3 SEC. iF 101404 IS ENTERED AS TARGETBOOST
    digitalWrite(5, HIGH); // TURNS ON METH
    delay(3000);
    digitalWrite(5, LOW); // TURN OFF METH
    offsetTMP = 0;
  } else if(offsetTMP > 12473100 && offsetTMP < 1247310000){ // This sets the new page update (the JS update interval)
    updateRATE = (offsetTMP - 12473000);
    if(updateRATE >= 999){
      updateRATE = (offsetTMP - 124730000);
    }
    updateRATEsave.write(updateRATE);
    offsetTMP = 0;
  } else if(offsetTMP == 5473404){ // This this will enable safemode
    updateRATE = 3;
    updateRATEsave.write(updateRATE);
    offsetTMP = 0;
  } 
  
    return;
}   

void WIFImaintainer(){
  if(COUNTER >= 60000){ // DUE TO A FUCKING BUG WHERE THE ARDUINO BECOMES SLOW WHEN NOT CONNECTED TO WIFI, WE NEED TO ONLY TRY TO RECONNECT EVERY 60000 LOOP SCANS... OTHERWISE THE ARDUINO BECOMES SLOW AND MAY OVERFUEL ENGINE WTIH METH.
    COUNTER = 0;
    String ssidStatus = WiFi.SSID(); //due to a FUCKING BUG where the STATUS var. will not change, if the connection fucks up. we need to use the SSID to maintain the wifi connection (ssid becomes blank when the connection is lost).
    if (ssidStatus.length() <= 0 || status != WL_CONNECTED) { //if SSID is blank or status is not 3, then try to connect again.
      status = WiFi.begin(userSSID, userSSIDpass); //connect to wifi
      ssidStatus = WiFi.SSID(); //update ssid so it wont loop forever.    
    }
  }

  return;
}

void SendDisplay(float Oil, int EGT, int Bst, int Tar, int LhB, int ACK){ // This function requires this data: Oil pressure, EGT temp., Boost pressure, Target pressure, Last high pressure, Active status of meth)
    display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS);
    display.clearDisplay();
    display.setTextSize(1);
    display.setCursor(3, 3);
    display.println("OIL Pres. ");
    display.setCursor(89, 3);
    display.print("EGT");
    display.drawLine(0, 12, 128, 12, 1);
    display.drawLine(64, 0, 64, 43, 1);
    display.drawLine(0, 43, 128, 43, 1);
    display.setCursor(10, 20);
    display.setTextSize(2);
    display.print(Oil); // Print Oil pressure to screen.
    display.setCursor(79, 20);
    display.print(EGT); // Print EGT to screen.
    display.setCursor(3, 46);
    display.setTextSize(1);
    display.print("Bst:");
    display.print(Bst); // Print actual boost pressure to screen.
    display.setCursor(3, 56);
    display.print("Tar:");
    display.print(Tar); // Print Target pressure to scrren (Meth is turned on when target pressure is met)
    display.setCursor(69, 46);
    display.print("LhB: ");
    display.print("LhB"); // Print last high boost pressure to screen.
    display.setCursor(83, 56);
    display.print(ACK); // Print STATUS OF METH INJECTION to scrren. (is the meth on or off?)
    display.display();

    return;
}


int EGT(){
  maxthermo.begin();
  maxthermo.setThermocoupleType(MAX31856_TCTYPE_K);
  
  return int(maxthermo.readThermocoupleTemperature());
}


float OilPressure(){ /// GET OIL PRESSURE DATA FROM VDO SENDER ///////////////////////////////////////////
/*
1200ohm - 5v - arduino - VDO oil pressure sender
Arduino is connected to the sender like this:

5v -- 1200ohm ---|----- VDO sender ----- GND
A0 --------------|

Raw values is mesuured with a ANALOG gauge. To be more precise.. USE A DIGITAL PRESSURE GAUGE
67 = 0bar
145 = 1bar
235 = 2bar
248 = 2,2bar
288 = 3bar
352 = 4bar
386 = 5bar
441 = 6bar
475 = 7bar


PLease note.. the VDO signal i NOT LINEAR, this is why i made it like this... to compensate for the unlinear signal.
Link to VDO signal curve: https://support.wellandpower.net/hc/en-us/articles/360002119317-How-do-you-test-an-oil-pressue-sender-
*/


int sensorValue = 0;        // value read from the pot
int outputValue = 0;        // value output
float output = 0;           // Final decimal output.
int stat = 101;             // Debug var.

  sensorValue = analogRead(analogInPin); // Get RAW value from VDO

  switch (sensorValue) {
  case 0 ... 66: // 3 - 4bar
    stat = 0;
    output = 0;
    break;
  case 67 ... 145: // 0 - 1bar
  stat = 1;
    sensorValue = map(sensorValue, 67, 145, 10, 31);
    outputValue = map(sensorValue, 10, 31, 0, 100);
    output = (float(outputValue) / 100);
    break;
  case 146 ... 235: // 1 - 2bar
  stat = 2;
    sensorValue = map(sensorValue, 146, 235, 31, 52);
    outputValue = map(sensorValue, 31, 52, 100, 200);
    output = (float(outputValue) / 100);
    break;
  case 236 ... 248: // 2 - 2,2bar
  stat = 3;
    sensorValue = map(sensorValue, 236, 248, 52, 66);
    outputValue = map(sensorValue, 52, 66, 100, 120);
    output = (float((outputValue + 100)) / 100);
    break;
  case 249 ... 288: // 2,2 - 3bar
  stat = 4;
    sensorValue = map(sensorValue, 249, 288, 66, 71);
    outputValue = map(sensorValue, 66, 71, 220, 300);
    output = (float(outputValue) / 100);
    break;
  case 289 ... 352: // 3 - 4bar
  stat = 5;
    sensorValue = map(sensorValue, 289, 352, 71, 90);
    outputValue = map(sensorValue, 71, 90, 300, 400);
    output = (float(outputValue) / 100);
    break;
  case 353 ... 386: // 4 - 5bar
  stat = 6;
    sensorValue = map(sensorValue, 353, 386, 90, 107);
    outputValue = map(sensorValue, 90, 107, 400, 500);
    output = (float(outputValue) / 100);
    break;
  case 387 ... 441: // 5 - 6bar
  stat = 7;
    sensorValue = map(sensorValue, 387, 441, 107, 124);
    outputValue = map(sensorValue, 107, 124, 500, 600);
    output = (float(outputValue) / 100);
    break;
  case 442 ... 457: // 6 - 7bar
  stat = 8;
    sensorValue = map(sensorValue, 442, 475, 124, 140);
    outputValue = map(sensorValue, 124, 140, 600, 700);
    output = (float(outputValue) / 100);
    break;
  }

  return output;
}

void WebServer(){
  WiFiClient client = server.available();   // listen for incoming clients
  if (client) {                             // if you get a client,
    String currentLine = "";                // make a String to hold incoming data from the client
    while (client.connected()) {            // loop while the client's connected
      if (client.available()) {             // if there's bytes to read from the client,
        char c = client.read();             // read a byte, then
        if (c == '\n') {                    // if the byte is a newline character
          // if the current line is blank, you got two newline characters in a row.
          // that's the end of the client HTTP request, so send a response:
          if (currentLine.length() == 0) {
            // HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK)
            // and a content-type so the client knows what's coming, then a blank line:
            client.println("HTTP/1.1 200 OK");
            client.println("Content-type:text/html");
            client.println();

            if(updateBOOST == 0){           
              client.print("<style>");
              client.print("body {background: #1e130d; color: #AD0C0C;}");
              client.print("table {font-family: arial, sans-serif; font-size: 6vw; border-collapse: collapse; width: 100%;}");
              client.print("td {border-bottom: 1px solid #404040; text-align: left; padding: 8px;}");
              client.print("tr:nth-child(even) {background-color: #000;}");
              client.print("input {height: 6vw; width: 33vw; font-size: 4.3vw;}");
              client.print("h2 {font-size: 7vw}");
              client.print("b {font-size: 4.5vw}");
              client.print("a {color: #AD0C0C;}");
              client.print(".blink_text {animation:1s blinker linear infinite; -webkit-animation:1s blinker linear infinite; -moz-animation:1s blinker linear infinite; color: #AD0C0C;}");
              client.print("@-moz-keyframes blinker {0% { opacity: 1.0; } 50% { opacity: 0.0; } 100% { opacity: 1.0; }}");
              client.print("@-webkit-keyframes blinker {0% { opacity: 1.0; } 50% { opacity: 0.0; } 100% { opacity: 1.0; }}");
              client.print("@keyframes blinker {0% { opacity: 1.0; } 50% { opacity: 0.0; } 100% { opacity: 1.0; }}");
              client.print("</style>");
              client.print("<body>");

              client.print("<div id=\"live\">");
              
              if(updateSTAT == 0){
                client.print("<table><tbody style=\"color: green\">>");
              } else {
                client.print("<table><tbody style=\"color: red\">>");
              }
              
              client.print("<tr><td>Target Boost pressure</td>");
              client.print("<td style=\"font-size: 16vw\">");
              client.print(targetBOOST);
              client.print("</td>");
              client.print("</tr>");
              client.print("<tr><td>Actual Boost pressure</td>");
              client.print("<td style=\"font-size: 16vw\">");           
              client.print(pressure);
              client.print("</td>");
              client.print("</tr>");

              client.print("</tbody></table><br><br>");
              client.print("<b style=\"font-size: 3vw\">Please enter target boost in mbar. (min. 2001 mbar)<br>by enter this to the end of the URL: <u>/TARGET BOOST/!boost</u></b><br><br>");
              client.print("<b style=\"font-size: 3vw\">Please enter calibration value in mbar. (default = 886, higher = less)<br>by enter this to the end of the URL: <u>/CALIBRATION VALUE/!cali</u></b><br><br>");
              client.print("<b style=\"font-size: 2vw\">Current Calibration number is: ");
              client.print(offset);
              client.print("<br><br>");
              client.print("The Last Boost press. over 2.5Bar was: ");
              client.print(lastBOOST);
              client.print("</b><br><br><br><br><br><br><br><br><br>");
              client.print("IF 101404 is entered as calibration value, the pump will run for 3 sec.<br><br><br>");
              client.print("IF 5473404 is entered as calibration value, then safemode is activated. Activate this, if you are experiencing problems with long loading times OR not being able to update target boost / calibration value. (adjust the update rate, to disable this mode)<br><br><br>");
              client.print("YOU can adjust the update rate of this page, by enter this as calibration value (range 100ms to 10000ms): 12473UPDATE RATE IN MS<br>Example /12473500/!cali if you want the page to update every 500ms<br><br>Current updaterate is: ");
              client.print(updateRATE);
              client.print(" (if 3 = safemode)<br><br><br>");

              client.print("</div>");
              
              if(updateRATE != 3){ // if update rate is 3 then we dont want the page to autoupdate (AKA safemode)
                client.print("<script>window.setInterval(newLoad, ");
                client.print(updateRATE);
                client.print(");} function newLoad() {var xhttp = new XMLHttpRequest(); xhttp.onreadystatechange = function() {if (this.readyState == 4 && this.status == 200) {document.getElementById(\"live\").innerHTML = this.responseText;}}; xhttp.open(\"GET\", \"\", true); xhttp.send();}</script>");
              }
            
              client.print("</body>");
            } else if(updateBOOST == 1) { // DUE TO A BUG WHERE TARGETBOOST UPDATES TO 0.. WE NEED TO DO THIS
              updateBOOST = 2;
              client.print(">>>>>>UPDATEING");
              client.print("<meta http-equiv=\"refresh\" content=\"0;URL='/'\" />"); 
            } else if(updateBOOST == 2) { // DUE TO A BUG WHERE TARGETBOOST UPDATES TO 0.. WE NEED TO DO THIS
              updateBOOST = 3000;
              client.print("UPDATEING<<<<<<");
              client.print("<meta http-equiv=\"refresh\" content=\"0;URL='/'\" />"); 
            } else if(updateBOOST == 3000) { // DUE TO A BUG WHERE TARGETBOOST UPDATES TO 0.. WE NEED TO DO THIS
              updateBOOST = 3;
              client.print(">>>>>>UPDATEING");
              client.print("<meta http-equiv=\"refresh\" content=\"0;URL='/'\" />"); 
            } else { // DUE TO A BUG WHERE TARGETBOOST UPDATES TO 0.. WE NEED TO DO THIS
              updateBOOST = 0;
              client.print("UPDATEING<<<<<<");
              client.print("<meta http-equiv=\"refresh\" content=\"0;URL='/'\" />");
            } 

            client.println(); // The HTTP response ends with another blank line:
            break; // Exit Transmission
            
          } else {
            currentLine = "";  // if you got a newline, then clear currentLine  
          }
        } else if (c != '\r') {    // if you got anything else but a carriage return character,
          currentLine += c;      // add it to the end of the currentLine   
        } 
        if(currentLine.endsWith("/!boost") && updateBOOST == 0){ //this will check for target boost commands.
          currentLine.replace("GET /", ""); //Clean GET input
          currentLine.replace("/!boost", ""); //Clean GET input
          targetBOOST = currentLine.toInt(); // Save the actual GET input
          BOOSTsave.write(targetBOOST); // Save the new target boost to "EEPROM"
          updateBOOST = 1; //this ensures the target boost does not get overwritten, due to a extra loop.
        }
        if(currentLine.endsWith("/!cali") && updateBOOST == 0){ //this will check for target boost commands.
          currentLine.replace("GET /", ""); //Clean GET input
          currentLine.replace("/!cali", ""); //Clean GET input     
          if(currentLine.toInt() <= 3000){    // THIS UPDATES THE CALIBRATION VALUE
            offset = currentLine.toInt(); // Save the actual GET input       
            CALI.write(offset); // Save the new calibration value to "EEPROM"       
          } else {
            offsetTMP = currentLine.toInt(); //Save the actual GET input, for use in other places (eg to turn on test pump, update js refresh rate, and so on.. see "MethController()" function for more info.  
          }
          updateBOOST = 1; //this ensures the target boost does not get overwritten, due to a extra loop.       
          } 
       }
    }
    client.stop(); // close the connection:
  }

  return;
}

Webserver code (to find IP):

<?php
header('Access-Control-Allow-Origin: *');
error_reporting(0);
if(isset($_GET["ip"]) || isset($_GET["ssid"])) {
	$conn = new mysqli("localhost", "USER", "PASSWORD", "mycarinfo");
	$date = time();
	
	$ip = mysqli_real_escape_string($conn, $_GET["ip"]);
	$ssid = mysqli_real_escape_string($conn, $_GET["ssid"]);
	$sql = "INSERT INTO ip (id, ip, ssid, date, wait) VALUES ('0', '$ip', '$ssid', '$date', '0')";
	mysqli_query($conn, $sql);
	$sql = "DELETE FROM ip WHERE `date` < (UNIX_TIMESTAMP() - 600);";
	mysqli_query($conn, $sql);

} else {

	$conn = new mysqli("localhost", "USER", "PASSWORD", "mycarinfo");
	$sql = "SELECT * FROM ip";
	$result = mysqli_query($conn, $sql);
	
	while($row = mysqli_fetch_array($result)) {
		echo "Local ip: ";
	    echo $row['ip'];      // Print the entire row data
		echo "<br> SSID: ";
	    echo $row['ssid'];
	    echo "<br><br><br>";
		echo "========================================<br>";
		echo "========================================<br><br><br>";
	}
}
?>

Of cause you will need a sql server aswell..

Pls let me say you better edit your subject to extend the first word.
If you just call it "Meth" someone could think you're Walter White... :innocent: :rofl:

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.