Hybrid RV project - Getting Two Arduinos to Talk

I have taken up a project to equip my RV with a hybrid vehicle control system that integrates hho and h2o injection in a standard gas V8. Much of the hardware is already installed including two Arduinos. The microcontrollers' end of things is to monitor and display data, but also to control the timing advance of the engine with a servo I have mounted in place of the vacuum advance on the engine distributor, among other things. But I have hit a snag in getting the two Arduinos to communicate.

I am not sure if it is a hardware or a software problem. But on the hardware end, I am using one USB Duemilanove and another that is serial 232. I have been programming each with one laptop on ports com6 and com1 respectively. Meanwhile, the controllers are connected via the 1 - 0 pins and 0 - 1.

The master unit (the USB) mainly runs an LCD and the basic logic, while the serial unit is mainly intended to provide stable servo control, which is a design that seems to be ideal for the RC servos I am using.

In testing, I have been sending an array of three integers from the master to the slave (1, 0, 1), but the slave somehow seems to keep getting 53, 51, 49. and sometimes 53, 51, 109, and sometimes something a little different.

I will leave it at that for now. Somebody probably knows the answer here and it is obvious to them. Any feedback would be appreciated.

If this is a software/syntax issue, the problem should most likely appear in the very first void of one of the two programs, SendData and ReceiveData.

Here is the Main Code:

#include <LiquidCrystal.h> 
#define TimeToWarmEngine  4  // in minutes
#define TempToWarmEngine  160  // in Fahrenheit degrees
char Program[] = " Prog: M100.52a";
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
const int dataSyncRate = 2000;  // a period in which to sync data trans
const byte LeftKnobPin = 0;          // analog  0
const byte EngineThermistor = 2;     // analog  2
const byte FourButtons = 4;          // analog  4
const byte RightKnobPin = 5;         // analog  5
//const byte InputToggleSwitch = 6;
//const byte AutoSwitch = 7;
const byte OilSendingUnit = 9; 
const byte SelectButton = 10;
const byte NumRdgsCt = 48;
const byte NumRdgsLK = 16;
const byte integersToSend = 3;
const byte integersToReceive = 1;
int SendDataArray[integersToSend]; 
int ReceiveDataArray[integersToReceive]; 
int Creadings[NumRdgsCt], LKreadings[NumRdgsLK];
int RightKnob = 0, LeftKnob = 0;
int Cindex = 0, Ctotal = 0, Caverage = 0, Creading = 0;
int LKindex = 0, LKtotal = 0, LKaverage = 0, LKreading = 0;
int EgTemp = 0, Advance = 0, count = 0, newData1 = 0;
int BtnTest = 0, BtnRead = 0, BtnRead2 = 0, newData2 = 0;
boolean Start, SystemsGo, EngineOn, dataSent, Automatic;
boolean dataReceived = false, EngineCold = true;
float ExTemp, CoolTemp = 120, tFar;
byte degree[8] = { 
  B01000,  B10100,  B01000,  B00000,  B00000,  B00000,  B00000,};
  
void SendData()                                       {
  if ((millis() % dataSyncRate) < (dataSyncRate/4)) {
    if (dataSent == false)                      {
      if (Automatic == false)
        SendDataArray[0] = 0;
      else SendDataArray[0] = 1;
      SendDataArray[1] = LKaverage;
      if (EngineCold == true) 
        {SendDataArray[2] = 1;}
      else SendDataArray[2] = 0;
      for(int x=0; x<integersToSend; x++)
        Serial.print(SendDataArray[x]);  
//      Serial.println("master out");
      dataSent = true;                          }  }
  else dataSent = false;                              } 
   
void ReceiveData()                                   {   
  if ((millis() % dataSyncRate) > (3*(dataSyncRate/4)))  {
    if (dataReceived == false)                 {
      for(int x=0; x<integersToReceive; x++)       {
        if (Serial.available() > 0)              {
          ReceiveDataArray[x] = Serial.read();   } } 
        Serial.flush(); 
        Serial.print(ReceiveDataArray[0]);
//        Serial.println("master in");
        dataReceived = true;                   }         }
  else dataReceived = false;                         }

void ReadLeftKnob()                           {
  LKreading = map(analogRead(LeftKnobPin), 0, 1023, 0, 32);
  LKtotal = LKtotal - LKreadings[LKindex];         
  LKreadings[LKindex] = LKreading;
  LKtotal = LKtotal + LKreadings[LKindex];       
  LKindex = LKindex + 1;                    
  if (LKindex >= NumRdgsLK)             {            
    LKaverage = LKtotal / NumRdgsLK;   
    LKindex = 0;                        }     }
    
void ReadRightKnob()                      {
  RightKnob = analogRead(RightKnobPin); }

void ReadButtons() {
  BtnRead = analogRead(FourButtons); 
  if (BtnRead >= 5) {
    delay(50); BtnRead2 = analogRead(FourButtons);
    if (((BtnRead + 5) % BtnRead2) <= 10)  {
      if(BtnRead > 950)    { 
        BtnTest = 2;       }; 
      if((BtnRead > 600) && (BtnRead < 950)) { 
        BtnTest = 4;       };
      if((BtnRead > 400) && (BtnRead < 600)) { 
        BtnTest = 3;       };
      if((BtnRead < 400) && (BtnRead > 10 ))  { 
        BtnTest = 1;       };    };   };
  if(BtnTest == 2) {
 //   digitalWrite(AutoSwitch, LOW);
    Automatic = true; 
    BtnTest = 0; 
    delay(300);   };
  if(BtnTest == 1) {
 //   digitalWrite(AutoSwitch, HIGH);
    Automatic = false; 
    BtnTest = 0; 
    delay(300);   };
  if (BtnTest == 3)  {
    BtnTest = 0;
    delay(300);      }
  if (BtnTest == 4) {
    BtnTest = 0;
    delay(300);      }}

void setup() {
  Serial.begin(9600);
  pinMode(SelectButton, INPUT); 
//  pinMode(InputToggleSwitch, INPUT);
//  digitalWrite(InputToggleSwitch, HIGH);
//  pinMode(AutoSwitch, OUTPUT);
//  digitalWrite(AutoSwitch, LOW);
//  pinMode(OilSendingUnit, INPUT);
  Start = false;  
  EngineOn = false; 
  SystemsGo = false;
  Automatic = true;
  lcd.begin(16,2);
  lcd.createChar(0, degree);
  lcd.clear(); 
  lcd.print("  System Check"); 
  lcd.setCursor(0, 1);
  lcd.print(Program);
  delay(2500);                        }

void WarmUp() {
  while (Start == false)                           {
    if ((!EngineOn) && (int(digitalRead(OilSendingUnit)) > 0))  {
      delay(3000); 
      if ((!EngineOn) && (int(digitalRead(OilSendingUnit)) > 0)){
        EngineOn = 1;  
        Start = true;               };                          } 
    if (!EngineOn && SystemsGo)                {
      lcd.clear(); 
      lcd.setCursor(0,0);
      lcd.print("     Ready      "); 
      delay(200);
      if (Start == false )                  {
        Start = digitalRead(SelectButton);  }   }
    else {
      lcd.setCursor(0, 1); 
      lcd.print("     Fault      "); 
      while (SystemsGo == false ) {
      // put tests here for SystemsGo 
      SystemsGo = true;    } } };
    if ((millis() << TimeToWarmEngine * 60000) &&
      (Caverage << TempToWarmEngine))
      EngineCold = true;              }
      
void FigureStuff()  {
  if (Automatic == true) Advance = ReceiveDataArray[0];
  else Advance = LKaverage;
}

void DisplayStuff()  {
  if (count >= 200) {
    lcd.clear();
    lcd.print('5');
    lcd.print('1');
    lcd.setCursor(3,0);
    lcd.print("ct");
    lcd.print(int(Caverage));
    lcd.setCursor(7,1);
    if (Automatic == true) 
      {lcd.print("Auto");}
    else lcd.print("Man");
    lcd.setCursor(9,0);
    lcd.print("egt");
    lcd.print(" 00");
    lcd.setCursor(0,1);
    lcd.print("adv");
    lcd.print(Advance);
    lcd.write(0);
    lcd.setCursor(12,1);
    lcd.print("inj");
    lcd.print(0);
    count = 0;          };
  ++count;    }

  void loop() {  
  WarmUp();    
  SendData();
  ReceiveData();
  ReadLeftKnob();
  ReadRightKnob();
  ReadButtons();
  FigureStuff();
  DisplayStuff();
}

And here is the Slave Code:

#define DistributorServo 7
#define ColdAdvance 5
#define minPulse 400   //pulse widths in microseconds
#define maxPulse 2200  
#define movementRate 10  //max resolution of change in pulsemicros
#define timeToPulse 20  // delay between servo pulses in millis
const int dataSyncRate = 2000;
const byte integersToSend = 1;
const byte integersToReceive = 3;
const int changeTime = 500;
int SendDataArray[integersToSend];
int ReceiveDataArray[integersToReceive]; 
int pulseTime = 0, oldPos = 0, newPos = 0, Advance = 0 ;
int newPulse = minPulse, oldPulse = minPulse;
boolean posChange = false, dataSent = false, pulsed = false;
boolean dataReceived = false, Automatic = false, EngineCold = true;

void ReceiveData()                                   {   
  if (((millis() % dataSyncRate) < (dataSyncRate/2))
    && (millis() % dataSyncRate) > (dataSyncRate/4))   {
    if (dataReceived == false)                {
      for(int x=0; x<integersToReceive; x++)       {
        if (Serial.available() > 0)             {
          ReceiveDataArray[x] = Serial.read();  }  } 
        Serial.flush(); 
        Serial.print(ReceiveDataArray[0]);
        Serial.print(ReceiveDataArray[1]);
        Serial.print(ReceiveDataArray[2]);
        Serial.println("slave in");
        dataReceived = true;                  }        }
  else dataReceived = false;                         }

void SendData()                                {
  if (((millis() % dataSyncRate) > (dataSyncRate/2))
    && (millis() % dataSyncRate) < ((3*dataSyncRate)/4))   {
    if (dataSent == false)          {
      SendDataArray[0] = Advance;
      for(int x=0; x<integersToSend; x++)
        Serial.print(SendDataArray[x]);  
      Serial.println("slave out");
      dataSent = true;              }                      }
  else dataSent = false;                       } 

void pulseOut(int pinNumber, int pulseWidth)                  {
 if ((pulseWidth >= minPulse) && (pulseWidth <= maxPulse)) {
   digitalWrite(pinNumber, HIGH);
   delayMicroseconds(pulseWidth);
   digitalWrite(pinNumber, LOW);                           }  } 

void FigureAdvance()                     {
  if (ReceiveDataArray[0] == 1) 
    {Automatic = true;}
  else {Automatic = false;};
  if (Automatic == false)
    {Advance = ReceiveDataArray[1];};
  if (ReceiveDataArray[2] == 1) 
    {EngineCold = true;}
  else {EngineCold = false;};
  if ((EngineCold == true)&&(Automatic == true)) 
    {Advance = ColdAdvance;}
  if (int(millis() % changeTime) <= (changeTime/10)) {
    if (posChange == false)     {
      if (newPos << Advance) 
        {newPos = newPos + 1;};
      if (newPos >> Advance) 
        {newPos = newPos - 1;};
      posChange = true;         }                    }
  else posChange = false;                }
  
void setup()                {
  Serial.begin(9600);
  pinMode (7, OUTPUT);      }
 
 void ControlServo()                 {     
  if ((millis() % timeToPulse) <= (timeToPulse/2))               {
    if (pulsed == false)                                       {
      newPulse = map(newPos, 0, 180, minPulse, maxPulse);
      if (oldPulse <= newPulse)                              {
        oldPulse = (oldPulse + movementRate);    
        pulseOut(DistributorServo, min(newPulse, oldPulse)); }
      else                                                   {   
        oldPulse = (oldPulse - movementRate);
        pulseOut(DistributorServo, max(newPulse, oldPulse)); }        
      pulsed = true;                                           } }  
  else pulsed = false;               }
  
void loop()           {
  ReceiveData();
  FigureAdvance(); 
  ControlServo();
  SendData();         
                           }

The data transmission is syncronized by a const period called dataSyncRate. This is a period in millis I have divided into four portions, and use one portion of each for master out, slave in, slave out, master in. This part works fine. And it prevents the system noise that would otherwise be disrupting servo timing. I plan to pass a variable between the units to sync this rate also, but again, the problem at this point is in getting anything across.

I have taken on a project to develop a hybrid vehicle control system that integrates hho and h2o injection in a standard gas V8.

You are aware that "hydrogen boosting"/"water injection" is a "scam", right? I am not saying you have been scammed, or that you are scamming someone, just that for something like this to work effectively you would have to be able to break some basic laws of thermodynamics? In effect, you might be "scamming yourself":

http://www.google.com/search?q=water+injection+scam
http://www.google.com/search?q=hho+scam
http://www.google.com/search?q=hydrogen+boost+scam

Just something to think about. We all would like something like this to be true, but if it were, and were easy to implement by a garage tinkerer, don't you think that the automobile manufacturers would already include it? Then again, perhaps it is a conspiracy...?

:wink:

There is a potential issue with using millis the way you are:

  if (((millis() % dataSyncRate) < (dataSyncRate/2))
    && (millis() % dataSyncRate) > (dataSyncRate/4))   {

The two calls to millis may not return the same value. Much better would be to record the value returned by millis and use that stored value in the if test. Particularly since % is not the fastest function around.

Thanks crOsh. I have heard your concerns rumoured. Fortunately, I am not basing my beliefs on the sales pitches of others. I have proven the system to myself. And concerning the thermodynamics involved, the internal combustion engine generally loses some 70% of the potential power of the gasoline to heat. Hho helps recover some of that loss. Water injection also recovers some more in the form of a steam boost in the combustion stroke, which is also (or recovered) free energy. The key to make this work even better involves advancing the timing in a dynamic way. And higher compression is also in the offing, but not for improved efficiency now.

And concerning the automotive industry, the problems there all point to the unwritten laws of big business and legal liability. Idiot-proofing, and things like that.

Look what they have done to Toyota for violating those laws.

Good point paulS. There is also the point that each of these programs call millis from different oscillators. That is why i plan to base all calls on one. And you are right, I should make it all by one call.