#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();
}