//######################### // Marble-Clock with 2 Hall-Sensors // Hardware-Boardauswahl: Lolin32 // Board: ESP32 // created: 20.12.2023 by joergeli // look at: http://arduino.joergeli.de/marbleclock/marbleclock.php //######################### #include #include // we need wifi to get internet access #include // time() ctime() #include const int steps_per_rev = 2038; // Schritte pro Umdrehung //Stepper-motor1 PINs (hours) #define IN1 15 #define IN2 13 #define IN3 4 #define IN4 5 //Stepper-motor2 PINs (minutes) #define IN5 19 #define IN6 21 #define IN7 22 #define IN8 23 #define LED_BUILTIN 2 //ON Board LED GPIO 2 #define GPIO_Pin_std 12 // Pin fuer Microschalter1 #define GPIO_Pin_min 14 // Pin fuer Microschalter2 #define GPIO_Pin_hall1 32 // Pin fuer hall1 #define GPIO_Pin_hall2 33 // Pin fuer hall2 void ICACHE_RAM_ATTR microswitch1(); // Wichtig! Sonst stürzt MCU ab! void ICACHE_RAM_ATTR microswitch2(); // Wichtig! Sonst stürzt MCU ab! void ICACHE_RAM_ATTR hall1(); // Wichtig! Sonst stürzt MCU ab! void ICACHE_RAM_ATTR hall2(); // Wichtig! Sonst stürzt MCU ab! static unsigned long last_interrupt_time1 = 0; static unsigned long last_interrupt_time2 = 0; static unsigned long last_interrupt_time_hall1 = 0; static unsigned long last_interrupt_time_hall2 = 0; AccelStepper motor1(AccelStepper::HALF4WIRE, IN1, IN3, IN2, IN4); AccelStepper motor2(AccelStepper::HALF4WIRE, IN5, IN7, IN6, IN8); int debug = 1; // 0/1 int run_flag1=0; // for stopping and running steppermotor1 int run_flag2=0; // for stopping and running steppermotor2 int microswitch_flag1=0; // for continuos running steppermotor1 // set hours int microswitch_flag2=0; // for continuos running steppermotor2 // set minutes int hall_flag1=1; // first sequence for steppermotor1 after boot int hall_flag2=1; // first sequence for steppermotor2 after boot int count1 = 0; // Counter zählt Microswitch1-Betätigungen int count2 = 0; // Counter zählt Microswitch2-Betätigungen //######################################################################################## // set your own network parameters here !!! IPAddress ip(xxx, xxx, xxx, xxx); //### static ip-address IPAddress gateway(xxx, xxx, xxx, xxx); //### router IPAddress subnet(255, 255, 255, 0); //### subnet-mask IPAddress dns(xxx, xxx, xxx, xxx); //### dns-server = router const char* ssid = "xxxxxxxxxxxx"; //### WLAN SSID const char* pwd = "xxxxxxxxxxxx"; //### WLAN Password const char* OTA_host = "Marble-Clock"; // name of the virtual port in the Arduino-IDE //######################################################################################## // NTP-Parameter #define MY_NTP_SERVER "194.25.134.196" // = ntp1.t-online.de //Die Sommerzeitumstellung erfolgt im dritten Monat (M3), der fuenften Woche (5), am Sonntag (0) um zwei Uhr (/02). //Die Winterzeitumstellung erfolgt im 10 Monat (M10) wieder in der fuenften Woche (5) am Sonntag(0) um drei Uhr (/03). // siehe https://werner.rothschopf.net/201802_arduino_esp8266_ntp.htm : //############################################################################## #define MY_TZ "CET-1CEST,M3.5.0/02,M10.5.0/03" // timezone = Europe/Berlin //############################################################################## time_t now; // this is the epoch tm tm; // the structure tm holds time information in a more convient way //_________________________________________________________________________________________ void showTime() { time(&now); // read the current time localtime_r(&now, &tm); // update the structure tm with the current time if (debug ==1){ // Only for debugging Serial.println("__________________________________________"); Serial.print("year:"); Serial.print(tm.tm_year + 1900); // years since 1900 Serial.print(" tmonth:"); Serial.print(tm.tm_mon + 1); // January = 0 (!) Serial.print(" tday:"); Serial.print(tm.tm_mday); // day of month Serial.print(" thour:"); Serial.print(tm.tm_hour); // hours since midnight 0-23 Serial.print(" tmin:"); Serial.print(tm.tm_min); // minutes after the hour 0-59 Serial.print(" tsec:"); Serial.print(tm.tm_sec); // seconds after the minute 0-61* Serial.print(" twday:"); Serial.print(tm.tm_wday); // weekday 0-6 ( 0=sunday) } if (tm.tm_isdst == 1){ // Daylight Saving Time flag Serial.print(" - Es ist Sommerzeit -> tm.tm_isdst=1"); // daylight saving Serial.print("");} else { Serial.print(" - Es ist Winterzeit -> tm.tm_isdst=0"); // no daylight saving Serial.println(""); } // ########################################################### // tm.tm_hour = 0; // 0-23 uncomment only for debugging // tm.tm_min = 41; // 0-59 uncomment only for debugging // tm.tm_sec = 30; // 0-59 uncomment only for debugging // tm.tm_isdst = 1; // 0/1 uncomment only for debugging // ########################################################### } //end of void showTime() //__________________________________________________________________________________________ void Uhrzeit_Anzeige(){ int Stunde = tm.tm_hour; int Minute = tm.tm_min; int Sekunde = tm.tm_sec; // Sommer-/Winterzeit Daylight-Saving routine if (tm.tm_isdst == 1){Stunde = Stunde + 1;} // Sommerzeit / Daylight-Saving active else if (tm.tm_isdst == 0){Stunde = Stunde;} // Winterzeit/ No Daylight-Saving // In summertime 1 hour has already been added 2 lines above, so Stunde 23 becomes -> 24, but it must be 0: if (Stunde > 23 && tm.tm_isdst == 1){Stunde = 0;} // Sommerzeit Übergang 23 -> 0 Uhr/ Daylight-Saving Transition from 23 to 0 (not to 24!) Serial.println(); Serial.print("Aktuelle Uhrzeit: "); Serial.print(Stunde); Serial.print(":"); Serial.print(Minute); Serial.print(":"); Serial.println(Sekunde); Serial.println("__________________________________________"); } // end of Uhrzeit_Anzeige() //_____________________________________________________________________________________ // subroutine for Wifi-connection void setup_wifi(){ WiFi.config(ip, gateway, subnet, dns); delay(100); WiFi.mode(WIFI_STA); WiFi.begin(ssid, pwd); Serial.println(""); Serial.println(""); Serial.println("___________________________________________________"); Serial.print("Connecting to "); Serial.println(ssid); while (WiFi.status() != WL_CONNECTED) { Serial.print("."); delay(300); } // end of while Serial.println(""); Serial.print(" OK ---> "); Serial.print("IP: "); Serial.println(WiFi.localIP()); } // end of void connectWIFI //_________________________________________________________________________________________ void setup() { Serial.begin(115200); delay(100); ArduinoOTA.setHostname("Marble-Clock"); Serial.println("Start ..."); pinMode(LED_BUILTIN, OUTPUT); pinMode(GPIO_Pin_hall1, INPUT); pinMode(GPIO_Pin_hall2, INPUT); setup_wifi(); delay (100); configTime(0, 0, MY_NTP_SERVER); // 0, 0 because we will use TZ in the next line setenv("TZ", MY_TZ, 1); // Set environment variable with your time zone tzset(); delay (1000); time(&now); // read the current time localtime_r(&now, &tm); delay (100); showTime(); Uhrzeit_Anzeige(); // Restart ESP if NTP-Time could not received if(tm.tm_hour == 1 && tm.tm_min == 0 && (tm.tm_sec == 1 || tm.tm_sec == 2 || tm.tm_sec == 3)){ Serial.println("Zeit nicht empfangen -> Reboot ..."); ESP.restart(); } attachInterrupt(GPIO_Pin_std, microswitch1, RISING); //interrupt mit microswitch1 attachInterrupt(GPIO_Pin_min, microswitch2, RISING); //interrupt mit microswitch2 attachInterrupt(GPIO_Pin_hall1, hall1, RISING); //interrupt mit hall1 attachInterrupt(GPIO_Pin_hall2, hall2, RISING); //interrupt mit hall2 for ( int i=0; i < 9; i++){ digitalWrite(LED_BUILTIN, HIGH); delay(50); digitalWrite(LED_BUILTIN, LOW); delay(50); } delay(800); motor1.setMaxSpeed(1200); motor1.setAcceleration(900); motor1.moveTo(steps_per_rev); motor2.setMaxSpeed(1200); motor2.setAcceleration(900); motor2.moveTo(steps_per_rev); //---------------------------- ArduinoOTA .onStart([]() { String type; if (ArduinoOTA.getCommand() == U_FLASH) type = "sketch"; else // U_SPIFFS type = "filesystem"; // NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end() Serial.println("Start updating " + type); }) .onEnd([]() { Serial.println("\nEnd"); }) .onProgress([](unsigned int progress, unsigned int total) { Serial.printf("Progress: %u%%\r", (progress / (total / 100))); }) .onError([](ota_error_t error) { Serial.printf("Error[%u]: ", error); if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed"); else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed"); else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed"); else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed"); else if (error == OTA_END_ERROR) Serial.println("End Failed"); }); ArduinoOTA.begin(); Serial.println("Ready"); Serial.print("IP address: "); Serial.println(WiFi.localIP()); //---------------------------- } // end of setup //_________________________________________________________________________________________ //////// Microswitch 1 /////// void IRAM_ATTR microswitch1(){ // Code wird bei Betätigung von Microswitch1 ausgefuehrt unsigned long interrupt_time1 = millis(); // Debounce 400 ms if (interrupt_time1 - last_interrupt_time1 > 400) { microswitch_flag1 = 1; count1++; if (count1==1){ digitalWrite(LED_BUILTIN, HIGH); } else { digitalWrite(LED_BUILTIN, LOW); } } last_interrupt_time1 = interrupt_time1; } //_________________________________________________________________________________________ //////// Microswitch 2 ///////// void IRAM_ATTR microswitch2(){ // Code wird bei Betätigung von Microswitch2 ausgefuehrt unsigned long interrupt_time2 = millis(); if (interrupt_time2 - last_interrupt_time2 > 400) { // Debounce 400 ms microswitch_flag2 = 1; count2++; if (count2==1){ digitalWrite(LED_BUILTIN, HIGH); } else { digitalWrite(LED_BUILTIN, LOW); } } last_interrupt_time2 = interrupt_time2; } //_________________________________________________________________________________________ //////// Hall-Sensor1 ///////// void IRAM_ATTR hall1(){ // Motor von Hall-Sensor1 stoppen unsigned long interrupt_hall1 = millis(); if (interrupt_hall1 - last_interrupt_time_hall1 > 200) { // Debounce 200 ms hall_flag1 = 0; motor1.stop(); // Stop as fast as possible: sets new target motor1.setCurrentPosition(0); // set current postion to 0 motor1.moveTo(-509); // = steps_per_rev/4 : move a few steps, so camshaft is moved to defined position motor1.run(); microswitch_flag1 = 1; // starts manually a sequenze, which normaly ist triggered by microswitch 1 digitalWrite(LED_BUILTIN, LOW); detachInterrupt(digitalPinToInterrupt(GPIO_Pin_hall1)); //#################### } last_interrupt_time_hall1 = interrupt_hall1; } // End of Hall1-Subroutine //_________________________________________________________________________________________ //////// Hall-Sensor2 ///////// void IRAM_ATTR hall2(){ // Motor von Hall-Sensor2 stoppen unsigned long interrupt_hall2 = millis(); if (interrupt_hall2 - last_interrupt_time_hall2 > 200) { // Debounce 200 ms hall_flag2 = 0; motor2.stop(); // Stop as fast as possible: sets new target motor2.setCurrentPosition(0); // set current postion to 0 motor2.moveTo(-509); // = steps_per_rev/4 : move a few steps, so camshaft is moved to defined position motor2.run(); microswitch_flag2 = 1; // starts manually a sequenze, which normaly ist triggered by microswitch 2 digitalWrite(LED_BUILTIN, LOW); detachInterrupt(digitalPinToInterrupt(GPIO_Pin_hall2)); //#################### } last_interrupt_time_hall2 = interrupt_hall2; } // End of Hall2-Subroutine //_________________________________________________________________________________________ void loop() { ArduinoOTA.handle(); time(&now); // read the current time localtime_r(&now, &tm); //--------------------------------------------------------------------- // Einmalige Sequenz nach dem Booten für Nullstellung Motor1 (Stunden)---- if (hall_flag1==1){ motor1.moveTo(steps_per_rev * 2); digitalWrite(LED_BUILTIN, HIGH); motor1.run(); } // Einmalige Sequenz nach dem Booten für Nullstellung Motor2 (Minuten)---- if (hall_flag2==1){ motor2.moveTo(steps_per_rev * 2); digitalWrite(LED_BUILTIN, HIGH); motor2.run(); } //---- Mehrfach täglich Rebooten zur Wellenjustierung mittels Hall-Sensoren ---------- // Reboot um 02:35:30, 06:55:30 Uhr, 10:35:30 Uhr, 14:55:30, 18:35:30, 22:35:30 Uhr if ((tm.tm_hour == 2 || tm.tm_hour == 6 || tm.tm_hour == 10 || tm.tm_hour == 14 || tm.tm_hour == 18 || tm.tm_hour == 22 ) && tm.tm_min == 35 && tm.tm_sec == 30){ ESP.restart(); } //Stunden-Motor jede Stunde drehen-------------------------------------------- if (count1 == 0 && (tm.tm_min == 0 && tm.tm_sec == 0) ){ // count 0 wegen kontinuierlichem Stell-Modus Stunden //Serial.print("Stunde: "); Serial.println(tm.tm_hour); motor1.enableOutputs(); // enable again after current reduction run_flag1 = 1; motor1.setCurrentPosition(0); } if (run_flag1 == 1){ motor1.moveTo(steps_per_rev); motor1.run(); } if (motor1.distanceToGo() == 0){ motor1.stop(); motor1.run(); run_flag1 = 0; motor1.disableOutputs(); // current reduction while motor is not moving } //Minuten-Motor alle 5 Minuten drehen-------------------------------------------- if (count2 == 0 && (tm.tm_sec == 0 ) ){ // count 2 wegen kontinuierlichem Stell-Modus Minuten //Serial.print("tm.tm_min %5 = "); Serial.println(tm.tm_min %5); // Jede 5te Minute ist tm.tm_min %5 = 0 if (tm.tm_min %5 == 0){ motor2.enableOutputs(); // enable again after current reduction run_flag2 = 1; motor2.setCurrentPosition(0); } } if (run_flag2 == 1){ motor2.moveTo(steps_per_rev); motor2.run(); } if (motor2.distanceToGo() == 0){ motor2.stop(); motor2.run(); run_flag2 = 0; motor2.disableOutputs(); // current reduction while motor is not moving } //------------ MicroswitchSubroutine fuer Stell-Mode Stunden -------------------- if (microswitch_flag1 == 1){ run_flag1 = 0; if (motor1.distanceToGo() == 0) { // if motor1 moved to the maximum position if (count1 == 1) { //Serial.print("count1: "); Serial.print(count1); Serial.println(" = Stell-Modus Stunden"); motor1.setCurrentPosition(0); // reset position to 0 motor1.moveTo(steps_per_rev); // move the motor1 to maximum position again } else if (count1 >= 2) { microswitch_flag1 = 0; count1 = 0; } } // end of motor1.distanceToGo motor1.run(); } // end of microswitch_flag1 == 1 //------------ MicroswitchSubroutine fuer Stell-Modus Minuten ------------------- if (microswitch_flag2 == 1){ run_flag2 = 0; if (motor2.distanceToGo() == 0) { // if motor1 moved to the maximum position if (count2 == 1) { //Serial.print("count2: "); Serial.print(count2); Serial.println(" = Stell-Modus Minuten"); motor2.setCurrentPosition(0); // reset position to 0 //################ motor2.moveTo(steps_per_rev); // move the motor1 to maximum position again } else if (count2 >= 2) { microswitch_flag2 = 0; count2 = 0; } } // end of motor2.distanceToGo motor2.run(); } // end of microswitch_flag2 == 1 //--------------------------------------------------------------------- } // end of loop //_______________________________________________________________________________________