From 9904aca97a2d84c7ba794fb1c562fbe8442a1521 Mon Sep 17 00:00:00 2001 From: sieja Date: Sun, 18 May 2025 22:31:22 +0200 Subject: [PATCH] Uproszczenie kodu --- .../ESP_AutomatedGearShifter.ino | 201 +++++++----------- .../{nowy 2.txt => Portowanie pinow.txt} | 2 + .../PortowaniePinow.xlsx | Bin 0 -> 8654 bytes 3 files changed, 82 insertions(+), 121 deletions(-) rename ESP32/ESP_AutomatedGearShifter/{nowy 2.txt => Portowanie pinow.txt} (99%) create mode 100644 ESP32/ESP_AutomatedGearShifter/PortowaniePinow.xlsx diff --git a/ESP32/ESP_AutomatedGearShifter/ESP_AutomatedGearShifter.ino b/ESP32/ESP_AutomatedGearShifter/ESP_AutomatedGearShifter.ino index d011a60..ea4719f 100644 --- a/ESP32/ESP_AutomatedGearShifter/ESP_AutomatedGearShifter.ino +++ b/ESP32/ESP_AutomatedGearShifter/ESP_AutomatedGearShifter.ino @@ -1,14 +1,16 @@ // #include -// #include // model servo: DS3218 PRO #include // model servo: DS3218 PRO +// nie włącza serovo +// nie podaje napięcia z baterii + #include #include #include #include #include -#define Version "1.15.8" +#define Version "2.0.1" ////2DO: //diagnostyka i/lub przeciwdziałanie skokom predkosci //dlaczego wskaznik odnosi sie do poprawnego biegu a w tym czasie bieg jest zly? bo czas ponizej 2s? @@ -23,13 +25,15 @@ #define SCREEN_ADDRESS 0x3C ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32 Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); // #define PinInterrupt 0 //on board: RX deklaracja pod stabilizacje TX -#define PinInSpeed 4 //on board: D4 -#define ServoSwitch 2 //on board: 4 -#define ServoPin 19 //on board: 8 -#define BrakingLight 13 //on board: 9 BrakingLight -#define PinLED 25 //on board: 10 ORANGE loop signal -#define Btn1 27 //on board: A0 Button1 -#define VoltInptPin 23 //on board: D23 Battery Voltage +#define PinInSpeed 34 //on board: D34 +#define ServoSwitch 15 //on board: D15 +#define ServoPin 19 //on board: D19 +#define BrakingLight 13 //on board: D13 BrakingLight +#define PinLED 25 //on board: D25 ORANGE loop signal +#define Btn1 27 //on board: D27 Button1 +#define Btn2 33 //on board: D33 Button2 + +// #define VoltInptPin 23 //on board: D23 Battery Voltage #define ServoMaxAngle 130 #define MaxAngle 179 @@ -47,12 +51,20 @@ Servo myservo; //SPEED -double speed = 0; -double speed_last = 0.0; -double speed_last_2 = 0.0; -double speed_last_3 = 0.0; +double readSignalTime_4; +double readSignalTime_3; +double readSignalTime_2; +double readSignalTime_1; +double readSignalTimeAvg; +double raw_speed; +double calcSpeedMain; +double calcSpeed3; +double calcSpeed2; +double calcSpeed1; +double calcSpeedAvg; + int speedTrend = 0; -double sigleTimeSpd = 0.0; +double readSignalTime = 0.0; double sleepSpd = 0.0; unsigned long millissSpd = millis(); unsigned long lastMillisSpd = millis(); @@ -147,14 +159,16 @@ void setup() { // pinMode(PinInterrupt, OUTPUT); // digitalWrite(PinInterrupt, HIGH); pinMode(Btn1, INPUT); - pinMode(VoltInptPin, INPUT); + pinMode(Btn2, INPUT); + + // pinMode(VoltInptPin, INPUT); //OUTPUT pinMode(PinLED, OUTPUT); pinMode(BrakingLight, OUTPUT); pinMode(ServoSwitch, OUTPUT); //Interrupts - attachInterrupt(digitalPinToInterrupt(PinInSpeed), calcSpeed, FALLING); + attachInterrupt(digitalPinToInterrupt(PinInSpeed), readSpeed, FALLING); display.clearDisplay(); delay(550); setPosition(7); @@ -176,10 +190,12 @@ void setup() { display.setCursor(75, 45); display.println(ServoMaxAngle); display.display(); + digitalWrite(ServoSwitch, HIGH); for (servoCurrPos = myservo.read(); servoCurrPos <= 180; servoCurrPos++) { myservo.write(servoCurrPos); delay(15); } + //MEMORY // eeprom_read_block(&totalDistReaded, totalDistMemLocation, 2); // totalDist = float(totalDistReaded); @@ -205,16 +221,14 @@ void loop() { //################################################ //SPEED - sleepSpd = millis() - lastMillisSpd; - if (sleepSpd >= 1500) { //podaj zerową prędkość jeśli nie było odcztu od 1,5 s + sleepSpd = millis() - millissSpd; + if (sleepSpd >= 1000.0) { //podaj zerową prędkość jeśli nie było odcztu od 1,5 s speed4Gear = 0.0; speed4Gear_2 = 0.0; - speed = 0.0; - speed_last = 0.0; - speed_last_2 = 0.0; - speed_last_3 = 0.0; + raw_speed = 0.0; + //przejście w tryb uśpienia za pomocą odpowiedniego ustawienia koła i magnesu - if ((digitalRead(PinInSpeed) == LOW)|| (sleepSpd >= LongTimeToSleep)) { + if ((digitalRead(Btn2) == HIGH)|| (sleepSpd >= LongTimeToSleep)) { display.fillCircle(75, 10, 10, SSD1306_WHITE); if (sleepSpd >= TimeToSleep) { prepareTurnOff(); @@ -225,10 +239,12 @@ void loop() { if (speed4Gear > 40 || isinf(speed4Gear)) { speed4Gear = speed4Gear_3; } + calcSpeed(); calcGear(); displGear = 9 - currentGear; setPosition(currentGear); + //duzy font //GEAR display.setCursor(8, 0); @@ -250,7 +266,7 @@ void loop() { display.setCursor(8, 40); display.println("S:"); display.setCursor(40, 40); - display.println(speed, 1); + display.println(calcSpeedMain, 1); //GearBar currentGearRangeMiddle = (currentGearRangeLower + currentGearRangeUpper) / 2; display.fillRect(115, 32, 7, 1, SSD1306_WHITE); @@ -266,9 +282,9 @@ void loop() { //VOLT_BAR - adcBattVoltValue = analogRead(VoltInptPin); - measuredVoltage = (adcBattVoltValue * referenceVoltage) / maxADCValue; - inputVoltage = measuredVoltage * voltageDividerRatio; + // adcBattVoltValue = analogRead(VoltInptPin); + // measuredVoltage = (adcBattVoltValue * referenceVoltage) / maxADCValue; + // inputVoltage = measuredVoltage * voltageDividerRatio; voltBarHeight = int(((inputVoltage-9)/3)*64); //odjemowanie 9 bo to minimalne napiecie, podział przez 3 bo zakladam max napiecie 12.0V a nie 12.6V voltBarPosition = 64 - voltBarHeight; @@ -277,8 +293,14 @@ void loop() { display.display(); loopTime = millis(); + if ((digitalRead(Btn2) == HIGH)) { + digitalWrite(PinLED, HIGH); + }else { + digitalWrite(PinLED, LOW); + } + //wstrzymanie pętli by odczyty były co 0,5s - for (; (millis() - loopTime) < 100 ;) { + for (; (millis() - loopTime) < 300 ;) { delay(10); } @@ -307,134 +329,71 @@ void loop() { } //########################################### LOOP ############################################################ //########################################### LOOP ############################################################ -void calcSpeed() { +void readSpeed() { lastLastMillisSpd = lastMillisSpd; lastMillisSpd = millissSpd; millissSpd = millis(); - sigleTimeSpd = double(millissSpd - lastLastMillisSpd) / 1000; -// speed_last_3 = speed_last_2; -// speed_last_2 = speed_last; -// speed_last = speed; - speed = (((2 * Pi) / sigleTimeSpd * ((WheelCircumference) / (Pi)) * ms2kmh)) / MagnetsCnt; - // diag -// if (((speed_last_3 * 1.5) > speed) && speed > 10.0 && speedTrend > 0) { //zabezpieczenie przed losowymi sygnałami magesu -// speed = speed_last_3; -// } -// speed = (speed + speed_last)/2; - totalDist = totalDist + (1/MagnetsCnt); + readSignalTime_3 = readSignalTime_2; + readSignalTime_2 = readSignalTime_1; + readSignalTime_1 = double(millissSpd - lastLastMillisSpd) / 1000; + readSignalTimeAvg = (readSignalTime_1 + readSignalTime_2 + readSignalTime_3 )/3; + + raw_speed = (((2 * Pi) / readSignalTimeAvg * ((WheelCircumference) / (Pi)) * ms2kmh)) / MagnetsCnt; + } +void calcSpeed(){ + calcSpeed3 = calcSpeed2; + calcSpeed2 = calcSpeed1; + calcSpeed1 = raw_speed; + + calcSpeedAvg = (calcSpeed1 + calcSpeed2 + calcSpeed3)/3; + calcSpeedMain = calcSpeedAvg; +} void calcGear() { - //speed validation - speedDiff_3 = speed4Gear_3-speed4Gear_2; - speedDiff_2 = speed4Gear_2-speed4Gear_1; - speed4Gear_3 = speed4Gear_2; - speed4Gear_2 = speed4Gear_1; - speed4Gear_1 = speed; - speed4Geat_estimated = (((speedDiff_3 * avgWeight_3 + speedDiff_2 * avgWeight_2)/(avgWeight_3 + avgWeight_2))+ speed4Gear_1) + 4; - if (speed <= 10.0 or speed <= speed4Geat_estimated) { - speed4Gear_2 = speed4Gear; - speed4Gear = speed4Gear_1; - } - - -//poniżej zamienić speed na speed4Gear -//speed_last_3 na speed4Gear_2 - - - - - if ((speed4Gear / speed4Gear_2) >= 1.5) { // przyspieszenie DO weryfikacji czy nie trzeba zamienic na czas lub zwiększyc wartość - speedTrend = 1; - } else if ((speed4Gear - speed4Gear_2) <= -1.5) { - speedTrend = -1; - } else { - speedTrend = 0; - } - - // if (speedTrend > 0){ // wymusza wczerśniejszą zmianę biegów gdy wykryto przyspieszanie - // accelerationShift = 1.05; - // }else { accelerationShift = 1; - // } - if (speed4Gear * accelerationShift >= 0 && speed4Gear * accelerationShift < spdRange1and2) { + + if (calcSpeedMain >= 0 && calcSpeedMain < spdRange1and2) { calculatedGear = 1; currentGearRangeLower = 2.5; currentGearRangeUpper = spdRange1and2; - } else if (speed4Gear * accelerationShift >= spdRange1and2 && speed4Gear * accelerationShift < spdRange2and3) { + } else if (calcSpeedMain >= spdRange1and2 && calcSpeedMain < spdRange2and3) { calculatedGear = 2; currentGearRangeLower = spdRange1and2; currentGearRangeUpper = spdRange2and3; - } else if (speed4Gear * accelerationShift >= spdRange2and3 && speed4Gear * accelerationShift < spdRange3and4) { + } else if (calcSpeedMain >= spdRange2and3 && calcSpeedMain < spdRange3and4) { calculatedGear = 3; currentGearRangeLower = spdRange2and3; currentGearRangeUpper = spdRange3and4; - } else if (speed4Gear * accelerationShift >= spdRange3and4 && speed4Gear * accelerationShift < spdRange4and5) { + } else if (calcSpeedMain >= spdRange3and4 && calcSpeedMain < spdRange4and5) { calculatedGear = 4; currentGearRangeLower = spdRange3and4; currentGearRangeUpper = spdRange4and5; - } else if (speed4Gear * accelerationShift >= spdRange4and5 && speed4Gear * accelerationShift < spdRange5and6) { + } else if (calcSpeedMain >= spdRange4and5 && calcSpeedMain < spdRange5and6) { calculatedGear = 5; currentGearRangeLower = spdRange4and5; currentGearRangeUpper = spdRange5and6; - } else if (speed4Gear * accelerationShift >= spdRange5and6 && speed4Gear * accelerationShift < spdRange6and7) { + } else if (calcSpeedMain >= spdRange5and6 && calcSpeedMain < spdRange6and7) { calculatedGear = 6; currentGearRangeLower = spdRange5and6; currentGearRangeUpper = spdRange6and7; - } else if (speed4Gear * accelerationShift >= spdRange6and7 && speed4Gear * accelerationShift < spdRange7and8) { + } else if (calcSpeedMain >= spdRange6and7 && calcSpeedMain < spdRange7and8) { calculatedGear = 7; currentGearRangeLower = spdRange6and7; currentGearRangeUpper = spdRange7and8; - } else if (speed4Gear * accelerationShift >= spdRange7and8) { + } else if (calcSpeedMain >= spdRange7and8) { calculatedGear = 8; currentGearRangeLower = spdRange7and8; currentGearRangeUpper = 60.0; } else { calculatedGear = 8; //Default }; + calcTimeDiff = millis() - lastGearCalc; + // previousGear = currentGear; + currentGear = calculatedGear; + lastGearCalc = millis(); - if (calculatedGear == currentGear) { - speedForBar = speed4Gear * accelerationShift; - } - if ((calculatedGear + 1) < currentGear || (calculatedGear - 1) > currentGear || calcTimeDiff >= changeDelayMs || speedTrend > 0 ) { - //zmień bieg tylko, gdy rożnica między biegiem wyliczonym a obecnym jest większa niż jeden lub gdy od zmieny biegu minely 3 sec - if (currentGear > calculatedGear) { - downs = downs + 1; - } - if (currentGear < calculatedGear) { - ups = ups + 1; - } - currentGear = calculatedGear; - previousGear = currentGear; - lastGearCalc = millis(); - speedForBar = speed4Gear * accelerationShift; - - } - if (speedTrend >= 1 and calculatedGear < 8) { - if (currentGear > calculatedGear) { - downs = downs + 1; - } - if (currentGear < calculatedGear) { - ups = ups + 1; - } - currentGear = calculatedGear; - previousGear = currentGear; - lastGearCalc = millis(); - speedForBar = speed4Gear * accelerationShift; - - } else if (speedTrend <= -1 and calculatedGear > 1) { - if (currentGear > calculatedGear) { - downs = downs + 1; - } - if (currentGear < calculatedGear) { - ups = ups + 1; - } - previousGear = currentGear; - currentGear = calculatedGear; - lastGearCalc = millis(); - speedForBar = speed4Gear * accelerationShift; - }; } void setPosition(int currentGear) { @@ -446,7 +405,7 @@ void setPosition(int currentGear) { if (pos <= 0) { pos = MinAngle; } - if (sleepMode == 1 && speed > 0.0) { + if (sleepMode == 1 && calcSpeedMain > 0.0) { digitalWrite(ServoSwitch, HIGH); for (servoCurrPos = myservo.read(); servoCurrPos <= 180; servoCurrPos++) { myservo.write(servoCurrPos); diff --git a/ESP32/ESP_AutomatedGearShifter/nowy 2.txt b/ESP32/ESP_AutomatedGearShifter/Portowanie pinow.txt similarity index 99% rename from ESP32/ESP_AutomatedGearShifter/nowy 2.txt rename to ESP32/ESP_AutomatedGearShifter/Portowanie pinow.txt index e49bd95..3fabb65 100644 --- a/ESP32/ESP_AutomatedGearShifter/nowy 2.txt +++ b/ESP32/ESP_AutomatedGearShifter/Portowanie pinow.txt @@ -1,7 +1,9 @@ czeck plytki HAT PinLED ARDUINO ESP32 + SCL 3 BRAK docelowy: D22 ServoSwitch 4 BRAK docelowy: D2 + VCC OK OK GND OK OK SLA 2 D21 diff --git a/ESP32/ESP_AutomatedGearShifter/PortowaniePinow.xlsx b/ESP32/ESP_AutomatedGearShifter/PortowaniePinow.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..46ff8b873b8100851beb616f00294577405c7845 GIT binary patch literal 8654 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