Autolaturi mökille varavoimaksi Arduinon avulla

tosin tollasella systeemillä et voi käyttää ehkä esim monitoria joka vaatii 12v koska autosähkössä 12v on vain nimellisjännite ja todellisuudessa moottorin käydessä sen 13,8-14.4v uskoisin että autosähkökäyttöön olevat vehkeet kestää sen 16v vielä helposti, ja jos jotain elektroniikkaa pitää ajaa regu siellä olisi oltava.ja joo kyllä siellä pitäisi olla logiigga joka ei anna sammutus funktion ajaa pwm 100 jos moottori käy
 
@ississ, Joo tuohan oli hyvä. Eli periaatteessa kolmelta anturilta ikään kuin tuossa vahvistus ennen kuin palautetaan akkujännite lataussäätimeen.

Kysyit, että mitä muita laitteita järjestelmässä on. On perus mökkivarusteita: aurinkolataussäädin, radio, lamppuja, jääkaappi, tarpeen mukaan erikseen päälle kytkettävä 230 V invertteri.

Jääkaape on ainakin tarkoitettu karavaanikäyttöön, joten siinä ainakin lienee jännitteen yläraja korkeampi.
 
@TemeV , mutta voiko jännite kuitenkin nousta 16 volttiin? Miten käy laitteille, jotka kytkettynä systeemiin, jossa jännite hetkellisesti 16 volttia?
Ei sen pitäisi pystyä noin korkealle hyppäämään sekuntien aikaskaalassa. Jos mietitään että latausvirta olisi vaikkapa 50A normaalisti. Jos saisit jollain erikoisella vikatilanteella nostettua vaikkapa 150 ampeeriin, akku latautuu ja jännite nousee siinä kahdessa sekunnissa saman verran kuin normaalisti kuudessa sekunnissa Eli ei juurikaan.

Johtojen resistanssit toki nostaa jännitettä laturin päässä ja siellä päässä saatettaisiin jo lähennellä sitä 16 volttia, riippuen johtojen paksuudesta ja liitosten laadusta. Jos kuitenkin laitteet on kytketty akkuun, eikä suoraan laturiin, ei ne näe sitä jännitettä. Akun resistanssi lienee joitain milliohmeja, ja tuo 150 ampeeria nostaisi siis jännitettä joitain satoja millivoltteja.
 
@TemeV , no tuolla perusteella, jos siis saa laitettua tuommoisen lisäturvapiirin katkaisemaan ohjauksen laturilta, niin riittäisi aivan ok tämän systeemiin suojaukseksi. Toki silti kannattaa tuossa nuo @ississ :n aiemmin mainitsemat jutut laittaa sinne koodiin.

Täytyy toki muistaa, että varmaan todennäköisyys tämmöiselle tapahtumalle on lähellä nollaa, mutta ei sitä varmaan liian varovainen tässä olla.
 
Tähän väliin sujuvasti koodikysymys taas. Voiko tuossa ajastuksissa käyttää tuota samaa lastTime -muuttuja vai pitääkö kaikille ajastuksille olla omat? Yritän saada noita delay() funktioita pois tuosta koodista, että olisi "nätimpi" koodi.

Näyttääkö toimivalta äkkiseltään katsottuna:

C++:
void shutDown() {  //engine shutdown, execute relay

  //If the charging current drops to 0 to 5 amperes and charging voltage is under 15 volts (safety) and motor is running
  // start the shutdown function and turn off the relay
  if (current >= 0 && current < 5 && sensVoltage < 15 && digitalRead(motorPin) == HIGH && motorStatus == 2) {
    digitalWrite(relayPin, LOW);

    if (millis() - lastTime >= 2000) {  //wait at least 2 seconds to engine stall down

      analogWrite(pwmPin, 255);  // Stop Duty Cycle at 100 %, alternator electromagnet off

      lcd.clear();
      lcd.setCursor(0, 0);
      lcd.print("Sammutus");

      motorStatus = 3;  //motor has run, to reset, switch power off, on next startup, it will be reset

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

  } else if (motorStatus == 2) {
    lastTime = millis();

    digitalWrite(relayPin, HIGH);  //else, keep relay on

#ifdef DEBUG
    Serial.println("Kaynnissa.");
#endif
  }
}
 
Viimeksi muokattu:
En nyt oo varma mutta jos tuo lasttime oon ns geneerinen muuttuja jota siis käytetään monessa void jotain se käsittääkseni pitäisi myös joka loop kierroksella päivittää tai se päivittää vain kun joku ajaa tuon
Koodi:
lastTime =millis();

jos sulla on void loop()

{

lastTime = millis();


}
niin silloin joka loop kierroksella sulla on tuo arvo johon voit verrata onko aika kulunut. mutta nyt pähkäilen kun tuossa esimerkissä päivität vain lastTimen jos motorstatus == 2 joten jos tuon lisää looppiin rikkooko se jonkun logiigan ? jos rikkoo niin teet toisen esim generallastmillis = millis(); johon vertaa yleismailmallisesti paljonko aikaa on kulunut.
 
Tähän väliin sujuvasti koodikysymys taas. Voiko tuossa ajastuksissa käyttää tuota samaa lastTime -muuttuja vai pitääkö kaikille ajastuksille olla omat? Yritän saada noita delay() funktioita pois tuosta koodista, että olisi "nätimpi" koodi.

Näyttääkö toimivalta äkkiseltään katsottuna:

C++:
void shutDown() {  //engine shutdown, execute relay

  //If the charging current drops to 0 to 5 amperes and charging voltage is under 15 volts (safety) and motor is running
  // start the shutdown function and turn off the relay
  if (current >= 0 && current < 5 && sensVoltage < 15 && digitalRead(motorPin) == HIGH && motorStatus == 2) {
    digitalWrite(relayPin, LOW);

    if (millis() - lastTime >= 2000) {  //wait at least 2 seconds to engine stall down

      analogWrite(pwmPin, 255);  // Stop Duty Cycle at 100 %, alternator electromagnet off

      lcd.clear();
      lcd.setCursor(0, 0);
      lcd.print("Sammutus");

      motorStatus = 3;  //motor has run, to reset, switch power off, on next startup, it will be reset

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

  } else if (motorStatus == 2) {
    lastTime = millis();

    digitalWrite(relayPin, HIGH);  //else, keep relay on

#ifdef DEBUG
    Serial.println("Kaynnissa.");
#endif
  }
}

Samaa muuttujaa voi käyttää jos ei ole vaaraa että eri viiveet menee päällekkäin.
Tarkoittaa siis sitä että todennäköisesti joillakin muilla muuttujilla pitää tallentaa tilatieto missä ollaan ja sen mukaan tulkita ajastuksia.

Seurannan kannalta voisi olla parempi määrittää nuo motorstatus- arvot nimillä eikä numeroilla. Koodina ihan sama asia mutta silmälle helpompi kun ei tarvitse muistaa mikä tila oli 2 ja mikä 3.
Esimerkiksi

#define MOTOR_OFF 0
#define MOTOR_STARTING 1
#define MOTOR_RUNNING 2
#define MOTOR_STOPPING 3

ja koodissa:

int motorStatus = MOTOR_OFF;

Ja muista varmistaa että tilat/ehdot ovat sellaisia ettei mene vahingossa ohi tai suoriteta liian monta kertaa.
Oikeastaan niin että kun ehto täyttyy niin tila vaihtuu ja silloin on muut ehdot, tarvitset siis ainakin "ajossa" -> "sammumassa" -> "sammutettu" jolloin ajossa tutkitaan virtarajat jne.
Kun pitää aloittaa sammutus niin silloin asettaa lastTime, sammutusrele, jne ja seuraava tila.
"Sammumassa"- tilassa sitten tutkitaan sitä aikaa eikä enää välitetä virroista (ehkä kannattaa asettaa myös pwm minimiin edellä) ja taas asetetaan seuraava tila kun aikaraja täyttyy.

Näin varmistat myös sen että lastTime (tai muu vastaava) joka tässä on "tapahtuman alkuhetki" päivittyy vain tilan vaihdossa silloin kun kyseinen tapahtuma oikeasti alkaa.
Asettamalla jokaisella kierroksella lastTime = millis() kun kone on käynnissä on tavallaan ihan turhaa ja varsinkin silloin sitä ei voi uusiokäyttää.

Jotain tähän tyyliin siis. Ei takeita toiminnasta.
C++:
void shutDown() {

  if ( motorStatus == MOTOR_RUNNING ) {
    // Käynnissä
    if ( ( current >= 0 ) && ( current < 5 ) && ( sensVoltage < 15 ) && ( digitalRead(motorPin) == HIGH ) ) {
      //  ehdot täyttyy -> pitää sammuttaa

      lcd.clear();
      lcd.setCursor(0, 0);
      lcd.print("Sammutus alkaa");

#ifdef DEBUG
      Serial.println("Sammutus alkaa");
#endif
      // Ehkä myös tämä
      //analogWrite(pwmPin, 15);  // 5% pwm
      digitalWrite(relayPin, LOW);
      motorStatus = MOTOR_STOPPING;
      lastTime = millis();
    }
  } else if ( motorStatus == MOTOR_STOPPING ) {
    // Sammutus menossa
    if ( ( millis() - lastTime ) >= 2000 ) {
      // Aika täynnä

      if ( [ tähän rpm/tärinä luku -> onko moottori oikeasti sammunut ? ] ) {
        // Ehkä varmuuden vuoksi tarkastaa latausvirta ja jännite myös ennen pwm- asetusta
        // Sammunut
        analogWrite(pwmPin, 255);

        lcd.clear();
        lcd.setCursor(0, 0);
        lcd.print("Sammutettu");

#ifdef DEBUG
        Serial.println("Sammutettu");
#endif
        motorStatus = MOTOR_OFF;
        // Sammutusrele pitäisi voida palauttaa jo tässä (koska tarkastettu että sammui)
        // tai sillä missä pitäisi olla silloin kun tehdään seuraava startti.
        //digitalWrite(relayPin, HIGH);
      }
    }
  }

}
 
No niin, eilisen ja tämän päivän aloin tuota koodia ähräämään ja löysin muutamankin ajastinkirjaston, kun en vieläkään tajua tuota millis() funktion käyttöä sen sijaan, että käyttäisi yksinkertaista, mutta muun toiminnan pysäyttävää delay() -funktiota. Löysin kirjastot BlockNot ja Neotimer, josta jälkimmäisen sain nähtävästi toimimaan tekoälykoodin avustuksella.

Periaatteessa tuossa BlockNotissa olisi kai vielä yksinkertaisempi käyttö ollut, mutta jotain siinä missasin, kun funktio suoritettiin heti. Varmaankin ajastin lähti käyntiin aina heti koodin alussa kutsumisesta, mutta Neotimerin avulla siis nyt näyttäisi nuo aikapohjaiset jutut toimivan ilman, että mikään muu keskeytyy. Tämän näkee hyvin sarjaportin tulosta, että tilat vaihtuvat hienosti ajastimen mukaan.

Sen verran tässä on jo kuitenkin "osannut", että pystyy nyt liittämään koodia, ilman että punaista tekstiä on ruutu täynnä! ;)

P.S. Tuskinpa tämä nyt näin meni, sanoo perus pessimisti. Vielä pitää testata, ja katsoa, mikä on esim. ulostulo, toimiiko PWM-portaat odotetusti. ;)

C++:
#define DEBUG  //Serial debug, comment to disable
#include <Wire.h>
#include <LiquidCrystal_I2C.h>

#include <neotimer.h>  //Timer from Neotimer library
Neotimer motorTime = Neotimer(6000);  // 6 second timer for startup detection
Neotimer stepTime = Neotimer(1000);   // 1 second timer for PWM step interval
Neotimer shutTime = Neotimer(2000);   // 2 second timer for engine shutdown (wait before switching PWM back to 100 %, electromagnet off)

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, HIGH = motor running, TEST purpose LOW
const int pwmPin = 9;                  //PWM output pin
const int relayPin = 4;                //Relay control pin
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
float sensVoltage;                   //store sensed voltage
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
int motorStatus;                     //0=stopped, 1=starting, 2=running, 3=stopped but did run, on status 3, just switch off and on again to go back 0
int pwmValues[] = { 24, 41, 58, 77 };  // PWM duty cycle steps, 77 = ~30 %
int currentPwmStep = 0;
// int lcdStep;                      //0=current display, 1=voltage display

void setup() {
  Serial.begin(250000);  // Start the serial communication
#ifdef DEBUG
  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
    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
  // Start Duty Cycle at 100 %, alternator electromagnet off, for starting the engine
  analogWrite(pwmPin, 255);
#ifdef DEBUG
  Serial.println("Setup ok.");
#endif
}

void motorRunning() {  //engine running function
  if (digitalRead(motorPin) == HIGH && motorStatus == 0) {
#ifdef DEBUG
    Serial.println("Kayntitieto...");
#endif
    if (!motorTime.started()) {  // Start the motorTime timer only, if it has not been started yet
      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 = 1;
#ifdef DEBUG
      Serial.println("Kaynnistetty.");
#endif
    }
  } else if (motorStatus == 0) {
    motorTime.stop();  // Stop the timer if motorPin is not high
#ifdef DEBUG
    Serial.println("Kaynnistys...");
#endif
    lcd.setCursor(0, 0);  //On lcd print Finnish to start the engine
    lcd.print("K");
    lcd.print((char)0xe1);
    lcd.print("ynnist");
    lcd.print((char)0xe1);
    lcd.print("   ");
    lcd.setCursor(0, 1);
    lcd.print("moottori");
  }
}

void rampUp() {  //rampup pwm
  // Ramp pwm up
  if (motorStatus == 1) {
    if (stepTime.repeat()) {
      analogWrite(pwmPin, pwmValues[currentPwmStep]);
      currentPwmStep++;
#ifdef DEBUG
      Serial.println("Ramp up...");
#endif
      if (currentPwmStep >= sizeof(pwmValues) / sizeof(pwmValues[0])) {  //checking the pwm step count
        motorStatus = 2;
      }
    }
  }
}

void shutDown() {  //engine shutdown, execute relay
  //If the charging current drops to 0 to 5 amperes and charging voltage drops under 14 volts and motor is running
  // start the shutdown function and turn off the relay
  if (current >= 0 && current < 5 && sensVoltage < 15 && digitalRead(motorPin) == HIGH && motorStatus == 2) {
    digitalWrite(relayPin, LOW);
    analogWrite(pwmPin, 15);  // Duty Cycle to 5 % before running down engine
    if (!shutTime.started()) {  //wait for shutDelay time to engine have time to stall down
      shutTime.start();
    }
    if (shutTime.done()) {
      analogWrite(pwmPin, 255);  // Stopped Duty Cycle at 100 %, alternator electromagnet off
      lcd.clear();
      lcd.setCursor(0, 0);
      lcd.print("Sammutus");
      motorStatus = 3;  //motor has run, to reset, switch power off, on next startup, it will be reset
#ifdef DEBUG
      Serial.println("Sammutettu.");
#endif
    }
  } else if (motorStatus == 2) {
    motorTime.stop();              // Stop the timer if motor is in state 2 (normal running) not high
    digitalWrite(relayPin, HIGH);  //else, keep relay on and don't
#ifdef DEBUG
    Serial.println("Kaynnissa.");
#endif
  }
}

void showCurrent() {  //show current and voltage on lcd only when normal running state
  if (motorStatus == 2) {
    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
    Serial.println("Virta LCD.");
#endif
  }
}

void loop() {
  //calculate current from shunt 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(5);  // wait for 5 milliseconds 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 new linear equation

  //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(5);  // wait for 5 milliseconds 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

  motorRunning();
  rampUp();
  shutDown();
  showCurrent();
  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();
}
 
Viimeksi muokattu:
tota siis en itse käytä kyllä timer kirjastoja kun se onnistuu siis logiigalla jos sulla on esim rampup mitä ajetaan jossa nyt on delya(xxxX) niin mä teen esim long rampuptimer
sitten siellä void rampup
Koodi:
if(millis<millis()+1000)
{
#tää ajetaan silloin kun 1000ms eli sekunti onkulunut ja silloin se päivittää tuon rampuptimering.
rampuptimer = millis();
}
tossa vielä joku webppi sivu jossa tuota selostetaan.
 
No niin, eilisen ja tämän päivän aloin tuota koodia ähräämään ja löysin muutamankin ajastinkirjaston, kun en vieläkään tajua tuota millis() funktion käyttöä sen sijaan, että käyttäisi yksinkertaista, mutta muun toiminnan pysäyttävää delay() -funktiota. Löysin kirjastot BlockNot ja Neotimer, josta jälkimmäisen sain nähtävästi toimimaan tekoälykoodin avustuksella.

Joskus kirjastot on ihan ok. Se mikä noiden kanssa voi tulla aika nopeastikin vastaan on koodin koko, kirjastot kun tuppaa tekemään kaikenlaista mitä itse ei tarvitsisikaan. Tuo nyt ei vielä ole kovin iso mutta kannattaa pitää mielessä.
 
Joskus kirjastot on ihan ok. Se mikä noiden kanssa voi tulla aika nopeastikin vastaan on koodin koko, kirjastot kun tuppaa tekemään kaikenlaista mitä itse ei tarvitsisikaan. Tuo nyt ei vielä ole kovin iso mutta kannattaa pitää mielessä.
No, nykypäivänä jollakin arduinolla ei pitäisi ihan yhdellä tai kahdella kirjastolla tulla muisti sentään täyteen ellei oma koodi ole jo todella iso, toki osa kirjastoista on melkoista bloattia. Itse kyllä muistan kun joskus vuosia sitten tein olikohan se joku ATtiny tai vastaavan pienen kontrollerin kanssa projektia. Kontrollerissa tilaa oli olikohan 2 vai peräti 4 kilotavua, siinä sai jo välillä repiä kirjastoja riekaleiksi ja koittaa saada muutamia tavuja sieltä sun täältä säästettyä että koodi mahtui kontrolleriin. Ainakin HD44780-LCD:lle tein minimaalisen version josta oli kaikki turha poistettu, samoin DS18B20-onewire-kirjastolle tuli tehtyä vastaava "laihdutusleikkaus".
 
@Nasty76 , tuttu sivu! Lueskelin jo tuota aiemmin monta kertaa läpi, mutta ei vain mennyt kaaliin, miten tuo homma tapahtuu. :geek:

@ississ, joo tuossa katsoinkin tuota "Dynamic Memory" -kohtaa, jossa tuli tätä kirjastoa käyttäen 39 % käyttöön, ilman tätä jäi muistaakseni selvästi alle 30 prosenttiin.. Liekö käytönaikainen muistinkäyttö sitten vielä enempi? Parempihan se varmaan olisi käyttää mahdollisimman vähän kirjastoja, jolloin säästettäisiin resursseja. Kaipa se hidasteleva käyttökokemus monissa nykyvermeissä osin johtuu kirjastojen käytöstä. Ainakin varmaan yksi syy. Turhaa hommaa prosessorilla?

@Hyrava, kaikkea sitä pystyy tekemään, kun osaa! On nämä ohjelmointihommat mulle melkein täyttä hepreaa, monesti jokaisesta kirjaimesta tulee mieleen 10 kysymystä, eikä mistään löydä selkeää vastausta. Parempi olisi katsoa vaikka vuoden ajan päivittäin näitä hommia niin, että joku aina on neuvomassa oikeaan suuntaan, mutta ei nyt sellaisia ole lähipiirissä, eteenpäin on päästy varsinkin tämän ketjun vinkeillä. Koodia en osaa kirjoittaa paljon ollenkaan, mutta pianolla pystyn tekemään pari kolme biisiä päivässä. ;)

Tähän ketjuun on jo osallistunut uskomattoman auttavaisia henkilöitä, ja olen jo aikaisemminkin varmaan kiitellyt, mutta mitäpä suotta jarruttelemaan, niin kiitos nyt vaan taas. Homma etenee hitaasti, mutta välillä vakaasti!
 
Viimeksi muokattu:
Tosin kirjoitin näemmä ihan höpöhöpö esimerkin. piti oikeen ihmetellä arduino online emulaattorilla miten oon ton ite tehny joten tähän selkeä lukunen esimerkki miten noilla millis arvoilla voidaan tehdä non block ajoa.
Koodi:
int period = 1000; //Tämä on viive joka odotetaan
unsigned long ramptimer = 0; // tämä on muuttuja esim jos ajetaan ramppia ylös tms tämä esimerkki tulostaa kerran sekunnissa tuon hello n

 
void setup() {
    Serial.begin(115200);
}
 
void loop() {
    
  
  
   if(ramptimer + period  <millis())
   {
    ramptimer=millis();
       Serial.println("Hello");
    }
}
 
No, nykypäivänä jollakin arduinolla ei pitäisi ihan yhdellä tai kahdella kirjastolla tulla muisti sentään täyteen ellei oma koodi ole jo todella iso, toki osa kirjastoista on melkoista bloattia. Itse kyllä muistan kun joskus vuosia sitten tein olikohan se joku ATtiny tai vastaavan pienen kontrollerin kanssa projektia. Kontrollerissa tilaa oli olikohan 2 vai peräti 4 kilotavua, siinä sai jo välillä repiä kirjastoja riekaleiksi ja koittaa saada muutamia tavuja sieltä sun täältä säästettyä että koodi mahtui kontrolleriin. Ainakin HD44780-LCD:lle tein minimaalisen version josta oli kaikki turha poistettu, samoin DS18B20-onewire-kirjastolle tuli tehtyä vastaava "laihdutusleikkaus".

Joo, 44780 ja ds- koodia on myös puukotettu mahtumaan pienempiin piireihin.
Olen käyttänyt enemmän atmega48/88/168 ja attiny13/15/25 kuin noita isompia, siksi tulee helposti mietittyä ensin myös koon puolesta.

@Nasty76 , tuttu sivu! Lueskelin jo tuota aiemmin monta kertaa läpi, mutta ei vain mennyt kaaliin, miten tuo homma tapahtuu. :geek:

@ississ, joo tuossa katsoinkin tuota "Dynamic Memory" -kohtaa, jossa tuli tätä kirjastoa käyttäen 39 % käyttöön, ilman tätä jäi muistaakseni selvästi alle 30 prosenttiin.. Liekö käytönaikainen muistinkäyttö sitten vielä enempi? Parempihan se varmaan olisi käyttää mahdollisimman vähän kirjastoja, jolloin säästettäisiin resursseja. Kaipa se hidasteleva käyttökokemus monissa nykyvermeissä osin johtuu kirjastojen käytöstä. Ainakin varmaan yksi syy. Turhaa hommaa prosessorilla?

@Hyrava, kaikkea sitä pystyy tekemään, kun osaa! On nämä ohjelmointihommat mulle melkein täyttä hepreaa, monesti jokaisesta kirjaimesta tulee mieleen 10 kysymystä, eikä mistään löydä selkeää vastausta. Parempi olisi katsoa vaikka vuoden ajan päivittäin näitä hommia niin, että joku aina on neuvomassa oikeaan suuntaan, mutta ei nyt sellaisia ole lähipiirissä, eteenpäin on päästy varsinkin tämän ketjun vinkeillä. Koodia en osaa kirjoittaa paljon ollenkaan, mutta pianolla pystyn tekemään pari kolme biisiä päivässä. ;)

Tähän ketjuun on jo osallistunut uskomattoman auttavaisia henkilöitä, ja olen jo aikaisemminkin varmaan kiitellyt, mutta mitäpä suotta jarruttelemaan, niin kiitos nyt vaan taas. Homma etenee hitaasti, mutta välillä vakaasti!

Hidastelu riippuu kovasti siitä mitä kaikkea taustalla tehdään.
Hyvä esimerkki on arduino-framen digitalWrite(), jokaisella kutsukerralla se päättelee asioita annetun pinnin numeron perusteella (tämä tarvitaan että toimii kätevästi eri alustoilla), poistaa pwm:n käytöstä (= jos pinnille ei käytetä pwm:ää niin ei tarvitsisi), tallentaa avr- status- rekisterin ja poistaa keskeytykset käytöstä. Vasta sitten asetetaan lähtö ja palautetaan statusrekisteri ja keskeytykset.
Eli silloin kun tiedetään että joku pinni on vain digilähtö niin suurin osa tuosta on tavallaan turhaa ja voisi vaan kääntää portin oikeaan asentoon...

Ja kun aiemmin mainitsit koodin ymmärtämisestä siihen kuuluu yhtenä osana myös helppolukuisuus.
Kannattaa siis vaihtaa ne motorStatus = 2 -->> motorStatus = MOTOR_ON niin ei tarvitse muistaa mikä numero on mikä, antaa vaan jokaiselle numerolle selkeän tekstin.
Tiedoston alkuun vaan jonnekin #define MOTOR_ON 2 ja kaikki muut vastaavat myös, helpottaa lukemista huomattavasti eikä kuluta muistia yhtään koska kääntäjä käyttää suoraan noita numeroita.


Tosin kirjoitin näemmä ihan höpöhöpö esimerkin. piti oikeen ihmetellä arduino online emulaattorilla miten oon ton ite tehny joten tähän selkeä lukunen esimerkki miten noilla millis arvoilla voidaan tehdä non block ajoa.
Koodi:
int period = 1000; //Tämä on viive joka odotetaan
unsigned long ramptimer = 0; // tämä on muuttuja esim jos ajetaan ramppia ylös tms tämä esimerkki tulostaa kerran sekunnissa tuon hello n

 
void setup() {
    Serial.begin(115200);
}
 
void loop() {
   
 
 
   if(ramptimer + period  <millis())
   {
    ramptimer=millis();
       Serial.println("Hello");
    }
}

Ajan vertailu pitäisi tehdä (millis() - alkuaika) ja vertaamalla sitä rajaan, muuten tulee epämääräisiä aikoja kun millis vuotaa yli (n. 50 päivän välein). Eli mieluummin aina näin:
C++:
if ( ( millis() - ramptimer ) > period ) {
  ramptimer = millis();
  Serial.print("Hello");
}

Täältä lukemista jos kaipaa varmistusta: Arduino Tutorial: Avoiding the Overflow Issue When Using millis() and micros() – Norwegian Creations , How can I handle the millis() rollover?

Ja kyllä, jos oletetaan että systeemi ei koskaan ole ~50 päivää käytössä niin ei väliä. Mutta jos aina tekee samalla tavalla oikein niin sitten ei tarvitse miettiä miten pitää tehdä jos tarvitseekin pidempiä käyntiaikoja vaan menee heti oikein.
 
No niin, tässä söhläsin taas hieman noita testejä, nyt oskilloskoopin kanssa, niin kylläpä nuo ajastukset näyttää odotetusti toimivan! Tilat vaihtuu hienosti ja PWM:n työsuhde on oikein. Huippujännite Arduinon PWM-pinniltä vaan on nyt jostain syystä n. 2,7 volttia, johtunee siitä, kun Arduino kortti on pohjakortilla, mutta pohjakortti ei saa mistään virtaan. En olisi tästä nyt hirveän huolissaan. Kunhan saadaan kaikki osat kotiin, niin voidaan testata ihan 12 voltin virtalähteellä koko hässäkkää.

Tässäpä kuvia nyt hieman jälkijättöisesti projektista. 👀

1. Testikokoonpano laturilla ja moottorilla. Tämä jää pois, kun on päätetty hankkia kuitenkin vaaka-akselimoottori. Uusiokäytetään tosta ainakin laturin kiinnitysvarsi ja perusrunko. Vaakamoottorilla systeemin saa merkittävästi matalammaksi.
DSC_1323.jpg
DSC_1324.jpg
DSC_1325.jpg
DSC_1326.jpg



2. Pohjakortin valmistelua
DSC_1332.jpg
DSC_1333.jpg
DSC_1334.jpg
 
Joo, tässäpä taas vähän päivitystä koodiin. Siinä olisi nyt sähköstarttaus ja RPM-mittaus mukana. Tuon starttausfunktion sain kohtuudella testattua, että se meni 5. yritykseen asti, jolloin siirrytään moottorin tilaan 5 (virhe).

Tärkeä toiminto tuostakin vielä puuttuu, eli se on kyllä ajastimeen määritelty, mutta pitää vielä lisätä siihen, eli tauko starttausyrityksen jälkeen.

Nyt koodi ei kuitenkaan enää toimi oikein, kun kokeilin saada tuota odotusaika timeria tuohon. Se jää startTry == 0, eli ei etene ekasta yrityksestä. Eli joku ongelma tuossa varmastikin on nyt logiikassa ehkä niin, että ajastin ei pääse käynnistymään (reset loopilla) tai muuta vastaavaa. Saa tutkia jos kiinnostaa!

BTW voisiko olla tämä kohta:

C++:
 } else if (startTry == 0) {  // If attempt not done yet
        crankWait.stop();          // stop wait timer

Tuo on kyllä minun maallikkoymmärryksen mukaan toisen funktion sisällä, mutta toimiiko kuitenkin niin, että estää ajastimen käynnistymisen?

BTW koodissa on jotkut kommentit väärin, koska jonkin verran on testiä varten muutoksia, esim. voltageWait timer, on lyhennetty 2 sekuntiin.


C++:
#define DEBUG  // Serial debug, comment to disable

#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <neotimer.h>  // 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(10000);    // 10 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 cranking
Neotimer crankWait = Neotimer(5000);    // 5 second timer, if crack failed, wait 5 seconds before a new try
Neotimer voltageWait = Neotimer(2000);  // 60 seconds timer, how long to wait with assigned conditions, before the starting function executes

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
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
float sensVoltage;                   // Store sensed voltage
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, 77 = ~30 %
int currentPwmStep = 0;

// Motor state, starting and shutting down
int motorStatus;            // 0=stopped, waiting for low voltage 1=not running, deary for cranking, 2=started, 3=ramping up pwm, 4=running, 5=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
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
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
  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
    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
  Serial.println("Setup ok.");
#endif
}


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

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

#ifdef DEBUG
    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 = 1;
      voltageWait.reset();  // Reset timer
    }

  } else if (motorStatus == 0) {
    voltageWait.stop();  // Else, stop timer
    voltageWait.reset();
  }
}


void Crank1() {  // Engine start first attempt

  if (motorStatus == 1 && startTry == 0 && rpm == 0) {  // If conditions are met, start executing

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

      if (!crankTime.started()) {  // Start the crankTime timer only, if it has not been started yet
        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, HIGH);  // Switch starter relay off
        crankTime.reset();               // Reset timer

        // Waiting time between starting attempts
        if (!crankWait.started()) {
          crankWait.start();
        }

        if (crankWait.done()) {  // If waiting time full
          startTry = 1;          // attempt done
          crankWait.reset();     // Reset timer
        }

      } else if (startTry == 0) {  // If attempt not done yet
        crankWait.stop();          // stop wait timer
              
      }

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

void Crank2() {  // Engine start second attempt

  if (motorStatus == 1 && startTry == 1 && rpm == 0) {  // If conditions are met, start executing

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

      if (!crankTime.started()) {  // Start the crankTime timer only, if it has not been started yet
        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, HIGH);  // Switch starter relay off
        crankTime.reset();               // Reset timer

        // Waiting time between starting attempts
        if (!crankWait.started()) {
          crankWait.start();
        }

        if (crankWait.done()) {  // If waiting time full
          startTry = 2;          // attempt done
          crankWait.reset();     // Reset timer
        }

      } else if (startTry == 1) {  // If attempt not done yet
        crankWait.stop();          // stop wait timer
      
      }

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


void Crank3() {  // Engine start third attempt

  if (motorStatus == 1 && startTry == 2 && rpm == 0) {  // If conditions are met, start executing

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

      if (!crankTime.started()) {  // Start the crankTime timer only, if it has not been started yet
        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, HIGH);  // Switch starter relay off
        crankTime.reset();               // Reset timer

        // Waiting time between starting attempts
        if (!crankWait.started()) {
          crankWait.start();
        }

        if (crankWait.done()) {  // If waiting time full
          startTry = 3;          // attempt done
          crankWait.reset();     // Reset timer
        }

      } else if (startTry == 2) {  // If attempt not done yet
        crankWait.stop();          // stop wait timer
      
      }

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

void Crank4() {  // Engine start fourth attempt

  if (motorStatus == 1 && startTry == 3 && rpm == 0) {  // If conditions are met, start executing

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

      if (!crankTime.started()) {  // Start the crankTime timer only, if it has not been started yet
        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, HIGH);  // Switch starter relay off
        crankTime.reset();               // Reset timer

        // Waiting time between starting attempts
        if (!crankWait.started()) {
          crankWait.start();
        }

        if (crankWait.done()) {  // If waiting time full
          startTry = 4;          // attempt done
          crankWait.reset();     // Reset timer
        }

      } else if (startTry == 3) {  // If attempt not done yet
        crankWait.stop();          // stop wait timer
      
      }

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

void Crank5() {  // Engine start fifth attempt

  if (motorStatus == 1 && startTry == 4 && rpm == 0) {  // If conditions are met, start executing

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

      if (!crankTime.started()) {  // Start the crankTime timer only, if it has not been started yet
        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, HIGH);  // Switch starter relay off
        motorStatus = 5;                 // Fault state
        crankTime.reset();               // Reset timer

        // Waiting time between starting attempts
        if (!crankWait.started()) {
          crankWait.start();
        }

        if (crankWait.done()) {  // If waiting time full
          startTry = 5;          // attempt done
          crankWait.reset();     // Reset timer
        }

      } else if (startTry == 4) {  // If attempt not done yet
        crankWait.stop();          // stop wait timer
      
      }

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


void motorRunning() {  // Engine running detection

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

    motorTime.reset();  // Reset the timer

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

    if (!motorTime.started()) {  // Start the motorTime timer only, if it has not been started yet
      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 = 3;

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

  } else if (motorStatus == 0 || motorStatus == 1) {
    motorTime.stop();  // Stop the timer if motor is not running before ramping up state

#ifdef DEBUG
    Serial.println();
    Serial.println("Kaynnistys...");
#endif

    lcd.setCursor(0, 0);  // On lcd print Finnish to start the engine
    lcd.print("K");
    lcd.print((char)0xe1);
    lcd.print("ynnist");
    lcd.print((char)0xe1);
    lcd.print("   ");
    lcd.setCursor(0, 1);
    lcd.print("moottori");
  }
}


void rampUp() {  // Ramping up PWM

  //Ramp pwm up
  if (motorStatus == 3) {
    stepTime.reset();  // Reset the timer
    if (stepTime.repeat()) {
      analogWrite(pwmPin, pwmValues[currentPwmStep]);
      currentPwmStep++;

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

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

        motorStatus = 4;  // Motor running on normal charging state
      }
    }
  }
}


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 && digitalRead(motorPin) == HIGH && motorStatus == 4) {

    shutTime.reset();  // Reset the timer
    digitalWrite(relayPin, LOW);
    analogWrite(pwmPin, 15);  // Duty Cycle to 5 % before running down engine

    if (!shutTime.started()) {  // Wait for shutDelay time to engine have time to stall down
      shutTime.start();
    }

    if (shutTime.done()) {
      analogWrite(pwmPin, 255);  // Stopped Duty Cycle at 100 %, alternator electromagnet off
      lcd.clear();
      lcd.setCursor(0, 0);
      lcd.print("Sammutus");

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

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

  } else if (motorStatus == 4) {
    motorTime.stop();              // Stop the timer if motor is in state 4 (normal running)
    digitalWrite(relayPin, HIGH);  // Else, keep relay on

#ifdef DEBUG
    Serial.println("Kaynnissa.");
#endif
  }
}


void showCurrent() {  // Show current and voltage on lcd only when normal running state

  if (motorStatus == 4) {

    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
    Serial.println("Virta LCD.");
#endif
  }
}


void loop() {
  // Calculate current from shunt 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(5);  //Wait for 5 milliseconds 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 new linear equation


  // 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(5);  // Wait for 5 milliseconds 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
  }

  motorCrankState();
  Crank1();
  Crank2();
  Crank3();
  Crank4();
  Crank5();
  motorRunning();
  rampUp();
  shutDown();
  showCurrent();

  //Debug
#ifdef DEBUG
  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();
#endif
}
 
Joo, tässäpä taas vähän päivitystä koodiin. Siinä olisi nyt sähköstarttaus ja RPM-mittaus mukana. Tuon starttausfunktion sain kohtuudella testattua, että se meni 5. yritykseen asti, jolloin siirrytään moottorin tilaan 5 (virhe).

Tärkeä toiminto tuostakin vielä puuttuu, eli se on kyllä ajastimeen määritelty, mutta pitää vielä lisätä siihen, eli tauko starttausyrityksen jälkeen.

Nyt koodi ei kuitenkaan enää toimi oikein, kun kokeilin saada tuota odotusaika timeria tuohon. Se jää startTry == 0, eli ei etene ekasta yrityksestä. Eli joku ongelma tuossa varmastikin on nyt logiikassa ehkä niin, että ajastin ei pääse käynnistymään (reset loopilla) tai muuta vastaavaa. Saa tutkia jos kiinnostaa!

BTW voisiko olla tämä kohta:

C++:
 } else if (startTry == 0) {  // If attempt not done yet
        crankWait.stop();          // stop wait timer

Tuo on kyllä minun maallikkoymmärryksen mukaan toisen funktion sisällä, mutta toimiiko kuitenkin niin, että estää ajastimen käynnistymisen?

crankWait on määritetty koodin alussa ja se on globaali muuttuja, on siis olemassa kaikkialla ja siten voi vaikuttaa.

Ihan ensin kannattaa viimeistään tässä vaiheessa muuttaa nuo motorStatus- arvot #define teksteiksi koska tiloja muistettavaksi tulee lisää.

Et tarvitse 5 yritykselle viittä funktiota. Yksi riittää koska kaikki muuttujat ovat globaaleja. Pitää vain kasvattaa yritysten lukumäärää ja lopettaa kun raja tulee vastaan. Vähemmän samanlaista koodia tehtäväksi ja testattavaksi.

Ja ehkä mieluummin niin että tiloja on enemmän, se ei maksa mitään koska muuttuja on jo olemassa. Näin saat helpommin hoidettua tilat
"ei käy" -> "odotus kun matala jännite havaittiin" -> "käynnistys" -> "odotus jos käynnistys ei onnistunut" sekä "käy" ja "virhe".

- Käynnistä ajastin vain silloin kun tiedät että on tietty tilan vaihto seuraavaan.
- Sammuta edellinen ajastin aina kun sen aika täyttyy
- Kirjastosta riippuen ajastimen reset() joko juuri ennen aloitusta tai lopetuksen jälkeen
- On helpompi lisätä uusi tila tarvittaessa kuin päätellä jossain pitikö nyt odottaa vai ei
- Koska startTry on globaali muuttuja, riittää että kutsuu yhtä funktiota ja tutkii muuttujan arvoa. Jos liian suuri niin tuli virhe ja vaihdetaan tila sen mukaan. Jos ei vielä liian suuri niin kasvatetaan yhdellä ja yritetään uudelleen (eli tilan vaihto sopivasti).
- Käytä sopivasti nimettyjä muuttujia tai mieluummin vakioita (#define) silloin kun arvot eivät muutu koodin suorituksen aikana, helputtaa lukemista ja sitä kautta ymmärtämistä
- Kannattaa välttää else- haarassa tekemistä, varsinkin jos ehdot eivät ole täysin yksiselitteiset
- Tunnista se ehto jonka seurauksena pitää vaihtaa tilasta toiseen. Ja juuri siinä kohdassa tehdään ne asiat jotka pitää tehdä vain kerran (ajastimen sammutus/aloitus, tulostus, jne)

En testannut mitenkään, mutta ehkä tähän tyyliin (huom: switch-case toimii kuten if () else if () else if () jne), se on vaan omasta mielestäni näppärämpi kirjoittaa. Ja tässä motorState- numeroarvoilla ei oikeasti ole merkitystä koska käytetään loogisia nimiä. Järjestyksellä ei siis myöskään ole merkitystä.

C++:
#define MOTOR_OFF                0
#define MOTOR_RUNNING            1
#define MOTOR_STARTING           2
#define MOTOR_START_WAIT         3
#define MOTOR_START_RETRY_WAIT   4
#define MOTOR_START_FAILED       5

#define MOTOR_RUNNING_MIN_RPM    300
#define START_VOLTAGE            11.9


void motorCrank() {

    switch (motorStatus ) {

    case MOTOR_OFF:
        // Ei käynnissä -> käynnistetään jos jännite tarpeeksi matala
        if ( sensVoltage < START_VOLTAGE ) {
            motorStatus = MOTOR_START_WAIT;   // Seuraava tila -> odotetaan ennen käynnistystä
            voltageWait.reset();    // Ajastuksen aloitus
            voltageWait.start();
            Serial.println("Jännite matala -> odotus ja käynnistys");
        } // else: ei käynnissä ja jännite riittävä
        break;

    case MOTOR_START_WAIT:
        // Käynnistysviive matalan jännitteen havaitsemisen jälkeen
        if ( voltageWait.done() ) {  // Odotusaika meni -> käynnistetään
            voltageWait.stop();      // Ajastin seis
            voltageWait.reset();
            motorStatus = MOTOR_STARTING;   // Seuraava tila -> startti
            digitalWrite(starterPin, LOW);  // Startti päälle
            crankTime.reset();       // Ajastuksen aloitus
            crankTime.start();
            Serial.println("Jännite matala -> odotus ohi -> käynnistys");
        } // else: odotetaan että aika kuluu ennen starttia
        break;

    case MOTOR_STARTING:
        // Käynnistys menossa
        if ( rpm > MOTOR_RUNNING_MIN_RPM ) {
            // Käynnistyi ennen ajastinta -> startti pois ja ok
            digitalWrite(starterPin, HIGH);   // Startti pois
            crankTime.stop();       // Ajastuksen lopetus
            crankTime.reset();
            motorStatus = MOTOR_RUNNING;   // Seuraava tila -> käy
            Serial.println("Käynnistetty");
        } else if ( crankTime.done() ) {
            // Ei käynnistynyt ennen ajastuksen loppua -> odotus ja uusi yritys
            digitalWrite(starterPin, HIGH);        // Startti pois
            motorStatus = MOTOR_START_RETRY_WAIT;  // Seuraava tila: odotetaan yritysten välissä
            crankTime.stop();       // Ajastuksen lopetus
            crankTime.reset();
            crankWait.reset();       // Ajastuksen aloitus
            crankWait.start();
            Serial.println("Käynnistys ei onnistunut -> odotus ja uusi yritys");
        } // else: odotellaan että aika kuluu
        break;

    case MOTOR_START_RETRY_WAIT:
        // Käynnistysyrityksen jälkeinen odotus menossa
        if ( crankWait.done() ) {
            // Odotus meni, yritetään käynnistää eli palataan alkuun
            motorStatus = MOTOR_OFF;  // Seuraava tila -> sammutettu, ei käynnistynyt niin jännite- ehto aloittaa seuraavan yrityksen
            startTry += 1;            // Yritysten lukumäärä +1
            Serial.print("Käynnistys ei onnistunut -> odotus meni -> yritys "); Serial.println(startTry);
            crankWait.stop();         // Ajastuksen lopetus
            crankWait.reset();
            if ( startTry > START_COUNT_LIMIT ) {
                // Jos yrityksiä on enemmän kuin sallitaan, vaihdetaan tilaksi virhe
                Serial.println("Käynnistys epäonnistui -> jäädään virheeseen");
                motorStatus = MOTOR_START_FAILED;
            }
        } // else: odotellaan että aika kuluu

        break;

    case MOTOR_RUNNING:
        // Jos pitää tehdä jotain käydessä, joka loop() kierroksella
        break;


    case MOTOR_START_FAILED:
        // Jos pitää tehdä jotain virhetilassa, joka loop() kierroksella
        break;

    }

}
 

Statistiikka

Viestiketjuista
265 091
Viestejä
4 589 392
Jäsenet
75 563
Uusin jäsen
KrisuHoo

Hinta.fi

Back
Ylös Bottom