Autolaturi mökille varavoimaksi Arduinon avulla

Itse huomannut lähes kaikessa koodaamisessa että kun tavallaan valmis koodi on niin kannattaa aloittaa alusta kun tiedetään tarkasti miten koodi on ajateltu toimivaksi itsellä ainakin tällä kerralla välistä jää paljon turhaa koodia pois enkä ole kertaakaan saanut koodia toimimaan laakista.
 
No tässä olisi tällainen vuokaavio tästä ohjaussysteemistä. Jos tarvii tarkentaa jotain kohtaa, kertokaa niin päivitän kaavion.

Laturi State Machine.jpg
 
No tässä olisi tällainen vuokaavio tästä ohjaussysteemistä. Jos tarvii tarkentaa jotain kohtaa, kertokaa niin päivitän kaavion.

Laturi State Machine.jpg
Running - waiting välille tulee varmaa tila stopping ?

Missä kaikissa tiloissa tarkastetaan jännite >15V ? Ilmeisesti aina tilasta riippumatta?
 
@ississ, STOPPING-tilaa ei ole lainkaan, ehkä se olisi hyvä lisätä? Ajateltu, että se menisi sammuttuaan heti WAITING-tilaan.

Kaikista tiloista mennään FAIL-tilaan, jos jännite on 15 V tai yli.
 
Tuossa mietin ainakin että kun startti ajaa konetta pyörittääkö se yli 300rpm eikö tuo raja kannattas nostaa jonnekin 1000rpm paikkeille vai paljonko tuollainen kone kiertää tyhjäkäynnillä.
Itse tekisin ehkä suoraan looppiin tuon yli15v jännitteen tutkimisen joka palauttaa heti tilan fail
 
@Nasty76 , kaikki kierroslukuarvot ja todennäköisesti myös alarajajännite ovat tässä vaiheessa vielä valistuneita arvauksia ja niitä ehtii sitten hienosäätää, kun koko systeemi on toimivana. Esim. tuo 300 kierrosta minuutissa lienee sitä luokkaa, että siinä voidaan ajatella, että moottori on käynnistynyt. Tuskin starttimoottori noin lujaa pyörittää moottoria ja helppohan sitä on tarpeen mukaan muuttaa ylemmäs.

Moottorin ilmoitettu tyhjäkäynti on 1 400 kierrosta minuutissa, starttimoottori kuitenkin tulisi irroittaa jo paljon ennen tätä, eli kun moottori alkaa niin sanotusti putputtamaan. Tyhjäkäyntiähän ei käytännössä tulla käyttämään koskaan, sillä moottori käynnistyy suoraan siihen kierrosnopeuteen, mihin se on asetettu. Veikkaanpa jotain puolikaasua, ehkä luokkaa 2 000 - 3 000 kierrosta minuutissa. Sitä suuremmalla syyllä startin olisi hyvä irrota moottorista jo ajoissa.
 
Stopping- tila ei ole välttämätön mutta arduinon kannalta moottorin sammuminen on kuitenkin hidas tapahtuma, loop() ajaa monta kertaa sen aikana kun kone sammuu (sekunteja kuitenkin koko tapahtuma).
Ja waiting- tilassa ei tietenkään saa olla koodia joka hämääntyy siitä että moottorilla on vielä kierroksia enemmän kuin 0.

Riippuu myös miten sammutus ja käynnistys tapahtuu, oikeastaanhan pitäisi ensin hallitusti lopettaa lataus (= pwm pienelle), kääntää sammutuslähtö ja sitten odottaa että kierrokset/tärinä ilmaisee että on seis.
2 ensimmäistä menee tilanvaihdossa running -> waiting mutta missä odotetaan että on oikeasti sammunut ?
Yksi tapa olla lisätä stopping- tila odotusta varten ja vasta kun todetaan että on oikeasti sammunut niin sammutuslähdön nollaus ja tilanvaihto -> waiting.
Toinen vaihtoehto on tietysti sammutuslähdön palautus ajo-asentoon vasta silloin kun käynnistetään. Tyyliin sammutuslähdön nollaus, odotetaan esim 200ms koska rele on hidas ja sitten starttilähtö päälle jne. Ja tässä välissä delay(200) on ihan ok eikä kannata tehdä ajastimella.
 
No niin, jotain edistystä. Nyt sentään startti toimii, muttei ihan oikein, viiveet ovat ainakin simulla pidemmät mitä pitäisi olla. Nyt tila ei kuitenkaan vaihtele hallitsemattomasti. Loop-funktiossa on case-break juttu käytössä, en tiedä onko tuo ihan oikein laitettu.

Lisäksi jännä juttu kun lisäsin tuon STOPPING-tilan, mutta sille ei sitten löytynytkään käyttöä, niin halusin sen poistaa. Sitten kävi niin, että ei mene kääntäjästä läpi, jos tuone #define STOPPING 6 -rivin poistaa. Herja tulee liquid crystal-kirjastosta, joka kyllä on oikein asennettu. Lisäksi tuota STOPPING-tilaa ei esiinny missään koodissa.

Ajastimien osalta häikkää oli, eli tuossa koodissa pitää olla ainakin tuo .started komento käytössä, muuten ajastin ei lähde päälle. Saattaa toisaalta johtua myös, jos on loopissa, niin se pitäisi osata laittaa sinne koodin sisään.


Tässä ajastinkirjaston testikoodi:

C++:
#include <neotimer.h>

Neotimer testTime = Neotimer(2000);  //2 second


void setup() {
  Serial.begin(250000);
}

void loop() {

  if (!testTime.started()) {
    testTime.start();
  }
  if (testTime.done()) {
    Serial.println("Tulosta 2 sek valein.");
    testTime.reset();
  }
}

Varsinainen koodi:
C++:
// Generator control system for car alternator (Ford with PWM controlled charging voltage) and small engine

#define DEBUG_STATUS  // Serial debug, comment to disable, downmost values
//#define DEBUG_MAIN //Serial debug, comment to disable, uppermost main function values

// System status variables (motorStatus)
#define WAITING 0
#define STARTING 1
#define STARTED 2
#define RAMP_UP 3
#define RUNNING 4
#define FAIL 5
#define STOPPING 6

#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <neotimer.h>  // Non-blocking timer from Neotimer library

Neotimer motorTime = Neotimer(10000);   // 10 second timer for startup detection
Neotimer stepTime = Neotimer(1000);     // 1 second timer for PWM step interval
Neotimer shutTime = Neotimer(15000);    // 15 second timer for engine shutdown (wait before switching PWM back to 100 %, electromagnet off)
Neotimer crankTime = Neotimer(4000);    // 4 second timer, if problems to start, try at most 4 seconds continuous cranking
Neotimer crankWait = Neotimer(5000);    // 5 second timer, if crank failed, wait 5 seconds before a new try
Neotimer voltageWait = Neotimer(2000);  // 60 seconds timer, how long to wait with undervoltage & other assigned conditions, before the starting function executes
Neotimer serDelay = Neotimer(1000);     // For serial print interval

LiquidCrystal_I2C lcd(0x27, 16, 2);  // Create a new LiquidCrystal_I2C display object with the correct I2C address and display size

// Define the pins
const int currentPin = A0;  // Information about current
const int voltagePin = A2;  // Information about voltage
const int motorPin = 2;     // Motor running pin (vibration sensor), HIGH = motor running, TEST purpose LOW
const int pwmPin = 9;       // PWM output pin
const int relayPin = 4;     // Shutdown relay control pin
const int rpmPin = 11;      // RPM Hall Sensor input pin, sensor type: 3144
const int starterPin = 3;   // Starter relay control pinn

// Current and voltage calculation
float voltageFactor = 5.00 / 1023.00;  // Factor to convert ADC reading to voltage

// Define the points for current calculation / the line equation
float x1 = 0.500;  // volts
float y1 = 0;      // amps
float x2 = 4.005;  // volts
float y2 = 150;    // amps
// Calculate the slope and intercept
float m = (y2 - y1) / (x2 - x1);
float b = y1 - m * x1;

float currentCal = 1;  // Variable to shift the whole current level

float sensVfactor = 20.00 / 4.9248;  // 4.9248 volts = 20 volts, factor to convert high voltage to 0-5 V
//float current;                       // Store current value, actual value
int current;  // For current testing, serial input
//float sensVoltage;                   // Store sensed voltage, actual value
int sensVoltage = 13;  // For voltage testing, serial input
const int N = 25;      // Number of current readings to average
const int Y = 25;      // Number of sensed voltage readings to average
int readings[N];       // Array to store the current readings
int readingsV[Y];      // Array to store the sensed voltage readings

// PWM
int pwmValues[] = { 24, 41, 58, 77 };  // PWM duty cycle steps (ramp up), 77 = ~30 %, you can add steps, if needed
int currentPwmStep = 0;

// Motor state, starting and shutting down
int motorStatus;            // 0=waiting, for low voltage 1=starting, 2=started, 3=ramping up pwm, 4=running, 5=stopping, 6=system or engine malfuntion, stopped
int startTry;               // 0=not tried to start yet, 1=first try done, 2=second, 3=third, 4=fourth, 5=fifth
int startInterval;          // 0=not waiting after start attempt, 1=waiting after start attempt
float startVoltage = 11.9;  // Volts, under what voltage the starting function will execute
float shutCurrent = 5;      // Amperes, below what charging current to execute the shutdown function
float upVoltage = 15;       // Volts, max allowed voltage
float rpmTH = 300;          // Revolutions per minute, rpm threshold greater than this to stop cranking (assume motor is started rpms over the threshold)
float rpmRun = 1800;        // Revolutions per minute, the rpm greater than this is accounted as motor normal running

//RPM Calculation
int rpm = 0;             //unsigned long rpm = 0;   // Variable to store rpm value
unsigned long duration;  // Sensor pulse in duration

// int lcdStep;                      //0=current display, 1=voltage display, not used at the time


void setup() {

  Serial.begin(250000);  // Start the serial communication

#ifdef DEBUG_MAIN
  Serial.println("Setup...");
#endif

  Wire.begin();
  // Check if the LCD is connected
  Wire.beginTransmission(0x27);
  if (Wire.endTransmission() == 0) {
    // LCD is connected, proceed with initialization
    delay(50);
    lcd.begin(16, 2);
    lcd.backlight();
    lcd.clear();
  } else {
    // LCD is not connected, continue without initializing the LCD

#ifdef DEBUG_MAIN
    Serial.println("Ei LCD:ta. Ohitus.");
#endif
  }

  // Change timers on pins to change PWM freq to 122 Hz
  // Pins D9 and D10 - 122 Hz
  TCCR1A = 0b00000001;  // 8bit
  TCCR1B = 0b00000100;  // x256 phase correct

  // Make pins output/input
  pinMode(currentPin, INPUT);       // Information on charging current
  pinMode(voltagePin, INPUT);       // Information on voltage, for real use, delete _PULLUP
  pinMode(motorPin, INPUT_PULLUP);  // Vibration sensor in, for motor running detect,  for real use, delete _PULLUP (it is for test purpose, pullup resistor)
  pinMode(pwmPin, OUTPUT);          // Alternator PWM output
  pinMode(relayPin, OUTPUT);        // Ignition relay pin
  pinMode(rpmPin, INPUT_PULLUP);    // RPM sensing pin
  pinMode(starterPin, OUTPUT);      // Engine starter relay pin

  // Start Duty Cycle at 100 %, alternator electromagnet off, for starting the engine
  analogWrite(pwmPin, 255);

#ifdef DEBUG_MAIN
  Serial.println("Setup ok.");
#endif
}


void motorCrankState() {  // Detection of undervoltage to trigger motor cranking function

  if (sensVoltage < startVoltage) {  // If motorStatus is WAITING and sensed voltage is less than start voltage, trigger cranking function

#ifdef DEBUG_MAIN
    Serial.println("Jannitekynnys kaynnistykseen!");
#endif

    if (!voltageWait.started()) {  // Start the voltageWait timer only, if it has not been started yet
      voltageWait.start();         // Start the voltageWait timer if it hasn't started yet
    }

    if (voltageWait.done()) {  // Wait for voltageWait to be done before moving to the cranking of the engine
      motorStatus = STARTING;
      digitalWrite(relayPin, HIGH);  // Switch ignition relay on
      voltageWait.stop();
      voltageWait.reset();  // Reset timer
    }
  }
}


void crank() {  // Engine starting function

  if (((startTry >= 0) && (startTry <= 4)) && (startInterval == 0)) {  // If conditions are met, start executing

    if (rpm <= rpmTH) {  // If motor runs slower than the threshold, switch starter relay on
      digitalWrite(starterPin, HIGH);

      if (!crankTime.started()) {
        crankTime.start();  // Start the crankTime timer if it hasn't started yet
      }

      if (crankTime.done()) {           // If motor did not start during the crankTime, stop starting
        digitalWrite(starterPin, LOW);  // Switch starter relay off
        crankTime.stop();
        crankTime.reset();  // Reset timer
        startTry += 1;      // Add one attempt
        startInterval = 1;  // Start waiting time function after starting attempt
      }
    }

  } else if (rpm > rpmTH) {         // If motor runs faster than the threshold, switch starter relay off
    digitalWrite(starterPin, LOW);  // Switch starter pin off
    startTry = 0;
    motorStatus = STARTED;
    crankTime.stop();
    crankTime.reset();  // Reset timer
  }
}




void Waiter() {  // Wait after starting attempt

  if (((startTry >= 0) && (startTry <= 4)) && (startInterval == 1)) {  // If conditions are met, start executing

    if (!crankWait.started()) {
      crankWait.start();  // Start the crankWait timer if it hasn't started yet
    }

    if (crankWait.done()) {  // If timer is done
      startInterval = 0;     // revert to state 0 (waiting time after start attempt ready)
      crankWait.stop();
      crankWait.reset();  // reset timer
    }
  }
}


void startFail() {  // If engine did not start put system status to state 5 (malfunction)

  if (startTry == 5) {
    motorStatus = FAIL;
    digitalWrite(starterPin, LOW);  // Starter relay off
    digitalWrite(relayPin, LOW);    // Ignition switch relay off

#ifdef DEBUG_MAIN
    Serial.println("Kaynnistysvirhe!");
#endif

    lcd.setCursor(0, 0);  // On lcd print "Engine start failed"
    lcd.print("K");
    lcd.print((char)0xe1);
    lcd.print("ynnistys      ");
    lcd.setCursor(0, 1);
    lcd.print("ep");
    lcd.print((char)0xe1);
    lcd.print("onnistui     ");
  }
}

void overvoltage() {  // If voltage is over max voltage put motor status to FAIL (malfuntion) and shut down the ignition relay

  if (sensVoltage >= upVoltage) {
    motorStatus = FAIL;
    digitalWrite(relayPin, LOW);

#ifdef DEBUG_MAIN
    Serial.println("Ylijannite!");
#endif

    lcd.setCursor(0, 0);  // On lcd print "Overvoltage"
    lcd.print("Ylij");
    lcd.print((char)0xe1);
    lcd.print("nnite      ");
    lcd.setCursor(0, 1);
    lcd.print("                ");
  }
}


void motorRunning() {  // Engine running detection

  if ((rpm > rpmRun) || (digitalRead(motorPin == HIGH))) {  // If engine runs faster than rpmRun value (revolution per minute) assume motor is running normally

#ifdef DEBUG_MAIN
    Serial.println("Kayntitieto!");
#endif

    if (!motorTime.started()) {
      motorTime.start();  // Start the motorTime timer if it hasn't started yet
    }

    if (motorTime.done()) {  // Wait for motorTime to be done before moving to ramping up PWM

      motorStatus = RAMP_UP;

      motorTime.stop();   // Stop the timer
      motorTime.reset();  // Reset the timer

#ifdef DEBUG_MAIN
      Serial.println("Kay.");
#endif
    }
  }
}


void rampUp() {  // Ramping up PWM

  //Ramp pwm up
  if (stepTime.repeat()) {
    analogWrite(pwmPin, pwmValues[currentPwmStep]);
    currentPwmStep++;

#ifdef DEBUG_MAIN

    Serial.println("Ramp up...");
#endif

    if (currentPwmStep >= sizeof(pwmValues) / sizeof(pwmValues[0])) {  //Checking the pwm step count

      motorStatus = RUNNING;  // Motor running on normal charging state
      stepTime.stop();
      stepTime.reset();  // Reset the timer
    }
  }
}



void shutDown() {  // Engine shutdown, execute relay

  // If the charging current drops to 0 to shutCurrent and charging voltage is under upVoltage and motor is running
  // start the shutdown function and turn off the relay
  if ((current >= 0) && (current < shutCurrent) && (sensVoltage < upVoltage)) {
    // if (digitalRead(motorPin) == HIGH) { // Vibration sensor

    digitalWrite(relayPin, LOW);  // Switch relay off
    analogWrite(pwmPin, 15);      // Duty Cycle to 5 % before running down engine

    if (!shutTime.started()) {
      shutTime.start();
    }

    if (shutTime.done()) {
      analogWrite(pwmPin, 255);  // Stopped Duty Cycle at 100 %, alternator electromagnet off

      lcd.setCursor(0, 0);
      lcd.print("Sammutus        ");  // Print on lcd "Shutdown"
      lcd.setCursor(0, 1);
      lcd.print("                ");

      motorStatus = WAITING;  // Motor has run, battery is full, go back to waiting voltage

      shutTime.stop();   // Reset the timer
      shutTime.reset();  // Reset the timer

#ifdef DEBUG_MAIN
      Serial.println("Sammutettu.");
#endif
    }
  }
}
//}


void lcdDisplay() {  // Show values on lcd only when

  if (motorStatus == RUNNING) {

    lcd.setCursor(0, 0);
    lcd.print("Virta:  ");
    lcd.setCursor(0, 1);
    lcd.print((float)current);
    lcd.print(" ");
    lcd.setCursor(5, 1);
    lcd.print(" A ");

    lcd.setCursor(8, 0);
    lcd.print("J");
    lcd.print((char)0xe1);
    lcd.print("nnite:");
    lcd.setCursor(8, 1);
    lcd.print((float)sensVoltage);
    lcd.print(" ");
    lcd.setCursor(13, 1);
    lcd.print(" V ");

#ifdef DEBUG_MAIN
    Serial.println("Virta LCD.");
#endif
  }
  if (motorStatus == WAITING || motorStatus == STARTING) {
    lcd.setCursor(0, 0);  // On lcd print "Waiting"
    lcd.print("Odotetaan...    ");
    lcd.setCursor(0, 1);
    lcd.print("J");
    lcd.print((char)0xe1);
    lcd.print("nnite: ");
    lcd.print((float)sensVoltage);
    lcd.print(" V");
  }
}


void loop() {
  // Calculate current from sensor reading
  // Calculate the charging current from the average and display it on the LCD screen.
  // Take N readings and store them in the array
  //  for (int i = 0; i < N; i++) {
  //   readings[i] = analogRead(currentPin);
  //    delay(1);  //Wait for a millisecond between readings
  //  }

  // Calculate the average of the N readings
  //  float sum = 0;
  //  for (int i = 0; i < N; i++) {
  //    sum += readings[i];
  //  }
  //  float average = sum / N;

  //  float voltage = average * voltageFactor;  // Convert ADC reading to voltage


  //  current = (m * voltage + b) * currentCal;  // Convert voltage to current using the linear equation, correct with current calibration value if needed


  // Calculate high voltage from voltage divider input
  // Calculate the charging voltage from the average and display it on the LCD screen.
  // Take Y readings and store them in the array
  //  for (int j = 0; j < Y; j++) {
  //    readingsV[j] = analogRead(voltagePin);
  //    delay(1);  // Wait for a millisecond between readings
  //  }

  // Calculate the average of the N readings
  //  float sumV = 0;
  //  for (int j = 0; j < Y; j++) {
  //    sumV += readingsV[j];
  //  }
  //  float averageV = sumV / Y;

  //  float voltageV = averageV * voltageFactor;  // Convert ADC reading to voltage

  //  sensVoltage = voltageV * sensVfactor;  // Convert voltage to real voltage


  // RPM Calculation from Hall Sensor input
  // {
  //   duration = pulseIn(rpmPin, FALLING, 500000);  // Times the amount of microseconds the motor is not timing IR, Times out after 100000 uS. Raise the timeout for slower RPM readings. .5 second
  //    rpm = 60000.0 / duration * 1000;              // See above
  //  }

  // Handling the motor status
  switch (motorStatus) {
    case WAITING:
      motorCrankState();
      lcdDisplay();
      startFail();
      overvoltage();
      break;
    case STARTING:
      crank();
      Waiter();
      lcdDisplay();
      startFail();
      overvoltage();
      break;
    case STARTED:
      motorRunning();
      startFail();
      overvoltage();
      break;
    case RAMP_UP:
      rampUp();
      lcdDisplay();
      startFail();
      overvoltage();
      break;
    case RUNNING:
      startFail();
      overvoltage();
      break;
  }


//Serial key input test code, write to various variables
#ifdef DEBUG_STATUS
  if (Serial.available() > 0) {
    char input = Serial.read();

    switch (input) {
      case '1':
        rpm = 0;
        break;
      case '2':
        rpm = 500;
        break;
      case '3':
        rpm = 2000;
        break;
      case 'a':
        current = 2;
        break;
      case 'b':
        current = 20;
        break;
      case 'c':
        current = 80;
        break;
      case 'z':
        sensVoltage = 11;
        break;
      case 'x':
        sensVoltage = 14;
        break;
      case 'y':
        sensVoltage = 16;
        break;
      default:
        Serial.println("Invalid input.");
        return;
    }
  }
#endif


  //Debug
#ifdef DEBUG_STATUS

  Serial.print("Jannite: ");
  Serial.print((float)sensVoltage);
  Serial.println();
  Serial.print("Virta: ");
  Serial.print((float)current);
  Serial.println();
  Serial.print("Tila: ");
  Serial.print(motorStatus);
  Serial.println();
  Serial.print("Start tila: ");
  Serial.print(startTry);
  Serial.println();
  Serial.print("Odotus: ");
  Serial.print(startInterval);
  Serial.println();
  Serial.print("RPM: ");
  Serial.print(rpm);
  Serial.println();
#endif
}
 
Kiinanpojan tekoälyyn pyöräytin koodin ja käskin kirjoitella uusiksi nuo funktiot, ja nyt simulaation mukaan on ainakin toiminnallisesti jokseenkin toimiva koodi. Onkohan tilojen hallinta nyt niin, ettei esim. starttaus ala heti, kun moottorin nopeus laskee? Ainakin se teki tuon switch-case jutu erillä tavalla, huomasin omasta sähhellyksestäni, että logiikkani petti ainakin siinä, että funktiot olivat ehkä väärissä paikoissa. Uuteen tekoälykoodiin täytyy lisätä vielä vikatilat.

C++:
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <neotimer.h>

// System status variables
#define WAITING 0
#define STARTING 1
#define STARTED 2
#define RAMP_UP 3
#define RUNNING 4
#define FAIL 5
#define STOPPING 6

// Pin definitions
const int currentPin = A0;
const int voltagePin = A2;
const int motorPin = 2;
const int pwmPin = 9;
const int relayPin = 4;
const int rpmPin = 11;
const int starterPin = 3;

// Timers
Neotimer motorTime = Neotimer(10000);   // 10 seconds for startup detection
Neotimer stepTime = Neotimer(1000);     // 1 second for PWM step interval
Neotimer shutTime = Neotimer(15000);    // 15 seconds for engine shutdown
Neotimer crankTime = Neotimer(4000);    // 4 seconds for cranking
Neotimer crankWait = Neotimer(5000);    // 5 seconds wait after failed crank
Neotimer voltageWait = Neotimer(2000);  // 2 seconds wait for undervoltage

// LCD
LiquidCrystal_I2C lcd(0x27, 16, 2);

// Variables
int motorStatus = WAITING;
int startTry = 0;
int startInterval = 0;
int currentPwmStep = 0;
int pwmValues[] = { 24, 41, 58, 77 };  // PWM steps

// Current and voltage calculation
float voltageFactor = 5.00 / 1023.00;  // Factor to convert ADC reading to voltage

// Define the points for current calculation / the line equation
float x1 = 0.500;  // volts
float y1 = 0;      // amps
float x2 = 4.005;  // volts
float y2 = 150;    // amps
// Calculate the slope and intercept
float m = (y2 - y1) / (x2 - x1);
float b = y1 - m * x1;

float currentCal = 1;  // Variable to shift the whole current level

float sensVfactor = 20.00 / 4.9248;  // 4.9248 volts = 20 volts, factor to convert high voltage to 0-5 V
const int N = 25;                    // Number of current readings to average
const int Y = 25;                    // Number of sensed voltage readings to average
int readings[N];                     // Array to store the current readings
int readingsV[Y];                    // Array to store the sensed voltage readings

float sensVoltage = 12.0;  // Example voltage
float current = 0.0;       // Example current

//RPM Calculation
int rpm = 0;             //unsigned long rpm = 0;
unsigned long duration;  // Sensor pulse in duration

void setup() {
  Serial.begin(250000);

  // Change timers on pins to change PWM freq to 122 Hz
  // Pins D9 and D10 - 122 Hz
  TCCR1A = 0b00000001;  // 8bit
  TCCR1B = 0b00000100;  // x256 phase correct

  // Initialize I2C and check if LCD is connected
  Wire.begin();
  Wire.beginTransmission(0x27);
  if (Wire.endTransmission() == 0) {
    // LCD is connected, proceed with initialization
    lcd.begin(16, 2);
    lcd.backlight();
    lcd.clear();
  } else {
    // LCD is not connected, continue without initializing the LCD
    Serial.println("LCD not connected. Skipping.");
  }

  // Pin modes
  pinMode(currentPin, INPUT);
  pinMode(voltagePin, INPUT);
  pinMode(motorPin, INPUT_PULLUP);
  pinMode(pwmPin, OUTPUT);
  pinMode(relayPin, OUTPUT);
  pinMode(rpmPin, INPUT_PULLUP);
  pinMode(starterPin, OUTPUT);

  analogWrite(pwmPin, 255);  // Start with PWM off
}

void motorCrankState() {
  if (sensVoltage < 11.9 && motorStatus == WAITING) {
    if (!voltageWait.started()) {
      voltageWait.start();
    }
    if (voltageWait.done()) {
      motorStatus = STARTING;
      digitalWrite(relayPin, HIGH);
      voltageWait.stop();
      voltageWait.reset();
    }
  }
}

void crank() {
  if (startTry < 5 && startInterval == 0) {
    if (rpm <= 300) {
      digitalWrite(starterPin, HIGH);
      if (!crankTime.started()) {
        crankTime.start();
      }
      if (crankTime.done()) {
        digitalWrite(starterPin, LOW);
        crankTime.stop();
        crankTime.reset();
        startTry++;
        startInterval = 1;
      }
    } else if (rpm > 300) {
      digitalWrite(starterPin, LOW);
      startTry = 0;
      motorStatus = STARTED;
      crankTime.stop();
      crankTime.reset();
    }
  }
}

void Waiter() {
  if (startInterval == 1) {
    if (!crankWait.started()) {
      crankWait.start();
    }
    if (crankWait.done()) {
      startInterval = 0;
      crankWait.stop();
      crankWait.reset();
    }
  }
}

void motorRunning() {
  if ((rpm > 1800 || digitalRead(motorPin) == HIGH) && motorStatus == STARTED) {
    if (!motorTime.started()) {
      motorTime.start();
    }
    if (motorTime.done()) {
      motorStatus = RAMP_UP;
      motorTime.stop();
      motorTime.reset();
    }
  }
}

void rampUp() {
  if (stepTime.repeat()) {
    analogWrite(pwmPin, pwmValues[currentPwmStep]);
    currentPwmStep++;
    if (currentPwmStep >= sizeof(pwmValues) / sizeof(pwmValues[0])) {
      motorStatus = RUNNING;
      stepTime.stop();
      stepTime.reset();
    }
  }
}

void shutDown() {
  if (current < 5 && sensVoltage < 15 && motorStatus == RUNNING) {
    digitalWrite(relayPin, LOW);  // Turn off the ignition relay
    analogWrite(pwmPin, 15);      // Set PWM to 5% (reduce alternator load)

    if (!shutTime.started()) {
      shutTime.start();  // Start the shutdown timer
    }

    if (shutTime.done()) {
      analogWrite(pwmPin, 255);  // Set PWM to 100% (alternator electromagnet off)
      motorStatus = WAITING;     // Revert to WAITING state
      shutTime.stop();           // Stop the timer
      shutTime.reset();          // Reset the timer

      // Clear LCD and display shutdown message
      lcd.setCursor(0, 0);
      lcd.print("Shutdown        ");
      lcd.setCursor(0, 1);
      lcd.print("                ");
    }
  }
}

void lcdDisplay() {
  if (motorStatus == RUNNING) {
    lcd.setCursor(0, 0);
    lcd.print("Current: ");
    lcd.print(current);
    lcd.print(" A");
    lcd.setCursor(0, 1);
    lcd.print("Voltage: ");
    lcd.print(sensVoltage);
    lcd.print(" V");
  } else if (motorStatus == WAITING || motorStatus == STARTING) {
    lcd.setCursor(0, 0);
    lcd.print("Waiting...");
    lcd.setCursor(0, 1);
    lcd.print("Voltage: ");
    lcd.print(sensVoltage);
    lcd.print(" V");
  }
}

void loop() {

  // Calculate current from sensor reading
  // Calculate the charging current from the average and display it on the LCD screen.
  // Take N readings and store them in the array
  //  for (int i = 0; i < N; i++) {
  //   readings[i] = analogRead(currentPin);
  //    delay(1);  //Wait for a millisecond between readings
  //  }

  // Calculate the average of the N readings
  //  float sum = 0;
  //  for (int i = 0; i < N; i++) {
  //    sum += readings[i];
  //  }
  //  float average = sum / N;

  //  float voltage = average * voltageFactor;  // Convert ADC reading to voltage


  //  current = (m * voltage + b) * currentCal;  // Convert voltage to current using the linear equation, correct with current calibration value if needed


  // Calculate high voltage from voltage divider input
  // Calculate the charging voltage from the average and display it on the LCD screen.
  // Take Y readings and store them in the array
  //  for (int j = 0; j < Y; j++) {
  //    readingsV[j] = analogRead(voltagePin);
  //    delay(1);  // Wait for a millisecond between readings
  //  }

  // Calculate the average of the N readings
  //  float sumV = 0;
  //  for (int j = 0; j < Y; j++) {
  //    sumV += readingsV[j];
  //  }
  //  float averageV = sumV / Y;

  //  float voltageV = averageV * voltageFactor;  // Convert ADC reading to voltage

  //  sensVoltage = voltageV * sensVfactor;  // Convert voltage to real voltage


  // RPM Calculation from Hall Sensor input
  // {
  //   duration = pulseIn(rpmPin, FALLING, 500000);  // Times the amount of microseconds the motor is not timing IR, Times out after 100000 uS. Raise the timeout for slower RPM readings. .5 second
  //    rpm = 60000.0 / duration * 1000;              // See above
  //  }


  // Simulate sensor readings via Serial input
  if (Serial.available() > 0) {
    char input = Serial.read();

    switch (input) {
      case '1':
        rpm = 0;
        break;
      case '2':
        rpm = 500;
        break;
      case '3':
        rpm = 2000;
        break;
      case 'a':
        current = 2;
        break;
      case 'b':
        current = 20;
        break;
      case 'c':
        current = 80;
        break;
      case 'z':
        sensVoltage = 11;
        break;
      case 'x':
        sensVoltage = 14;
        break;
      case 'y':
        sensVoltage = 16;
        break;
      default:
        Serial.println("Invalid input.");
        return;
    }
  }

  // Handle motor status
  switch (motorStatus) {
    case WAITING:
      motorCrankState();
      lcdDisplay();
      break;
    case STARTING:
      crank();
      Waiter();
      lcdDisplay();
      break;
    case STARTED:
      motorRunning();
      break;
    case RAMP_UP:
      rampUp();
      lcdDisplay();
      break;
    case RUNNING:
      shutDown();
      lcdDisplay();
      break;
  }

  // Debug output
  Serial.print("Voltage: ");
  Serial.print(sensVoltage);
  Serial.print(" V, Current: ");
  Serial.print(current);
  Serial.print(" A, RPM: ");
  Serial.print(rpm);
  Serial.print(", Status: ");
  Serial.println(motorStatus);
}
 
Lisäksi jännä juttu kun lisäsin tuon STOPPING-tilan, mutta sille ei sitten löytynytkään käyttöä, niin halusin sen poistaa. Sitten kävi niin, että ei mene kääntäjästä läpi, jos tuone #define STOPPING 6 -rivin poistaa. Herja tulee liquid crystal-kirjastosta, joka kyllä on oikein asennettu. Lisäksi tuota STOPPING-tilaa ei esiinny missään koodissa.

Tähän ei voi auttaa ellet laita sitä koko virheilmoitusta mukaan.
Yleisempi ongelma on se että yrittää itse määrittää jotain joka on jo olemassa.


Kiinanpojan tekoälyyn pyöräytin koodin ja käskin kirjoitella uusiksi nuo funktiot, ja nyt simulaation mukaan on ainakin toiminnallisesti jokseenkin toimiva koodi. Onkohan tilojen hallinta nyt niin, ettei esim. starttaus ala heti, kun moottorin nopeus laskee? Ainakin se teki tuon switch-case jutu erillä tavalla, huomasin omasta sähhellyksestäni, että logiikkani petti ainakin siinä, että funktiot olivat ehkä väärissä paikoissa. Uuteen tekoälykoodiin täytyy lisätä vielä vikatilat.

C++:
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <neotimer.h>

// System status variables
#define WAITING 0
#define STARTING 1
#define STARTED 2
#define RAMP_UP 3
#define RUNNING 4
#define FAIL 5
#define STOPPING 6

// Pin definitions
const int currentPin = A0;
const int voltagePin = A2;
const int motorPin = 2;
const int pwmPin = 9;
const int relayPin = 4;
const int rpmPin = 11;
const int starterPin = 3;

// Timers
Neotimer motorTime = Neotimer(10000);   // 10 seconds for startup detection
Neotimer stepTime = Neotimer(1000);     // 1 second for PWM step interval
Neotimer shutTime = Neotimer(15000);    // 15 seconds for engine shutdown
Neotimer crankTime = Neotimer(4000);    // 4 seconds for cranking
Neotimer crankWait = Neotimer(5000);    // 5 seconds wait after failed crank
Neotimer voltageWait = Neotimer(2000);  // 2 seconds wait for undervoltage

// LCD
LiquidCrystal_I2C lcd(0x27, 16, 2);

// Variables
int motorStatus = WAITING;
int startTry = 0;
int startInterval = 0;
int currentPwmStep = 0;
int pwmValues[] = { 24, 41, 58, 77 };  // PWM steps

// Current and voltage calculation
float voltageFactor = 5.00 / 1023.00;  // Factor to convert ADC reading to voltage

// Define the points for current calculation / the line equation
float x1 = 0.500;  // volts
float y1 = 0;      // amps
float x2 = 4.005;  // volts
float y2 = 150;    // amps
// Calculate the slope and intercept
float m = (y2 - y1) / (x2 - x1);
float b = y1 - m * x1;

float currentCal = 1;  // Variable to shift the whole current level

float sensVfactor = 20.00 / 4.9248;  // 4.9248 volts = 20 volts, factor to convert high voltage to 0-5 V
const int N = 25;                    // Number of current readings to average
const int Y = 25;                    // Number of sensed voltage readings to average
int readings[N];                     // Array to store the current readings
int readingsV[Y];                    // Array to store the sensed voltage readings

float sensVoltage = 12.0;  // Example voltage
float current = 0.0;       // Example current

//RPM Calculation
int rpm = 0;             //unsigned long rpm = 0;
unsigned long duration;  // Sensor pulse in duration

void setup() {
  Serial.begin(250000);

  // Change timers on pins to change PWM freq to 122 Hz
  // Pins D9 and D10 - 122 Hz
  TCCR1A = 0b00000001;  // 8bit
  TCCR1B = 0b00000100;  // x256 phase correct

  // Initialize I2C and check if LCD is connected
  Wire.begin();
  Wire.beginTransmission(0x27);
  if (Wire.endTransmission() == 0) {
    // LCD is connected, proceed with initialization
    lcd.begin(16, 2);
    lcd.backlight();
    lcd.clear();
  } else {
    // LCD is not connected, continue without initializing the LCD
    Serial.println("LCD not connected. Skipping.");
  }

  // Pin modes
  pinMode(currentPin, INPUT);
  pinMode(voltagePin, INPUT);
  pinMode(motorPin, INPUT_PULLUP);
  pinMode(pwmPin, OUTPUT);
  pinMode(relayPin, OUTPUT);
  pinMode(rpmPin, INPUT_PULLUP);
  pinMode(starterPin, OUTPUT);

  analogWrite(pwmPin, 255);  // Start with PWM off
}

void motorCrankState() {
  if (sensVoltage < 11.9 && motorStatus == WAITING) {
    if (!voltageWait.started()) {
      voltageWait.start();
    }
    if (voltageWait.done()) {
      motorStatus = STARTING;
      digitalWrite(relayPin, HIGH);
      voltageWait.stop();
      voltageWait.reset();
    }
  }
}

void crank() {
  if (startTry < 5 && startInterval == 0) {
    if (rpm <= 300) {
      digitalWrite(starterPin, HIGH);
      if (!crankTime.started()) {
        crankTime.start();
      }
      if (crankTime.done()) {
        digitalWrite(starterPin, LOW);
        crankTime.stop();
        crankTime.reset();
        startTry++;
        startInterval = 1;
      }
    } else if (rpm > 300) {
      digitalWrite(starterPin, LOW);
      startTry = 0;
      motorStatus = STARTED;
      crankTime.stop();
      crankTime.reset();
    }
  }
}

void Waiter() {
  if (startInterval == 1) {
    if (!crankWait.started()) {
      crankWait.start();
    }
    if (crankWait.done()) {
      startInterval = 0;
      crankWait.stop();
      crankWait.reset();
    }
  }
}

void motorRunning() {
  if ((rpm > 1800 || digitalRead(motorPin) == HIGH) && motorStatus == STARTED) {
    if (!motorTime.started()) {
      motorTime.start();
    }
    if (motorTime.done()) {
      motorStatus = RAMP_UP;
      motorTime.stop();
      motorTime.reset();
    }
  }
}

void rampUp() {
  if (stepTime.repeat()) {
    analogWrite(pwmPin, pwmValues[currentPwmStep]);
    currentPwmStep++;
    if (currentPwmStep >= sizeof(pwmValues) / sizeof(pwmValues[0])) {
      motorStatus = RUNNING;
      stepTime.stop();
      stepTime.reset();
    }
  }
}

void shutDown() {
  if (current < 5 && sensVoltage < 15 && motorStatus == RUNNING) {
    digitalWrite(relayPin, LOW);  // Turn off the ignition relay
    analogWrite(pwmPin, 15);      // Set PWM to 5% (reduce alternator load)

    if (!shutTime.started()) {
      shutTime.start();  // Start the shutdown timer
    }

    if (shutTime.done()) {
      analogWrite(pwmPin, 255);  // Set PWM to 100% (alternator electromagnet off)
      motorStatus = WAITING;     // Revert to WAITING state
      shutTime.stop();           // Stop the timer
      shutTime.reset();          // Reset the timer

      // Clear LCD and display shutdown message
      lcd.setCursor(0, 0);
      lcd.print("Shutdown        ");
      lcd.setCursor(0, 1);
      lcd.print("                ");
    }
  }
}

void lcdDisplay() {
  if (motorStatus == RUNNING) {
    lcd.setCursor(0, 0);
    lcd.print("Current: ");
    lcd.print(current);
    lcd.print(" A");
    lcd.setCursor(0, 1);
    lcd.print("Voltage: ");
    lcd.print(sensVoltage);
    lcd.print(" V");
  } else if (motorStatus == WAITING || motorStatus == STARTING) {
    lcd.setCursor(0, 0);
    lcd.print("Waiting...");
    lcd.setCursor(0, 1);
    lcd.print("Voltage: ");
    lcd.print(sensVoltage);
    lcd.print(" V");
  }
}

void loop() {

  // Calculate current from sensor reading
  // Calculate the charging current from the average and display it on the LCD screen.
  // Take N readings and store them in the array
  //  for (int i = 0; i < N; i++) {
  //   readings[i] = analogRead(currentPin);
  //    delay(1);  //Wait for a millisecond between readings
  //  }

  // Calculate the average of the N readings
  //  float sum = 0;
  //  for (int i = 0; i < N; i++) {
  //    sum += readings[i];
  //  }
  //  float average = sum / N;

  //  float voltage = average * voltageFactor;  // Convert ADC reading to voltage


  //  current = (m * voltage + b) * currentCal;  // Convert voltage to current using the linear equation, correct with current calibration value if needed


  // Calculate high voltage from voltage divider input
  // Calculate the charging voltage from the average and display it on the LCD screen.
  // Take Y readings and store them in the array
  //  for (int j = 0; j < Y; j++) {
  //    readingsV[j] = analogRead(voltagePin);
  //    delay(1);  // Wait for a millisecond between readings
  //  }

  // Calculate the average of the N readings
  //  float sumV = 0;
  //  for (int j = 0; j < Y; j++) {
  //    sumV += readingsV[j];
  //  }
  //  float averageV = sumV / Y;

  //  float voltageV = averageV * voltageFactor;  // Convert ADC reading to voltage

  //  sensVoltage = voltageV * sensVfactor;  // Convert voltage to real voltage


  // RPM Calculation from Hall Sensor input
  // {
  //   duration = pulseIn(rpmPin, FALLING, 500000);  // Times the amount of microseconds the motor is not timing IR, Times out after 100000 uS. Raise the timeout for slower RPM readings. .5 second
  //    rpm = 60000.0 / duration * 1000;              // See above
  //  }


  // Simulate sensor readings via Serial input
  if (Serial.available() > 0) {
    char input = Serial.read();

    switch (input) {
      case '1':
        rpm = 0;
        break;
      case '2':
        rpm = 500;
        break;
      case '3':
        rpm = 2000;
        break;
      case 'a':
        current = 2;
        break;
      case 'b':
        current = 20;
        break;
      case 'c':
        current = 80;
        break;
      case 'z':
        sensVoltage = 11;
        break;
      case 'x':
        sensVoltage = 14;
        break;
      case 'y':
        sensVoltage = 16;
        break;
      default:
        Serial.println("Invalid input.");
        return;
    }
  }

  // Handle motor status
  switch (motorStatus) {
    case WAITING:
      motorCrankState();
      lcdDisplay();
      break;
    case STARTING:
      crank();
      Waiter();
      lcdDisplay();
      break;
    case STARTED:
      motorRunning();
      break;
    case RAMP_UP:
      rampUp();
      lcdDisplay();
      break;
    case RUNNING:
      shutDown();
      lcdDisplay();
      break;
  }

  // Debug output
  Serial.print("Voltage: ");
  Serial.print(sensVoltage);
  Serial.print(" V, Current: ");
  Serial.print(current);
  Serial.print(" A, RPM: ");
  Serial.print(rpm);
  Serial.print(", Status: ");
  Serial.println(motorStatus);
}

No ainakin lcdDisplay() kuuluu oikeastaan tuon switch-case ulkopuolelle, siis tilasta riippumatta päivitetään näyttö. Ja siellä funktiossa onkin jo oma tarkastus mitä missäkin tilassa näytetään niin voi kutsua joka kierroksella.

Sanoisin että crank() ja waiter() kannattaa yhdistää.

shutdown() ei tee mitään jos jännite on > 15, se taitaa olla turha ehto tuolla jos jännite > 15 tulee johonkin virhetarkastukseen tilojen ulkopuolelle.
Parempi ehkä tehdä sellainen "sammuta moottori"- funktio jota voit kutsua tuolta shutdown()/ "ollaan ajossa, pitää sammuttaa koska akku täynnä" ja myös "traaginen virhe, sammuta moottori". Samat jutut pitää kuitenkin suurimmaksi osaksi tehdä molemmissa tapauksissa.

Tarkasta sitten myös kommentoidusta osasta jännitteen ja virran mittaukset, ne m, b, x1, x2, y1, y2 laskennat on ihan turhia... Tarvitset molemmille vain mittauksen ja arvon skaalauksen oikealle alueelle.

"Onkohan tilojen hallinta nyt niin, ettei esim. starttaus ala heti, kun moottorin nopeus laskee"
-> ainakaan pikakatsauksella ei vaikuta siltä koska crank() kutsutaan vain starting- tilassa. Tietysti jos startin aikana kierrokset laskevat niin sitten kyllä.
 
@ississ, jännite- ja virtamittauksiin ei ole ehty muutoksia. Tekoäly poisti ne kokonaan, mutta liitin takaisin ne. On kyllä tiedossa, kuten aiemminkin olet maininnut, ettei tuollainen virran ja jännitteen laskenta ole ihan optimaalisin ratkaisu. :)

Yritin kyllä saada toimimaan sen map() -funktiolla tms. vastaavalla ja se ei harmi kyllä onnistunut. Tämä hieman tosiaankin varmaan turhan järeä systeemi kuitenkin on toiminut. Esim. tuo Pololun anturihan on 0 A ~ 0,5 V ja 150 A ~ 4 V.
 
Huhhuh, pakko laittaa koodi tänne, kun olen tehnyt siihen muutoksia. Pelkään, etten vain tee sille kohta "meisselihalvausta". :P

Muutoksia:

-FAIL tilalle käyttöä, esim. ylijännite ja epäonnistunut käynnistys, mutta myös nyt kierroluvun tarkastelu. Lisäsin myös samalla tyylillä värinäanturin tarkastelun, mutta voi olla, että koko anturi jätetään pois, koska kierroslukuanturi tulee olemaan keskeinen tätä systeemiä käynnistyksen vuoksi.
-lcd:tä päivitetty
-käynnistysfunktiossa (crank()) puuttui ajastimen sammutus ja nollaus, mikäli jännite heilahtelee ylös. Tämä on hyvin todennäköinen tilanne. Hyvin näytti pelevaan simulaattorissa, vaikka tuossa nyt tulee tuo else -ehto, mutta käsittääkseni tuo switch-case -kombinaatio ehkäisee tuon ajamista koko ajan.

Ja seuraavana, jos koodi vielä toimii, tulisi testata vielä fyysisesti skoopilla PWM sekä releet. Releet pitää käskyttää niin (HIGH/LOW), että sytytysvirtarele ei johda, kun sille ei anneta käskyä ja starttireleen ei myöskään johda, ku sitä ei käskytetä. Toki releestä voi vaihtaa nuo kummin päin vaan. Pitää vaan varmistua, ettei myöskään koodissa ole mitään ristiriitojen releiden ohjauksessa niin, että rele voisi jäädä väärään asentoon. Siltä se ei Arduino simulaattorin mukaan näytä, mikä on hyvä asia.


C++:
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <neotimer.h>

// System status variables
#define WAITING 0
#define STARTING 1
#define STARTED 2
#define RAMP_UP 3
#define RUNNING 4
#define FAIL 5
#define STOPPING 6

// Pin definitions
const int currentPin = A0;
const int voltagePin = A2;
const int motorPin = 2;
const int pwmPin = 9;
const int relayPin = 4;
const int rpmPin = 11;
const int starterPin = 3;

// Timers
Neotimer motorTime = Neotimer(10000);    // 10 seconds for startup detection
Neotimer stepTime = Neotimer(1000);      // 1 second for PWM step interval
Neotimer shutTime = Neotimer(15000);     // 15 seconds for engine shutdown
Neotimer crankTime = Neotimer(4000);     // 4 seconds for cranking
Neotimer crankWait = Neotimer(5000);     // 5 seconds wait after failed crank
Neotimer voltageWait = Neotimer(10000);  // 10 seconds wait for undervoltage
Neotimer sensorTime = Neotimer(5000);    // 5 seconds check time for vibration sensor condition on RUNNNING status

// LCD
LiquidCrystal_I2C lcd(0x27, 16, 2);

// Variables
int motorStatus = WAITING;
int startTry = 0;
int startInterval = 0;  // Waiting time on after start attempt
int currentPwmStep = 0;
int pwmValues[] = { 32, 64, 96, 128 };  // PWM steps

// Current and voltage calculation
float voltageFactor = 5.00 / 1023.00;  // Factor to convert ADC reading to voltage

// Define the points for current calculation / the line equation
float x1 = 0.500;  // volts
float y1 = 0;      // amps
float x2 = 4.005;  // volts
float y2 = 150;    // amps
// Calculate the slope and intercept
float m = (y2 - y1) / (x2 - x1);
float b = y1 - m * x1;

float currentCal = 1;  // Variable to shift the whole current level

float sensVfactor = 20.00 / 4.9248;  // 4.9248 volts = 20 volts, factor to convert high voltage to 0-5 V
const int N = 25;                    // Number of current readings to average
const int Y = 25;                    // Number of sensed voltage readings to average
int readings[N];                     // Array to store the current readings
int readingsV[Y];                    // Array to store the sensed voltage readings

float sensVoltage = 12.0;  // Example voltage
float current = 0.0;       // Example current

//RPM Calculation
int rpm = 0;             //unsigned long rpm = 0;
unsigned long duration;  // Sensor pulse in duration

void setup() {

  Serial.begin(250000);

  // Change timers on pins to change PWM freq to 122 Hz
  // Pins D9 and D10 - 122 Hz
  TCCR1A = 0b00000001;  // 8bit
  TCCR1B = 0b00000100;  // x256 phase correct

  // Initialize I2C and check if LCD is connected
  Wire.begin();
  Wire.beginTransmission(0x27);
  if (Wire.endTransmission() == 0) {
    // LCD is connected, proceed with initialization
    lcd.begin(16, 2);
    lcd.backlight();
    lcd.clear();
  } else {
    // LCD is not connected, continue without initializing the LCD
    Serial.println("LCD not connected. Skipping.");
  }

  // Pin modes
  pinMode(currentPin, INPUT);
  pinMode(voltagePin, INPUT);
  pinMode(motorPin, INPUT_PULLUP);
  pinMode(pwmPin, OUTPUT);
  pinMode(relayPin, OUTPUT);
  pinMode(rpmPin, INPUT_PULLUP);
  pinMode(starterPin, OUTPUT);

  analogWrite(pwmPin, 255);  // Start with PWM off
}

void motorCrankState() {
  if (sensVoltage < 11.9 && motorStatus == WAITING) {
    if (!voltageWait.started()) {
      voltageWait.start();
    }
    if (voltageWait.done()) {
      motorStatus = STARTING;
      digitalWrite(relayPin, HIGH);
      voltageWait.stop();
      voltageWait.reset();
    }
  } else {
    voltageWait.stop();
    voltageWait.reset();
  }
}

void crank() {
  if (startTry < 5 && startInterval == 0) {
    if (rpm <= 300) {
      digitalWrite(starterPin, HIGH);
      if (!crankTime.started()) {
        crankTime.start();
      }
      if (crankTime.done()) {
        digitalWrite(starterPin, LOW);
        crankTime.stop();
        crankTime.reset();
        startTry++;
        startInterval = 1;
      }
    } else if (rpm > 300) {
      digitalWrite(starterPin, LOW);
      startTry = 0;
      motorStatus = STARTED;
      crankTime.stop();
      crankTime.reset();
    }
  }
}

void Waiter() {
  if (startInterval == 1) {
    if (!crankWait.started()) {
      crankWait.start();
    }
    if (crankWait.done()) {
      startInterval = 0;
      crankWait.stop();
      crankWait.reset();
    }
  }
}

void motorRunning() {
  if ((rpm > 1800 || digitalRead(motorPin) == HIGH) && motorStatus == STARTED) {
    if (!motorTime.started()) {
      motorTime.start();
    }
    if (motorTime.done()) {
      motorStatus = RAMP_UP;
      motorTime.stop();
      motorTime.reset();
    }
  }
}

void rampUp() {
  if (stepTime.repeat()) {
    analogWrite(pwmPin, pwmValues[currentPwmStep]);
    currentPwmStep++;
    if (currentPwmStep >= sizeof(pwmValues) / sizeof(pwmValues[0])) {
      motorStatus = RUNNING;
      stepTime.stop();
      stepTime.reset();
    }
  }
}

void shutDown() {
  if (current < 5 && motorStatus == RUNNING) {
    digitalWrite(relayPin, LOW);  // Turn off the ignition relay
    analogWrite(pwmPin, 15);      // Set PWM to 5% (reduce alternator load)

    if (!shutTime.started()) {
      shutTime.start();  // Start the shutdown timer
    }

    if (shutTime.done()) {
      analogWrite(pwmPin, 255);  // Set PWM to 100% (alternator electromagnet off)
      motorStatus = WAITING;     // Revert to WAITING state
      shutTime.stop();           // Stop the timer
      shutTime.reset();          // Reset the timer
    }
  }
}

void failState() {  // Handle failed start attempts, overvoltage etc.

  if (startTry == 5) {  // Failed start
    motorStatus = FAIL;
    digitalWrite(starterPin, LOW);  // Starter relay off
    digitalWrite(relayPin, LOW);    // Ignition switch relay off

    lcd.setCursor(0, 0);  // On lcd print "Engine start failed"
    lcd.print("K");
    lcd.print((char)0xe1);
    lcd.print("ynnistys      ");
    lcd.setCursor(0, 1);
    lcd.print("ep");
    lcd.print((char)0xe1);
    lcd.print("onnistui     ");

  } else if (sensVoltage >= 15) {  // Overvoltage
    motorStatus = FAIL;
    digitalWrite(relayPin, LOW);

    lcd.setCursor(0, 0);  // On lcd print "Overvoltage"
    lcd.print("Ylij");
    lcd.print((char)0xe1);
    lcd.print("nnite      ");
    lcd.setCursor(0, 1);
    lcd.print("                ");

    if (!shutTime.started()) {
      shutTime.start();  // Start the shutdown timer
    }

    if (shutTime.done()) {
      analogWrite(pwmPin, 255);  // Set PWM to 100% (alternator electromagnet off)
      shutTime.stop();           // Stop the timer
      shutTime.reset();          // Reset the timer
    }

  } else if ((rpm <= 900 || rpm >= 3700) && motorStatus == RUNNING) {  // RPM sensor
    motorStatus = FAIL;
    digitalWrite(relayPin, LOW);

    lcd.setCursor(0, 0);  // On lcd print "Faulty rpm"
    lcd.print("Kierrosluku     ");
    lcd.setCursor(0, 1);
    lcd.print("virheellinen    ");

    if (!shutTime.started()) {
      shutTime.start();  // Start the shutdown timer
    }

    if (shutTime.done()) {
      analogWrite(pwmPin, 255);  // Set PWM to 100% (alternator electromagnet off)
      shutTime.stop();           // Stop the timer
      shutTime.reset();          // Reset the timer
    }

    //  } else if (motorStatus == RUNNING) { // Vibration sensor
    //    if (digitalRead(motorPin) == LOW) {
    //      if (!sensorTime.started()) {
    //        sensorTime.start();  // Start the sensor blackout timer
    //      }

    //      if (sensorTime.done()) {
    //        motorStatus = FAIL;
    //        analogWrite(pwmPin, 255);

    //        lcd.setCursor(0, 0);  // On lcd print "Faulty vibration signal"
    //        lcd.print("Virheellinen");
    //        lcd.setCursor(0, 1);
    //        lcd.print("V");
    //        lcd.print((char)0xe1);
    //        lcd.print("rin");
    //        lcd.print((char)0xe1);
    //        lcd.print("signaali");
    //        lcd.print("  ");

    //        sensorTime.stop();
    //        sensorTime.reset();
    //      }

    //    } else {  // If signal reverts to normal during the time, stop timer
    //      sensorTime.stop();
    //      sensorTime.reset();
  }
}
//}


void lcdDisplay() {
  if (motorStatus == RUNNING) {
    lcd.setCursor(0, 0);
    lcd.print("Virta: ");
    lcd.print(current);
    lcd.print(" A ");
    lcd.setCursor(0, 1);
    lcd.print("J");
    lcd.print((char)0xe1);
    lcd.print("nnite: ");
    lcd.print(sensVoltage);
    lcd.print(" V ");

  } else if (motorStatus == WAITING || motorStatus == STARTING) {
    lcd.setCursor(0, 0);
    lcd.print("Odottaa...      ");
    lcd.setCursor(0, 1);
    lcd.print("J");
    lcd.print((char)0xe1);
    lcd.print("nnite: ");
    lcd.print(sensVoltage);
    lcd.print(" V ");
  }
}

void loop() {

  // Calculate current from sensor reading
  // Calculate the charging current from the average and display it on the LCD screen.
  // Take N readings and store them in the array
  //  for (int i = 0; i < N; i++) {
  //   readings[i] = analogRead(currentPin);
  //    delay(1);  //Wait for a millisecond between readings
  //  }

  // Calculate the average of the N readings
  //  float sum = 0;
  //  for (int i = 0; i < N; i++) {
  //    sum += readings[i];
  //  }
  //  float average = sum / N;

  //  float voltage = average * voltageFactor;  // Convert ADC reading to voltage


  //  current = (m * voltage + b) * currentCal;  // Convert voltage to current using the linear equation, correct with current calibration value if needed


  // Calculate high voltage from voltage divider input
  // Calculate the charging voltage from the average and display it on the LCD screen.
  // Take Y readings and store them in the array
  //  for (int j = 0; j < Y; j++) {
  //    readingsV[j] = analogRead(voltagePin);
  //    delay(1);  // Wait for a millisecond between readings
  //  }

  // Calculate the average of the N readings
  //  float sumV = 0;
  //  for (int j = 0; j < Y; j++) {
  //    sumV += readingsV[j];
  //  }
  //  float averageV = sumV / Y;

  //  float voltageV = averageV * voltageFactor;  // Convert ADC reading to voltage

  //  sensVoltage = voltageV * sensVfactor;  // Convert voltage to real voltage


  // RPM Calculation from Hall Sensor input
  // {
  //   duration = pulseIn(rpmPin, FALLING, 500000);  // Times the amount of microseconds the motor is not timing IR, Times out after 100000 uS. Raise the timeout for slower RPM readings. .5 second
  //    rpm = 60000.0 / duration * 1000;              // See above
  //  }


  // Simulate sensor readings via Serial input
  if (Serial.available() > 0) {
    char input = Serial.read();

    switch (input) {
      case '1':
        rpm = 0;
        break;
      case '2':
        rpm = 500;
        break;
      case '3':
        rpm = 2000;
        break;
      case 'a':
        current = 2;
        break;
      case 'b':
        current = 20;
        break;
      case 'c':
        current = 80;
        break;
      case 'z':
        sensVoltage = 11;
        break;
      case 'x':
        sensVoltage = 14;
        break;
      case 'y':
        sensVoltage = 16;
        break;
      default:
        Serial.println("Invalid input.");
        return;
    }
  }

  // Handle motor status
  switch (motorStatus) {
    case WAITING:
      motorCrankState();
      break;
    case STARTING:
      crank();
      Waiter();
      break;
    case STARTED:
      motorRunning();
      break;
    case RAMP_UP:
      rampUp();
      break;
    case RUNNING:
      shutDown();
      break;
  }

  lcdDisplay();
  failState();

  // Debug output
  Serial.print("Voltage: ");
  Serial.print(sensVoltage);
  Serial.print(" V, Current: ");
  Serial.print(current);
  Serial.print(" A, RPM: ");
  Serial.print(rpm);
  Serial.print(", Status: ");
  Serial.println(motorStatus);
}
 
Viimeksi muokattu:
Huhhuh, pakko laittaa koodi tänne, kun olen tehnyt siihen muutoksia. Pelkään, etten vain tee sille kohta "meisselihalvausta". :P

Muutoksia:

-FAIL tilalle käyttöä, esim. ylijännite ja epäonnistunut käynnistys, mutta myös nyt kierroluvun tarkastelu. Lisäsin myös samalla tyylillä värinäanturin tarkastelun, mutta voi olla, että koko anturi jätetään pois, koska kierroslukuanturi tulee olemaan keskeinen tätä systeemiä käynnistyksen vuoksi.
-lcd:tä päivitetty
-käynnistysfunktiossa (crank()) puuttui ajastimen sammutus ja nollaus, mikäli jännite heilahtelee ylös. Tämä on hyvin todennäköinen tilanne. Hyvin näytti pelevaan simulaattorissa, vaikka tuossa nyt tulee tuo else -ehto, mutta käsittääkseni tuo switch-case -kombinaatio ehkäisee tuon ajamista koko ajan.

Ja seuraavana, jos koodi vielä toimii, tulisi testata vielä fyysisesti skoopilla PWM sekä releet. Releet pitää käskyttää niin (HIGH/LOW), että sytytysvirtarele ei johda, kun sille ei anneta käskyä ja starttireleen ei myöskään johda, ku sitä ei käskytetä. Toki releestä voi vaihtaa nuo kummin päin vaan. Pitää vaan varmistua, ettei myöskään koodissa ole mitään ristiriitojen releiden ohjauksessa niin, että rele voisi jäädä väärään asentoon. Siltä se ei Arduino simulaattorin mukaan näytä, mikä on hyvä asia.

switch-case on loogisesti sama kuin if () else if () else if () jne.

Releet pitää kytkeä niin että käynnistettäessä ne ovat siinä tilassa jossa pitää ollakin ilman että käännetään setup- funktiossa "oletustilaan".
Eli todennäköisesti sammutusrele pitää moottorin sammuneena ja käynnistysrele on irti. Näin tehden moottori sammuu jos arduinolta lähtee sähköt joka vastaa tietysti virrat laitetaan päälle tilannetta.
Ja muista että releessä on kytkentäviive, yleisesti joku 100-200ms on turvallinen oletus mutta riippuu releestä.
 

Statistiikka

Viestiketjuista
266 240
Viestejä
4 610 091
Jäsenet
75 786
Uusin jäsen
krisipisi

Hinta.fi

Back
Ylös Bottom