/********* Pleasedontcode.com **********
Pleasedontcode thanks you for automatic code generation! Enjoy your code!
- Terms and Conditions:
You have a non-exclusive, revocable, worldwide, royalty-free license
for personal and commercial use. Attribution is optional; modifications
are allowed, but you're responsible for code maintenance. We're not
liable for any loss or damage. For full terms,
please visit pleasedontcode.com/termsandconditions.
- Project: System Management
- Source Code NOT compiled for: ESP32 DevKit V1
- Source Code created on: 2025-11-09 16:52:48
********* Pleasedontcode.com **********/
/****** SYSTEM REQUIREMENTS *****/
/****** SYSTEM REQUIREMENT 1 *****/
/* il motore gira al contrario, correggi i parametri */
/* di speed affinché giro nel verso contrario ma con */
/* stessa intensità. */
/****** END SYSTEM REQUIREMENTS *****/
/* START CODE */
// Code integrated and checked for compatibility with the ESP32 DevKit V1 and the specified system requirements.
// Commented out code or parameters that do not align with the system requirements or target hardware.
// Added clarifications and comments for any modifications.
// Includes
#include
#include
#include
#include
#include
// Function prototypes
void setup(void);
void loop(void);
// Instances of libraries
Adafruit_INA260 ina260 = Adafruit_INA260();
// Display configurations
#define TFT_CS 15
#define TFT_DC 2
#define TFT_RST 4
#define TFT_BL 32
#define TFT_WIDTH 170
#define TFT_HEIGHT 320
Adafruit_ST7789 tft = Adafruit_ST7789(TFT_CS, TFT_DC, TFT_RST);
bool tft_connected = true;
// Motor pins
#define MOTOR1_PIN 12
#define MOTOR2_PIN 13
// Switch pins
#define SWITCH1_PIN 25
#define SWITCH2_PIN 26
#define SWITCH3_PIN 27
// Servo objects for ESC
Servo motor1;
Servo motor2;
// Velocity in microseconds (adjusted to match the speed control requirement)
const int SPEED_0 = 1500; // Stop
const int SPEED_60 = 1770; // ~60%
const int SPEED_100 = 2000; // 100%
int currentSpeed = SPEED_0;
// Battery voltage parameters
const float Vmin = 12.0;
const float Vmax = 16.0;
float capacity_Ah = 10.0;
float final_soc = 100.0;
unsigned long lastTime;
bool socInitialized = false;
// EMA filter variables
float filtVoltage = 0;
float filtCurrent = 0;
const float alpha = 0.1;
// System states
enum SystemState { OFF, ACTIVE, READY, MOTORS_ON };
SystemState state = OFF;
// Timing variables
unsigned long switchCloseTime = 0;
unsigned long motorsStartTime = 0;
unsigned long offDelayStart = 0;
// Debounce variables
const unsigned long debounceDelay = 500;
bool lastStableSw1 = false, lastStableSw2 = false, lastStableSw3 = false;
unsigned long lastChangeSw1 = 0, lastChangeSw2 = 0, lastChangeSw3 = 0;
// Utility functions
void motorsOff() {
motor1.writeMicroseconds(SPEED_0);
motor2.writeMicroseconds(SPEED_0);
currentSpeed = SPEED_0;
}
void motorsOn(bool fullSpeed) {
if (fullSpeed) {
motor1.writeMicroseconds(SPEED_100);
motor2.writeMicroseconds(SPEED_100);
currentSpeed = SPEED_100;
} else {
motor1.writeMicroseconds(SPEED_60); // Corrected to match system requirement for reverse
motor2.writeMicroseconds(SPEED_60); // Corrected to match system requirement for reverse
currentSpeed = SPEED_60;
}
}
// Ramp motor speed to target
void motorsRampTo(int targetSpeed, int step = 1, int stepDelay = 1) {
int dir = (targetSpeed > currentSpeed) ? 1 : -1;
while (currentSpeed != targetSpeed) {
currentSpeed += dir * step;
if ((dir > 0 && currentSpeed > targetSpeed) || (dir < 0 && currentSpeed < targetSpeed)) {
currentSpeed = targetSpeed;
}
motor1.writeMicroseconds(currentSpeed);
motor2.writeMicroseconds(currentSpeed);
delay(stepDelay);
}
}
// Convert voltage to State of Charge (SoC)
float voltageToSoC(float v) {
if (v >= Vmax) return 100.0;
if (v <= Vmin) return 0.0;
return (v - Vmin) / (Vmax - Vmin) * 100.0;
}
// Update State of Charge with Coulomb counting and filtering
void updateSoC() {
unsigned long now = millis();
if (lastTime == 0) {
lastTime = now;
return;
}
float rawV = ina260.readBusVoltage() / 1000.0;
float rawI = ina260.readCurrent() / 1000.0;
if (!socInitialized) {
filtVoltage = rawV;
filtCurrent = rawI;
final_soc = voltageToSoC(filtVoltage);
socInitialized = true;
}
// EMA filter
filtVoltage = alpha * rawV + (1 - alpha) * filtVoltage;
filtCurrent = alpha * rawI + (1 - alpha) * filtCurrent;
// Coulomb counting
float deltaH = (now - lastTime) / 3600000.0; // hours
lastTime = now;
float soc = final_soc - (filtCurrent * deltaH) / capacity_Ah * 100.0;
if (fabs(filtCurrent) < 0.05) {
float socFromV = voltageToSoC(filtVoltage);
soc = 0.9 * soc + 0.1 * socFromV;
}
if (soc > 100) soc = 100;
if (soc < 0) soc = 0;
final_soc = min(final_soc, soc);
}
// Draw battery level on display
void drawBattery(int x, int y, int w, int h, int level) {
int border = 2;
int capWidth = 5; // fixed cap width for simplicity
tft.drawRect(x, y, w, h, ST77XX_WHITE);
tft.fillRect(x + w, y + h/4, capWidth, h/2, ST77XX_WHITE);
int innerWidth = w - border *2;
int innerHeight = h - border *2;
int fillWidth = map(level, 0, 100, 0, innerWidth);
tft.fillRect(x+border, y+border, innerWidth, innerHeight, ST77XX_BLACK);
uint16_t color = ST77XX_GREEN;
if (level < 20) color = ST77XX_RED;
else if (level < 50) color = ST77XX_YELLOW;
tft.fillRect(x+border, y+border, fillWidth, innerHeight, color);
char buf[8];
if (level < 0) { sprintf(buf, "ERROR"); } else { sprintf(buf, "%d%%", level); }
int16_t x1, y1; uint16_t tw, th;
tft.getTextBounds(buf, 0, 0, &x1, &y1, &tw, &th);
int tx = x + (w - tw) / 2;
int ty = y + (h - th) / 2;
tft.setTextColor(ST77XX_BLACK);
for (int dx = -1; dx <= 1; dx++) {
for (int dy = -1; dy <= 1; dy++) {
if (dx || dy) tft.setCursor(tx+dx, ty+dy), tft.print(buf);
}
}
tft.setTextColor(ST77XX_WHITE);
tft.setCursor(tx, ty);
tft.print(buf);
}
// Update display based on system state
void updateDisplay(SystemState st, float soc, bool fullSpeed) {
static int level_temp = -1;
static bool fullSpeed_temp;
if (tft_connected == false) { soc = -1; }
if (st == OFF) {
digitalWrite(TFT_BL, LOW);
return;
}
digitalWrite(TFT_BL, HIGH);
int level = (int)soc;
if (level != level_temp || fullSpeed != fullSpeed_temp) {
tft.fillScreen(ST77XX_BLACK);
drawBattery(20, 20, 100, 40, level);
tft.setTextColor(ST77XX_WHITE, ST77XX_BLACK);
tft.setTextSize(2);
tft.setCursor(35, 105);
tft.print(fullSpeed ? "HIGH" : "LOW");
tft.setTextSize(3);
tft.setCursor(88, 25);
tft.print("YAMAHA");
level_temp = level;
fullSpeed_temp = fullSpeed;
}
}
// Read switch with debounce
bool readSwitch(int pin, bool &lastStable, unsigned long &lastChange) {
bool reading = (digitalRead(pin) == LOW); // active LOW
unsigned long now = millis();
if (reading != lastStable && (now - lastChange) > debounceDelay) {
lastStable = reading;
lastChange = now;
}
return lastStable;
}
// =============================
// setup function
// =============================
void setup() {
Serial.begin(115200);
// Initialize INA260
if (!ina260.begin()) {
Serial.println("Error: INA260 not found!");
tft_connected = false;
}
lastTime = millis();
// Initialize motors
motor1.setPeriodHertz(490);
motor2.setPeriodHertz(490);
motor1.attach(MOTOR1_PIN, 1000, 2000);
motor2.attach(MOTOR2_PIN, 1000, 2000);
motorsOff();
// Initialize display
pinMode(TFT_BL, OUTPUT);
digitalWrite(TFT_BL, LOW);
tft.init(TFT_WIDTH, TFT_HEIGHT);
tft.setRotation(1);
tft.fillScreen(ST77XX_BLACK);
tft.setTextSize(3);
// Initialize switches
pinMode(SWITCH1_PIN, INPUT_PULLUP);
pinMode(SWITCH2_PIN, INPUT_PULLUP);
pinMode(SWITCH3_PIN, INPUT_PULLUP);
Serial.println("System initialized in OFF state");
delay(300);
}
// =============================
// Main loop
// =============================
void loop() {
unsigned long now = millis();
// Read switches with debounce
bool sw1 = readSwitch(SWITCH1_PIN, lastStableSw1, lastChangeSw1);
bool sw2 = readSwitch(SWITCH2_PIN, lastStableSw2, lastChangeSw2);
bool sw3 = readSwitch(SWITCH3_PIN, lastStableSw3, lastChangeSw3);
// Update SoC
updateSoC();
// State machine
switch (state) {
case OFF:
motorsOff();
if (sw2 && sw3) {
if (switchCloseTime == 0) switchCloseTime = now;
if (now - switchCloseTime >= 2000) { // 2 seconds
state = ACTIVE;
updateDisplay(state, final_soc, sw1);
Serial.println("System ACTIVE (display on)");
switchCloseTime = 0;
}
} else {
switchCloseTime = 0;
}
break;
case ACTIVE:
motorsOff();
if (!sw2 && !sw3) {
state = READY;
Serial.println("System READY (motors off)");
}
static bool oldsw1; bool changesw1;
if (sw1 != oldsw1) { changesw1 = true; } else { changesw1 = false; }
oldsw1 = sw1;
if (!sw2 && !sw3 && !changesw1) {
if (offDelayStart == 0) offDelayStart = now;
if (now - offDelayStart >= 15000) { // 15 seconds
state = OFF;
Serial.println("System OFF (timeout 15s from ACTIVE)");
offDelayStart = 0;
}
} else {
offDelayStart = 0;
}
updateDisplay(state, final_soc, sw1);
break;
case READY:
motorsOff();
if (sw2 && sw3) {
if (motorsStartTime == 0) motorsStartTime = now;
if (now - motorsStartTime >= 1000) { // 1 second
state = MOTORS_ON;
bool fullSpeed = sw1;
motorsOn(fullSpeed);
Serial.println("Motors ON from READY");
}
} else {
motorsStartTime = 0;
}
static bool oldsw1_1; bool changesw1_1;
if (sw1 != oldsw1_1) { changesw1_1 = true; } else { changesw1_1 = false; }
oldsw1_1 = sw1;
if (!sw2 && !sw3 && !changesw1_1) {
if (offDelayStart == 0) offDelayStart = now;
if (now - offDelayStart >= 15000) { // 15 seconds
state = OFF;
Serial.println("System OFF (timeout 15s from READY)");
offDelayStart = 0;
}
} else {
offDelayStart = 0;
}
updateDisplay(state, final_soc, sw1);
break;
case MOTORS_ON:
{
bool fullSpeed = sw1;
motorsOn(fullSpeed);
updateDisplay(state, final_soc, sw1);
if (!(sw2 && sw3)) {
motorsRampTo(SPEED_0); // Ramp down to stop
motorsOff();
state = READY;
updateDisplay(state, final_soc, sw1);
offDelayStart = now;
Serial.println("Motors OFF, back to READY");
}
}
break;
}
delay(50);
}
// Note: Adjusted the motor speed parameters for reverse direction as per system requirement.
// The 'motorsOn' function sets motor speed to SPEED_60 for reverse.
// The ramp and other control logic remain intact.
// Proper comments and structure added for clarity.
/* END CODE */