Skocz do zawartości

jacqob569

Użytkownicy
  • Zawartość

    1
  • Rejestracja

  • Ostatnio

O jacqob569

  • Urodziny 01.01.2004

Informacje

  • Płeć
    Mężczyzna
  • Programuję w
    C, C++
  • Zawód
    uczeń technik informatyk
  • Moje zainteresowania:
    programowanie Arduino i STM32, druk 3D, elektronika

Ostatnio na profilu byli

Blok z ostatnio odwiedzającymi jest wyłączony i nie jest wyświetlany innym użytkownikom.

jacqob569's Achievements

1/10

1/10 (1/10)

8

Reputacja

  1. Witam, Chcę przedstawić wam moje podejście do tematu sterowników akwarystycznych. Sterownik ten zbudowałem dla swojego dziadka, który chciał załączać automatycznie pompkę, napowietrzacz i światło do oświetlenia akwarium. Zacząłem więc planować, stwierdziłem, że sterownik musi posiadać minimum 2 wyjścia 230V, jedno wyjście 12V z możliwością sterowania PWM, jakieś bajery (odczyt temperatury wody, automatyczne wyłącznie przekaźników, automatyczny karmnik dla ryb itp). Kilka lat temu zbudowałem swój pierwszy sterownik akwarystyczny (nazwałem go V.1), wykorzystałem do tego celu esp8266-12e, moduł podwójnego przekaźnika elektromagnetycznego oraz czujnik DS18B20. Nie mam niestety zdjęć pierwszego modelu. Działał on u mnie przez ponad rok, w tym czasie wynotowałem kilka wad mojego rozwiązania. Największe wady: -Podczas braku dostępu do internetu nie mogłem korzystać z sterownika, -Przekaźnik nie zawsze wyłączał się, -esp zawieszało się podczas załączania przekaźnika (podczas rozbiórki sterownika okazało się, że dobrałem złe wartości rezystorów do LM317 i zamiast 3,3V na esp podawałem 2,95V ) Sterownik V.2 (dla dziadka) Postanowienia: Urządzenie musiało być proste w użytkowaniu, nie mogło tracić swoich podstawowych funkcjonalności podczas braku dostępu do internetu, sterownik powinien pamiętać stany wyjść podczas braku zasilania sterownik powinien automatycznie sterować wyjściami i modułami do niego podłączonymi sterownik powinien sterować wyjściem 12V w sposób płynny, imitować zachód lub wschód słońca na pasku LED musiał posiadać funkcję zdalnego zarządzania swoimi funkcjami Ten artykuł bierze udział w naszym konkursie! Na zwycięzców czekają karty podarunkowe Allegro, m.in.: 2000 zł, 1000 zł i 500 zł. Potrafisz napisać podobny poradnik? Opublikuj go na forum i zgłoś się do konkursu! Czekamy na ciekawe teksty związane z elektroniką i programowaniem. Sprawdź szczegóły » Działanie: Sterownik składa się z dwóch części, głównego sterownika oraz karty sieciowej. Sterownik główny odpowiada za włączanie/wyłączanie gniazd 230V, podawaniem sygnału PWM na wyjście, odczyt temperatury/wilgotności z czujników ds18b20 i DHT11, wyświetlanie godziny i innych danych na wyświetlaczu siedmio segmentowym, sprawdzaniu i wykonywaniu "alarmów", komunikowanie się z modułem karty sieciowej poprzez UART. Karta sieciowa - czyli moduł esp8266 (Wemos D1 mini) odpowiada za komunikację z aplikacją Blynk, zbieranie informacji (logów) z działania całego urządzenia i zapisywaniu ich na karcie SD, pobieraniu z internetu godziny do synchronizacji czasu, wysyłaniu wiadomości email z błędami w działaniu urządzenia lub plikami txt z logami, wyświetlaniu statusu połączenia z siecią i aplikacją za pomocą diod LED, komunikacją ze sterownikiem głównym, obsłudze komend wysyłanych przez terminal w aplikacji Blynk, itp. Budowa: W sterowniku głównym znajduje się: Arduino Nano Moduł podwójnego przekaźnika półprzewodnikowego SSR, zdecydowałem się na zmianę rodzaju przekaźnika z powodu tego, że taki przekaźnik jest bezgłośny, zawsze działa (nie zawiesza się tak jak przekaźniki elektromagnetyczne) Moduł zegara czasu rzeczywistego DS1307, odpowiada za dostarczanie dla wyświetlacza aktualnej godziny nawet po wyłączeniu zasilania oraz jako czas potrzebny do działania alarmów. Czujniki DS18B20 (wersja wodoodporna na metrowym przewodzie, metalowa końcówka czujnika znajduje się w wodzie) oraz DHT11 (wyprowadzony jest na zewnątrz obudowy sterownika) 4 przyciski z podświetleniem led w 4 kolorach(czerwony, zielony, niebieski, żółty). Odpowiadają one za włączanie i wyłączanie przekaźników, zmianę jasności paska LED, włączenie karmnika dla ryb oraz wyświetleniu na wyświetlaczu aktualnych temperatur Wyświetlacz siedmio segmentowy TM1637, wyświetla on aktualną godzinę, temperaturę, kody błędów Tranzystor IRF540 wraz z transoptorem PC817 i kilkoma rezystorami Przetwornica step-down (obniża napięcie 12V z zasilacza, na 5V potrzebnych do zasilenia Arduino i reszty modułów) Bezpieczniki 1A na gniazdka 230V oraz 1,2A na zasilaczu 12V Włącznik dźwigniowy (przerywa napięcie zasilania 12V) Zasilacz 12V 1,5A W karcie sieciowej znajduje się: Wemos D1 Mini, z dolutowaną anteną od starego routera tp-link (link do instrukcji takiej operacji instrukcja ) Adapter do karty microSD - SD 3 diody plus rezystory 220Ω micro switch, służy do resetowania płytki przełącznik 3 pozycyjny, służy do wyboru sieci WiFi, np. 1 - sieć1, 2 - sieć2, 3 - sieć serwisowa przełącznik 2 pozycyjny, służy do wyłączenia 3 diod sygnalizacyjnych LED, kiedy nie są potrzebne konwerter stanów logicznych, służy do konwersji napięć między Arduino a Wemosem na magistrali UART moduł pcf8574, steruje diodami oraz automatycznym karmnikiem, w przyszłości może także sterować kolejnymi przekaźnikami Działanie Sterownika głównego void setup: wczytanie ostatnich stanów wyjść z pamięci EEPROM ustawienie przerwania na 1ms (odpowiada za zmianę wartości na pinie PWM oraz dekrementuje zmienną potrzebną do wyświetlania informacji na wyświetlaczu ustawiam wejścia i wyjścia ustawiam stany pinów przy pomocy danych odczytanych z pamięci EEPROM (funkcja przywracania ostatnich stanów pinów) ustawiam jasność paska led (podczas włączenia sterownika, światło powoli rozjaśnia się, do ostatniego zapisanego stanu) Inicjuje zegar, czujniki temperatur, przyciski itp. void loop: W pętli loop sprawdzam: Czy jakiś przycisk został wciśnięty Ustawiam stany pinów, odświeżam informacje na wyświetlaczu Sprawdzam czy jakiś alarm może zostać wykonany Sprawdzam czy karta sieciowa wysłała jakieś dane Liczę ilość przejść pętli w czasie jednej sekundy, co sekundę wyświetlam tę ilość na serial monitorze Sprawdzam, czy minęło 5 sekund, po tym czasie raportuje do karty sieciowej o stanach wyjść ------------------------------------------------------------------------------------------------------------------------ 1.Sprawdzenie przycisków - Arduino obsługuje 4 przyciski, służą one do: Pojedyncze (krótkie wciśnięcie) - włączenie/wyłączenie przekaźnika 1 i 2, zmiany jasności paska LED podłączonego do pinu PWM, załączenie karmnika, Długie wciśnięcie - wyświetlenie na wyświetlaczu siedmio segmentowym temperatury wody/powietrza, szybkie wyłączenie paska LED przycisk niebieski - krótkie wciśnięcie (zmiana stanu przekaźnika 1) / długie wciśnięcie (wyświetlenie na wyświetlaczu temperatury wody) przycisk czerwony - krótkie wciśnięcie (zmiana stanu przekaźnika 2) / długie wciśnięcie (wyświetlenie na wyświetlaczu temperatury powietrza) przycisk zielony - krótkie wciśnięcie (zmiana stanu na pinie PWM ), każde wciśnięcie zwiększa wartość na pinie PWM [0,20,40,60,80,100%] / długie wciśnięcie szybko wygasza pasek LED przycisk żółty - krótkie wciśnięcie. Powoduje mruganie podświetleniem przycisku czerwonego i zielonego przez 10 sekund, wciśnięcie przycisku zielonego spowoduje podjęcie przez sterownik próby załączenia automatycznego karmnika \ wciśnięcie przycisku czerwonego anuluje ten proces, tak samo jak nie wciśnięcie żadnego z przycisków przez 10 sekund void checkbutton() { if (checkbutton_debuce == 1) { Serial.print(F("R_SW_State: ")); Serial.print(R_SW_state); Serial.print(F(" | B_SW_State: ")); Serial.print(B_SW_state); Serial.print(F(" | G_SW_State: ")); Serial.print(G_SW_state); Serial.print(F(" | Y_SW_State: ")); Serial.println(Y_SW_state); } G_SW.read(); B_SW.read(); Y_SW.read(); R_SW.read(); if (B_SW.changed()) { display.clear(); display.setSegments(display_symbol_G, 1, 0); display.showNumberDec(1, false, 1, 1); last_millis2 = 0; B_SW_state = B_SW.toggleState(); if (B_SW_state == 1) { display.setSegments(display_symbol_off, 2, 2); send_command(s1on, 8); } else { display.setSegments(display_symbol_on, 2, 2); send_command(s1off, 8); } screen_timeout = 5000; } if (R_SW.changed()) { display.clear(); display.setSegments(display_symbol_G, 1, 0); display.showNumberDec(2, false, 1, 1); last_millis2 = 0; R_SW_state = R_SW.toggleState(); if (R_SW_state == 1) { send_command(s2on, 8); display.setSegments(display_symbol_off, 2, 2); } else { send_command(s2off, 8); display.setSegments(display_symbol_on, 2, 2); } screen_timeout = 5000; } if (G_SW.pressedFor(LONG_PRESS)) { digitalWrite(GREEN_SW_LIGHT, 0); delay(50); digitalWrite(GREEN_SW_LIGHT, 1); delay(50); digitalWrite(GREEN_SW_LIGHT, 0); delay(50); digitalWrite(GREEN_SW_LIGHT, 1); delay(50); last_millis2 = 0; G_SW_button_cycle = 0; G_SW_state = 0; set_pwm_led_1(G_SW_state); led_step = 2; Serial.println("Green Button LONG PRESS DETECTED !"); display.clear(); display.showNumberDec(map(G_SW_state, 0, 255, 0, 100), false, 3, 1); display.setSegments(display_symbol_s, 1, 0); while (true) { G_SW.read(); if (G_SW.wasReleased()) { break; } } screen_timeout = 5000; return; } if (G_SW.changed()) { digitalWrite(GREEN_SW_LIGHT, 1); last_millis2 = 0; G_SW_button_cycle++; if (G_SW_button_cycle >= 6) { G_SW_button_cycle = 0; } switch (G_SW_button_cycle) { case 0: G_SW_state = 0; break; case 1: G_SW_state = 51; break; case 2: G_SW_state = 102; break; case 3: G_SW_state = 153; break; case 4: G_SW_state = 204; break; case 5: G_SW_state = 255; break; } display.clear(); display.showNumberDec(map(G_SW_state, 0, 255, 0, 100), false, 3, 1); display.setSegments(display_symbol_s, 1, 0); vpwm1[4] = (byte)G_SW_button_cycle * 2; send_command(vpwm1, 8); delay(40); if (G_SW_state != 255) { digitalWrite(GREEN_SW_LIGHT, 0); } led_step = led_step_button; screen_timeout = 5000; } if (Y_SW.pressedFor(LONG_PRESS)) { //locking feeding option Serial.println("Yellow Button LONG PRESS DETECTED !"); while (true) { Y_SW.read(); if (Y_SW.wasReleased()) { //send_command(auto_feed, 9); delay(25); break; } } return; } if (Y_SW.changed()) { bool wait2pressB = 0; bool w2p_ledstate = 1; unsigned long y_long_press_ms = millis(); unsigned long y_long_press_ms_led = millis(); while (wait2pressB == 0) { green_led_switch_on = 0; R_SW.read(); G_SW.read(); if (millis() - y_long_press_ms_led > 500) { digitalWrite(GREEN_SW_LIGHT, w2p_ledstate); digitalWrite(RED_SW_LIGHT, w2p_ledstate); w2p_ledstate = !w2p_ledstate; y_long_press_ms_led = millis(); } if (G_SW.changed()) { digitalWrite(GREEN_SW_LIGHT, G_SW_state); digitalWrite(RED_SW_LIGHT, R_SW_state); send_command(auto_feed, 9); delay(25); green_led_switch_on = 1; return; } else if (R_SW.changed()) { digitalWrite(GREEN_SW_LIGHT, G_SW_state); digitalWrite(RED_SW_LIGHT, R_SW_state); green_led_switch_on = 1; return; } else { } if (millis() - y_long_press_ms > 10000) { digitalWrite(GREEN_SW_LIGHT, G_SW_state); digitalWrite(RED_SW_LIGHT, R_SW_state); green_led_switch_on = 1; return; } } } } 2.Ustawienie odpowiednich stanów pinów: Ta funkcja odpowiada za zmianę stanów wyjść do których podłączony jest moduł przekaźników oraz za zmianę informacji na wyświetlaczu (godziny). void set_pin_states() { set_power_230V_1(B_SW_state); set_power_230V_2(R_SW_state); display_value_on_7s_display(Y_SW_state); } void set_power_230V_1(bool s) { digitalWrite(power_230V_1, !s); digitalWrite(BLUE_SW_LIGHT, s); } void set_power_230V_2(bool s) { digitalWrite(power_230V_2, !s); digitalWrite(RED_SW_LIGHT, s); } void display_value_on_7s_display(byte dis) { if (screen_timeout > 0) { return; } int current_t = 0; current_t = H * 100 + MIN; display.showNumberDecEx(current_t, 64, true); } 3."Alarmy": Poprzednia wersja sterownika posiadała ogromną wadę, chodzi o automatyczne załączanie przekaźników lub innych modułów. W poprzedniej wersji za załączanie automatyczne odpowiadała aplikacja Blynk z widżetem Timer, było to dość wygodne wyjście, ponieważ całą konfigurację (co ma się włączyć, kiedy, itp.) robiło się w przejrzystym menu aplikacji. To rozwiązanie miało jednak dużą wadę, podczas braku dostępu do internetu, sterownik nie wykonywał żadnych akcji. W drugiej wersji postanowiłem całkowicie oddzielić od siebie warstwę internetową (komunikacji z internetem) od warstwy podstawowych funkcjonalności, takich jak automatyczne włączanie/wyłączanie przekaźników, automatyczna zmiana jasności, włączenie karmnika itp. Co to jest "alarm"? - alarmem nazywam jedną funkcję, która wykona się o danej godzinie, minucie, dniu. Przechowywany jest w formie 5 jedno bajtowych komórek, ułożonych obok siebie w pamięci EEPROM. Alarm składa się z: A (Action- Pierwsza komórka, przechowuje numer funkcji, która ma zostać wykonana.) D (Day- Dzień tygodnia w który musi wykonać się alarm. 1 - poniedziałek / ... / 7 - niedziela / 8 - każdy dzień tygodnia) H (Hour - godzina. 0 - 23) M (Minute - minuta, 0 - 59) V (Value - parametr funkcji, np. funkcja 1 przyjmuje parametr 1 lub 0 [ włączenie / wyłączenie przekaźnika 1]) Ograniczenia - sterownik może przechować maksymalnie 80 alarmów, dwa lub więcej alarmów nie może zostać wykonanych w tej samej minucie Funkcja sprawdzająca, czy jakiś alarm może zostać wykonany: void check_alarm() { if (alarm_onoff == 1) { int p = 0; int n = 0; while (n <= ile_alarmow) { if (EEPROM.read(n * 5) != 127) { p = (byte)(n * 5); byte alarm_d, alarm_h, alarm_min, alarm_action, alarm_val; alarm_action = (byte)EEPROM.read(p); alarm_d = (byte)EEPROM.read(p + 1); alarm_h = (byte)EEPROM.read(p + 2); alarm_min = (byte)EEPROM.read(p + 3); alarm_val = (byte)EEPROM.read(p + 4); get_current_time(); if ((alarm_d == 8 || alarm_d == weekd) && alarm_onoff == 1) { if (alarm_h == H && alarm_min == MIN) { Serial.print(F("Wykonam akcje nr. ")); Serial.println(alarm_action); Serial.print(F("Alarm is trigered on ")); Serial.print(alarm_h); Serial.print(F(":")); Serial.print(alarm_min); Serial.print(F(":")); Serial.println(S); execute_alarm((byte)alarm_action, (byte)alarm_val); alarm_onoff = 0; alarm_timer.reset(); //1 minuta przerwy między alarmami } } else { //Serial.print(F("Nie wykonam akcji nr. ")); //Serial.println(i); } } n++; } } } void execute_alarm(byte execute_action_number, byte execute_value) { Serial.print(F("I'm executing function number ")); Serial.print(execute_action_number); Serial.print(F(" with value: ")); Serial.println(execute_value); if (execute_action_number == 1 || execute_action_number == '1') { bool action_flag; if (execute_value == 1) { action_flag = 1; } else if (execute_value == 0) { action_flag = 0; } else { action_flag = 0; } B_SW_state = action_flag; display.clear(); display.setSegments(display_symbol_G, 1, 0); display.showNumberDec(1, false, 1, 1); if (B_SW_state == 1) { display.setSegments(display_symbol_off, 2, 2); } else { display.setSegments(display_symbol_on, 2, 2); } screen_timeout = 5000; //set_power_230V_1(action_flag); } else if (execute_action_number == 2 || execute_action_number == '2') { bool action_flag2; if (execute_value == 1) { action_flag2 = 1; } else if (execute_value == 0) { action_flag2 = 0; } else { action_flag2 = 0; } R_SW_state = action_flag2; display.clear(); display.setSegments(display_symbol_G, 1, 0); display.showNumberDec(2, false, 1, 1); last_millis2 = 0; R_SW_state = R_SW.toggleState(); if (R_SW_state == 1) { send_command(s2on, 8); display.setSegments(display_symbol_off, 2, 2); } else { send_command(s2off, 8); display.setSegments(display_symbol_on, 2, 2); } screen_timeout = 5000; //set_power_230V_2(action_flag2); } else if (execute_action_number == 3 || execute_action_number == '3') { if (execute_value < 11 && execute_value > -1) { G_SW_state = map(execute_value, 0, 10, 0, 255); Serial.print(F("Setting led pwm value by alarm to ")); Serial.println(G_SW_state); if (execute_value == 0) { G_SW_button_cycle = 0; } else if (execute_value > 0 && execute_value <= 2) { G_SW_button_cycle = 1; } else if (execute_value > 2 && execute_value <= 4) { G_SW_button_cycle = 2; } else if (G_SW_state > 4 && G_SW_state <= 6) { G_SW_button_cycle = 3; } else if (G_SW_state > 6 && G_SW_state <= 8) { G_SW_button_cycle = 4; } else if (G_SW_state > 8 && G_SW_state <= 10) { G_SW_button_cycle = 5; } else { G_SW_button_cycle = 0; } led_step = led_step_alarm; //set_pwm_led_1(execute_value); display.clear(); display.showNumberDec(map(G_SW_state, 0, 255, 0, 100), false, 3, 1); display.setSegments(display_symbol_s, 1, 0); screen_timeout = 5000; } else { Serial.println(F("Wrong value !")); } } else if (execute_action_number == 4 || execute_action_number == '4') { Serial.println(F("Automatic feeding !!!")); //byte auto_feed[9] = {0x7E, 'A', 'F', ';', 'e', 'E', 'G', 0, 0x7F} send_command(auto_feed, 9); } else if (execute_action_number == 5 || execute_action_number == '5') { //re_send_time[9] = {0x7E, 'R', 'E', ':', 'S', '8', 'M', 0, 0x7F}; Serial.println(F("Alarm Automatic time syncing !!!")); send_command(re_send_time, 9); } else if (execute_action_number == 6 || execute_action_number == '6') { set_brightness(execute_value); } else if (execute_action_number == 7 || execute_action_number == '7') { if (EEPROM.read(1010) == 0) { //EEPROM.write(1010, 1); eeprom_optimalization(1010, 1); if (execute_value == 0) { //reset arduino Serial.println(F("Resseting arduino...")); resetFunc(); } else if (execute_value == 1) { //reset esp Serial.println(F("Resseting esp...")); send_command(reset_esp_command, 9); } else if (execute_value == 2) { //reset arduino and esp Serial.println(F("Resseting esp...")); send_command(reset_esp_command, 9); Serial.println(F("Resseting arduino...")); resetFunc(); } else { Serial.println(F("Wrong reset value!range:0-2")); //EEPROM.write(1010, 0); eeprom_optimalization(1010, 0); } } } else if (execute_action_number == 8 || execute_action_number == '8') { //send_all_mail } else if (execute_action_number == 9 || execute_action_number == '9') { //deleta_all_sd_card_files } else { Serial.println(F("Wrong alarm !!!")); } } 4.Sprawdzenie czy karta sieciowa wysłała jakieś dane: Esp8266 i Arduino Nano komunikują się ze sobą poprzez UART. Wszystkie informacje przesyłane są w postaci komend rozpoczynających się od znaku 0x7E, dalej reszta komendy wraz z parametrami, suma kontrolna, znak 0x7F, 3 znaki nowej lini '\n'. Przykładowa komenda wysyłana przez Arduino do Esp: 0x7E, 'n', '1', 'o', 'n', '+', (wyliczona suma kontrolna), 0x7F, '\n', '\n', '\n' Funkcja sprawdzająca odebrane dane w karcie sieciowej oraz Arduino jest prawie taka sama, polega na nasłuchiwaniu czy jest coś nadawane. Jeżeli tak to funkcja zapisuje do buffera bajt po bajcie aż do otrzymania trzech znaków nowej lini '\n' pod rząd. Jeżeli otrzymała te 3 znaki, rozpoczyna liczenie sumy kontrolnej od początku komendy do ostatniego bajtu danych w komendzie. Następnie porównuje wyliczoną sumę kontrolną z odebraną sumą, jeżeli sumy zgadzają się (są takie same), to Arduino lub Esp wykonuje daną komendę, np. komenda (0x7E, 'n', '1', 'o', 'n', '+', (wyliczona suma kontrolna), 0x7F, '\n', '\n', '\n') odpowiada za ustawienie w aplikacji Blynk stanu przycisku nr. 1 na włączony (podczas włączenia przekaźnika 1 przy pomocy przycisku na obudowie, Arduino musi wysłać tą informacje do karty sieciowej, aby użytkownik miał podgląd na aktualny stan przekaźnika). void recive_data() { bool control_val = 1; espSerial.flush(); if (espSerial.available() && control_val == 1) { espSerial.flush(); //digitalWrite(13, 1); delay(3); r_array_counter = 0; r_array[r_array_counter] = '\n'; timestart = millis(); //byte character_counter = 0; bool detect_end = 0; byte add_val = 2; while (espSerial.available() && r_array_counter < 25 && (millis() - timestart) < 400 && control_val == 1) { delay(1); byte character = espSerial.read(); r_array[r_array_counter] = character; r_array_counter++; r_array[r_array_counter] = '\n'; if (character != '\n' && control_val == 1) { if (serial_reciv_debug == 1) { Serial.print(F("{")); Serial.print(character); Serial.print(F("}")); } } if (character == '\n' && r_array[r_array_counter - 3] == '\n' && r_array[r_array_counter - 2] == '\n' && r_array[r_array_counter - 4] == 0x7F) { detect_end = 1; } if (detect_end == 1 && control_val == 1) { detect_end = 0; r_array_counter -= 1; if (serial_reciv_debug == 1) { Serial.print(F(" [r_array_counter]-> ")); Serial.println(r_array_counter); Serial.println(F(" - new line symbol detect!")); } byte chk = 0; uint8_t i = 0; for (i = 0; i < r_array_counter - (2 + add_val); i++) { chk ^= r_array[i]; } Serial.print("Control sum = "); Serial.println(chk); if (r_array[0] == 0x7E && r_array[r_array_counter - (1 + add_val)] == 0x7F && (byte)r_array[r_array_counter - (2 + add_val)] == (byte)chk && control_val == 1) { if (serial_reciv_debug == 1) { Serial.println(F("Good control sum !")); } last_millis2 = 0; if (r_array[1] == 's' && (r_array[3] == '$' || r_array[3] == '#')) { switch (r_array[2]) { case '1': if (r_array[4] == 'o' && r_array[5] == 'n') { B_SW_state = 1; set_power_230V_1(1); B_SW.m_toggleState = 1; display.clear(); display.setSegments(display_symbol_G, 1, 0); display.showNumberDec(1, false, 1, 1); display.setSegments(display_symbol_off, 2, 2); screen_timeout = 5000; } else if (r_array[4] == 'o' && r_array[5] == 'f') { B_SW_state = 0; set_power_230V_1(0); B_SW.m_toggleState = 0; display.clear(); display.setSegments(display_symbol_G, 1, 0); display.showNumberDec(1, false, 1, 1); display.setSegments(display_symbol_on, 2, 2); screen_timeout = 5000; } else { } break; case '2': if (r_array[4] == 'o' && r_array[5] == 'n') { R_SW_state = 1; set_power_230V_2(1); R_SW.m_toggleState = 1; display.clear(); display.setSegments(display_symbol_G, 1, 0); display.showNumberDec(2, false, 1, 1); display.setSegments(display_symbol_off, 2, 2); screen_timeout = 5000; } else if (r_array[4] == 'o' && r_array[5] == 'f') { R_SW_state = 0; set_power_230V_2(0); R_SW.m_toggleState = 0; display.clear(); display.setSegments(display_symbol_G, 1, 0); display.showNumberDec(2, false, 1, 1); display.setSegments(display_symbol_on, 2, 2); screen_timeout = 5000; } else { } break; } control_val = 0; } ... } } } 5.Sprawdzanie wydajności programu: Podczas pisania programu dla tego sterownika, musiałem sprawdzać wydajność danego rozwiązania, np. ile czasu zajmuje włączenie/wyłączenie jednej funkcji. Napisałem więc prostą funkcję, która sprawdza ile raz wykonują się wszystkie funkcje w funkcji main. Co sekundę Arduino wysyła na serial monitor informację ile razy wykonała się funkcja main. void framerate() { fr++; if (S != fr_sec) { Serial.print("##> FRAMERATE = "); Serial.print(fr); Serial.println(" fps <##"); fr = 0; fr_sec = S; } } 6.Funkcja wysyłające dane do karty sieciowej: Sterownik co 5 sekund wysyła do karty sieciowej informację o aktualnych temperaturach wody i powietrza oraz wilgotność. Wysyła je w formie komendy: 0x7E, '%', (wilgotność powietrza), 'T', (temp. powietrza liczba całkowita), (temp. powietrza liczba po przecinku), '@', (temp. wody liczba całkowita), (temp. wody liczba po przecinku), (suma kontrolna), 0x7F, '\n', '\n', '\n' Co 10 sekund wysyła dane oraz zapisuje stan wyjść w pamięci EEPROM, jeżeli są one inne od ostatnio zapisanych ( jest to potrzebne do działania funkcji przywracania stanów podczas wyłączenia sterownika). Wysyłane dane składają się z informacji o stanach wyjść sterownika (stan przekaźnika 1 i 2, wartość na pinie PWM). Komenda wygląda następująco: 0x7E, (stan przekaźnika 1), '&', (stan przekaźnika 2), '^', (wartość na pinie PWM przeskalowana od 0 do 10), '%', (suma kontrolna) , 0x7F, '\n', '\n', '\n' void execute_millis_functions() { if ((millis() - last_millis1) > 5000) { send_sensors_data(); last_millis1 = millis(); } if ((millis() - last_millis2) > 10000) { sync_data(); Serial.print(F("****> power_loos_counter = ")); Serial.println(power_loos_counter); if (B_SW_state != EEPROM.read(1001)) { //EEPROM.write(1001, B_SW_state); eeprom_optimalization(1001, B_SW_state); power_loos_counter++; } if (R_SW_state != EEPROM.read(1002)) { //EEPROM.write(1002, R_SW_state); eeprom_optimalization(1002, R_SW_state); power_loos_counter++; } if (G_SW_state != EEPROM.read(1003)) { //EEPROM.write(1003, G_SW_state); eeprom_optimalization(1003, G_SW_state); power_loos_counter++; } last_millis2 = millis(); } if (alarm_onoff == 0 && alarm_timer.isReady()) { alarm_onoff = 1; } if (timer1.isReady()) { timer_clock(); } } void sync_data() { //sync[9] = {0x7E, 0, '&', 0, '^', 0, '%', 0, 0x7F}; if (B_SW_state == 1) { data_2_sync[1] = 1; } else if (B_SW_state == 0) { data_2_sync[1] = 0; } else { } if (R_SW_state == 1) { data_2_sync[3] = 1; } else { data_2_sync[3] = 0; } if (G_SW_button_cycle == 0) { data_2_sync[5] = 0; } else if (G_SW_button_cycle == 1) { data_2_sync[5] = 2; } else if (G_SW_button_cycle == 2) { data_2_sync[5] = 4; } else if (G_SW_button_cycle == 3) { data_2_sync[5] = 6; } else if (G_SW_button_cycle == 4) { data_2_sync[5] = 8; } else if (G_SW_button_cycle == 5) { data_2_sync[5] = 10; } else { data_2_sync[5] = 0; } send_command(data_2_sync, 9); } KOD Sterownika (Zalecam wykorzystanie bibliotek dołączonych do tego artykułu, ponieważ zmieniałem w nich kilka rzeczy): aquarium_driver_v2.zip Działanie Karty Sieciowej (esp8266) void setup(): void setup() { Serial.begin(9600); EEPROM.begin(1000); //0-405 - alarms eeprom //507-998 - epprom variable space //1000-4096 - log eeprom space delay(50); pcf8574.pinMode(Wifi_status_led, OUTPUT); pcf8574.pinMode(Blynk_status_led, OUTPUT); pcf8574.pinMode(Connection_status_led, OUTPUT); pcf8574.pinMode(feeding_pin, OUTPUT); pcf8574.pinMode(retraction_pin, OUTPUT); pcf8574.pinMode(wifi_network_sw_a, INPUT_PULLUP); pcf8574.pinMode(wifi_network_sw_b, INPUT_PULLUP); pcf8574.begin(); pcf8574.digitalWrite(Wifi_status_led, OFF); pcf8574.digitalWrite(Blynk_status_led, OFF); pcf8574.digitalWrite(Connection_status_led, OFF); pcf8574.digitalWrite(feeding_pin, OFF); pcf8574.digitalWrite(retraction_pin, OFF); for (int i = 0; i < 880; i++) { message_aray[i] = 127; } feeding_time = EEPROM.read(510); //510 retraction_time = EEPROM.read(511); //511 feeding_today = EEPROM.read(509); //509 delay(100); set_network(); byte datalog_d, datalog_m, datalog_y; datalog_d = EEPROM.read(523); datalog_m = EEPROM.read(524); datalog_y = EEPROM.read(525); String dd_str = ""; if (datalog_d < 10) { dd_str = "0"; } dd_str += String(datalog_d); String dm_str = ""; if (datalog_m < 10) { dm_str = "0"; } dm_str += String(datalog_m); String dy_str = ""; if (datalog_y < 10) { dy_str = "0"; } dy_str += String(datalog_y); current_log_filename = "L" + dd_str + dm_str + dy_str + ".txt"; cmd.print("Log filename is "); cmd.println(current_log_filename); cmd.flush(); if (EEPROM.read(522) == 1) { EEPROM.write(522, 0); EEPROM.commit(); send_mail(0); } else if (EEPROM.read(522) == 2) { EEPROM.write(522, 0); EEPROM.commit(); send_mail(1); } else { } if (WiFi.status() == 6) { pcf8574.digitalWrite(Wifi_status_led, OFF); } unsigned long startWifi = millis(); WiFi.mode(WIFI_STA); WiFi.begin(E_ssid.c_str(), E_pass.c_str()); bool wifi_flag = false; while (WiFi.status() != WL_CONNECTED) { delay(100); pcf8574.digitalWrite(Wifi_status_led, wifi_flag); wifi_flag = !wifi_flag; if (millis() > startWifi + myWifiTimeout) { Serial.println("Time out"); break; } } Blynk.config(auth, server, port); checkBlynk(); timer.setInterval(functionInterval, myfunction); // run some function at intervals per functionInterval timer.setInterval(blynkInterval, checkBlynk); // check connection to server per blynkInterval timer.setInterval(500L, period_function); timer.setInterval(5000L, sync_alarms); // sync alarm && check_logs(); timer.setInterval(15000L, check_day_change); cmd.clear(); blynk_led.off(); setSyncInterval(1000); dp_network(); if (debug_log_string.length() > 0) { cmd.println(debug_log_string); cmd.flush(); } add_log(48, datalog_d, datalog_m, datalog_y); add_log(30, 1, 999, 999); check_day_change(); } Zainicjowanie UARTA oraz pamięci "EEPROM" esp8266 Ustawienie pinów modułu ekspandera pcf8574 (wejścia dla przełącznika do wyboru sieci WiFi, wyjścia do sterowania karmnikiem oraz dla diod LED wyświetlających stan połączenia z siecią WiFi, połączenia z serwerem aplikacji Blynk oraz potwierdzenie o otrzymaniu komendy od Arduino na magistrali UART) Odczytanie parametrów z pamięci "EEPROM" (karta sieciowa odczytuje: informacje potrzebne do działania karmnika) Odczytanie z przełącznika wybranej sieci wifi oraz wczytanie nazwy Sieci (SSID) oraz hasła do sieci Ustawienie nazwy pliku z logami sterownika Próba połączenia się z siecią Konfiguracja ustawień aplikacji Blynk i sprawdzenie połączenia z serwerem aplikacji Ustawienie Timerów Dodanie do logów informacji o starcie karty sieciowej ------------------------------------------------------------------------------------------------------------------------ 4.Sterownik może obsłużyć maksymalnie 3 sieci WiFi, 2 sieci użytkownika oraz jedną sieć serwisową. Na karcie sieciowej znajduje się przełącznik 3 pozycyjny, pierwsze dwie pozycje to sieci użytkownika, ostatnia, to sieć serwisowa. Po sprawdzeniu przez kartę sieciową stanu przełącznika, odczytuje z pamięci "EEPROM" nazwę sieci (SSID) oraz hasło, jeżeli wybrano 3 pozycję przełącznika, SSID ustawiane jest na SIEC_SERWISOWA oraz hasło SERWISANT123. Ma to na celu zapobieganie blokadzie karty sieciowej, przez źle podane hasło. Hasło do sieci WiFi można zmienić przy pomocy telefonu, służy do tego komenda add_wifi=(numer pod którym ma zostać zapisana nowa sieć, 1 lub 2)-(SSID maks 20 znaków),(Hasło maks 20 znaków) 5.Podczas załączania się karty sieciowej tworzona jest nazwa pliku, do którego będą zapisywane dane. Nazwa zawiera aktualną datę, np. L010321.txt, urządzenie zapisuję do tego pliku oraz pliku DATALOG.txt informację o działaniu sterownika oraz karty sieciowej w postaci logów. void loop(): void loop() { recive_data(); check_feeding(); if (millis() - repair_data_timer >= 10000 && EEPROM.read(526) == 1) { repair_data_timer = millis(); byte repair_number = check_is_data_ok(); if (repair_number != 0) { repair_data(repair_number); } } if (millis() >= pwm_timeout_time) { pwm_timeout = 0; } if (Blynk.connected()) { Blynk.run(); } timer.run(); if (millis() - one_hour_timer >= 60000 && one_hour_timer != 0) { //rechecking values one_hour_timer = 0; E_value_eeprom_Succes_write_counter_esp = 0; E_value_eeprom_Fail_write_counter_esp = 0; E_value_eeprom_Succes_write_counter_arduino = 0; E_value_eeprom_Fail_write_counter_arduino = 0; E_arduino_alarm_syncing_counter = 0; } } 1.Sprawdzenie czy otrzymano jakieś dane 2.Włączenie/wyłączenie karmnika 3.Sprawdzenie poprawności danych 4.Callback Blynk'a 5.Resetowanie kodów błędu po upłynięciu 1h ------------------------------------------------------------------------------------------------------------------------ 1. Funkcja ta działa tak samo jak funkcja sprawdzająca czy otrzymano dane z sterownika 2. Karta sieciowa odpowiada za sterowanie modułem karmnika, sterowany jest dwoma pinami z modułu pcf8574 podłączonego do Wemosa. Stan wysoki na pierwszym pinie załącza karmienie, stan wysoki na drugim pinie zaczyna cofać pokarm do zbiornika. 3. Ten element programu służy do sprawdzania, czy wszystko z danymi (np. stany wyjść, zmienne zapisane w "EEPROMIE" itp.) jest ok. Np. komórka 510 w pamięci "EEPROM" przechowuje czas podawania pokarmu, zakres to 0-29 sekund. Cały sterownik umożliwia zmianę wartości w pamięci EEPROM zdalnie przy pomocy komend, np. write_esp_eeprom=(komórka pamięci "EEPROM" w zakresie 0-1000),(wartość od 0 do 255) lub write_ard_eeprom=(komórka pamięci EEPROM w zakresie 0-1023),(wartość od 0 do 255). Przez przypadek mogę ustawić wartość w komórce 510 na 255, oznacza to, że karmnik będzie podawał pokarm przez ponad 4 minuty ! Jest to dość niebezpieczne dla ryb. Funkcja ta sprawdza więc czy wszystkie wpisane wartości w odpowiednich komórkach są poprawne, jeżeli nie, to sterownik informuje o tym użytkownika oraz stara się naprawić dane, np. ustawia wartość w komórce 510 na 2 sekundy. Sterownik może wysłać maila jeżeli usterka jest bardziej złożona, np. sterownik oraz karta sieciowa korzystają z pamięci EEPROM, pozwala ona na zachowanie wartości nawet po zaniku zasilania, niestety korzystanie z tej pamięci jest ograniczone ilością zapisów (średnio około 100 000), po tej wartości z czasem mogą pojawiać się problemy z pamięcią, może ona ulec uszkodzeniu. Napisałem funkcję która ma na celu kontrolowanie zużycia tej pamięci, void eeprom_optimalization(int E_case, int E_val2w, bool add2counter, bool safe_mode) { if (E_val2w > 255 || E_val2w < 0) { cmd.println("Value to write is out of range!"); add_log(31, E_case, E_val2w, 0); return; } if (safe_mode == 1) { //int eeprom_case_not2write[7] = {507, 508, 509, 510, 511, 512, 513}; for (int i = 0; i < eeprom_case_not2write_length; i++) { if (E_case == eeprom_case_not2write[i]) { cmd.println("This eeprom case cannot be written!"); cmd.flush(); add_log(31, E_case, E_val2w, 1); return; } } } else { for (int i = 0; i < eeprom_case_not2write_length; i++) { if (E_case == eeprom_case_not2write[i]) { cmd.println("This eeprom case is dangerous to written!"); cmd.flush(); break; } } } byte val2write = (byte)E_val2w; byte E_val_read = EEPROM.read(E_case); if (E_val_read != E_val2w) { EEPROM.write(E_case, val2write); if (add2counter == 1) { int r_e = ((EEPROM.read(514) << 8) + EEPROM.read(515)); r_e += 1; byte r_e_b = highByte(r_e); EEPROM.write(514, r_e_b); r_e_b = lowByte(r_e); EEPROM.write(515, r_e_b); cmd.print("Writting eeprom succesful counter = "); cmd.println(r_e); } add_log(31, E_case, E_val2w, 2); } else { if (add2counter == 1) { int r_e = ((EEPROM.read(516) << 8) + EEPROM.read(517)); r_e += 1; byte r_e_b = highByte(r_e); EEPROM.write(516, r_e_b); r_e_b = lowByte(r_e); EEPROM.write(517, r_e_b); cmd.print("Writting eeprom fail counter = "); cmd.println(r_e); } add_log(31, E_case, E_val2w, 3); } EEPROM.commit(); } Ma ona kilka ważnych według mnie funkcji: sprawdzenie czy podana wartość mieści się w zakresie 0-255 sprawdzenie czy podany numer komórki nie jest zapisany na liście z niebezpiecznymi komórkami do zapisu, jeżeli tak to albo odmówi zapisu w danym miejscu albo poinformuje użytkownika, że trzeba uważać na tą komórką pamięci sprawdzenie czy aktualna wartość w podanej komórce jest inna od podanej, jeżeli tak to następuje zapis oraz zwiększenie się licznika udanych zapisów Sterownik sprawdza czy ilość zapisów nie przekracza wartości maksymalnej, np. 1500 zapisów, jeżeli przekracza, to informuje o tym użytkownika wysyłając mu wiadomość email. 5. Jeżeli coś z danymi jest nie tak, to użytkownik może dostać wiadomość, nie chcę jej jednak otrzymywać często, wystarczy przypomnienie np. co godzinę. Dlatego karta sieciowa ustawia jedno godzinny time out na wysyłanie wiadomości, jeżeli w czasie jednej godziny użytkownik nie naprawi usterki (nie zresetuje przy pomocy komendy licznika zapisów) to wyśle on kolejną wiadomość. Obsługa komend przez kartę sieciową: Podczas budowy i programowania tego urządzenia miałem na celu stworzenie czegoś w rodzaju zdalnego zarządzania całym urządzeniem, obecnie sterownik pracuje u dziadka dlatego muszę mieć możliwość kontrolowania jego działania zdalnie. Dla przykładu, jedyną opcją na dodanie alarmu jest wpisanie go w formie komendy na telefonie. Plusem tego rozwiązania jest to, że nie muszę jechać do dziadka aby zmienić np. godzinę włączenia światła itp., mam także podgląd na to czy wszystko działa. Komendy wprowadzane są w widżecie Terminal w aplikacji Blynk, kod obsługujący komendy: BLYNK_WRITE(V6) { String cmd_request = " "; cmd_request = param.asStr(); if (String("cls") == cmd_request) { cmd.clear(); add_log(51, 1, 999, 999); } else if (cmd_request.startsWith("show_alarms=") || cmd_request.startsWith("sha=")) { uint8_t index_val_1 = cmd_request.indexOf('='); uint8_t cmd_val = cmd_request.substring(index_val_1 + 1).toInt(); show_all_alarms[3] = cmd_val + '0'; send_command(show_all_alarms, 8); add_log(52, cmd_val, 999, 999); } else if (String("alarms_counter") == cmd_request || String("ac") == cmd_request) { int e_count = 0, n = 0; while (n <= ile_alarmow) { if (EEPROM.read((n * 5) + 1) != 127) { e_count++; } n++; } cmd.print("Esp alarms: "); cmd.println(e_count); send_command(show_alarms_counter, 8); add_log(53, e_count, 999, 999); } else if (cmd_request.startsWith("display_alarm=") || cmd_request.startsWith("da=")) { uint8_t index_val_1 = cmd_request.indexOf('='); uint8_t ter_val = cmd_request.substring(index_val_1 + 1).toInt(); if (ter_val <= ile_alarmow) { cmd.print("Displayed alarm "); cmd.print(ter_val); cmd.println(" :"); cmd.print("-Esp: "); cmd.flush(); int p = ter_val * 5; cmd.print("f(x): "); cmd.print(EEPROM.read(p + 4)); cmd.print("|D: "); cmd.print(EEPROM.read(p + 1)); cmd.print("|H: "); cmd.print(EEPROM.read(p + 2)); cmd.print("|MIN: "); cmd.print(EEPROM.read(p + 3)); cmd.print("|X: "); cmd.println(EEPROM.read(p + 5)); cmd.flush(); show_alarm[4] = ter_val + '0'; send_command(show_alarm, 8); } else { cmd.print("Wrong alarm case (0-"); cmd.print(ile_alarmow); cmd.println(")"); cmd.flush(); } add_log(54, ter_val, 999, 999); } else if (cmd_request.startsWith("set_alarm=") || cmd_request.startsWith("sa=")) { int eep_szuf = 1; int eep_buff = 1; uint8_t eep_a, eep_b, eep_c, eep_d, eep_f; uint8_t index_val_1, index_val_2, index_val_3, index_val_4, index_val_5, index_val_6; int cmd_val_1, cmd_val_2, cmd_val_3, cmd_val_4, cmd_val_5, cmd_val_6; index_val_1 = cmd_request.indexOf('='); index_val_2 = cmd_request.indexOf(','); index_val_3 = cmd_request.indexOf('-'); index_val_4 = cmd_request.indexOf(':'); index_val_5 = cmd_request.indexOf('&'); index_val_6 = cmd_request.indexOf('*'); cmd_val_1 = cmd_request.substring(index_val_1 + 1).toInt(); cmd_val_2 = cmd_request.substring(index_val_2 + 1).toInt(); cmd_val_3 = cmd_request.substring(index_val_3 + 1).toInt(); cmd_val_4 = cmd_request.substring(index_val_4 + 1).toInt(); cmd_val_5 = cmd_request.substring(index_val_5 + 1).toInt(); cmd_val_6 = cmd_request.substring(index_val_6 + 1).toInt(); if (cmd_request[index_val_1] == '=' && cmd_request[index_val_2] == ',' && cmd_request[index_val_3] == '-' && cmd_request[index_val_4] == ':' && cmd_request[index_val_5] == '&' && cmd_request[index_val_6] == '*') { if ((cmd_val_2 >= 0 && cmd_val_2 < 9) && (cmd_val_3 > -1 && cmd_val_3 < 24) && (cmd_val_4 > -1 && cmd_val_4 <= 60)) { if (cmd_val_1 == 127) { cmd.print("[auto-case]-"); for (int w = 0; w <= ile_alarmow; w++) { if (EEPROM.read((w * 5) + 1) == 127) { eeprom_optimalization(0, w, 1, 0); byte x = EEPROM.read(0); cmd.print("Setting auto alarm value to "); cmd.println(x); cmd.flush(); //EEPROM.commit(); eep_szuf = x * 5; eep_buff = eep_szuf / 5; break; } } } else { if (cmd_val_1 >= 0 && cmd_val_1 <= ile_alarmow) { cmd.print("Setting manual alarm value to "); cmd.println(cmd_val_1); eep_szuf = cmd_val_1 * 5; eep_buff = eep_szuf / 5; } else { cmd.print("Wrong manual case value!"); return; } } cmd.println(); cmd.flush(); cmd.print("Setting alarm "); cmd.print(eep_buff); cmd.print(" on the day "); cmd.print(cmd_val_2); cmd.print(" ("); cmd.print(cmd_val_3); cmd.print(":"); cmd.print(cmd_val_4); cmd.print(") - function: "); cmd.print(cmd_val_5); cmd.print(" with value: "); cmd.println(cmd_val_6); cmd.flush(); eeprom_optimalization(eep_szuf + 1, cmd_val_2, 1, 0); eeprom_optimalization(eep_szuf + 2, cmd_val_3, 1, 0); eeprom_optimalization(eep_szuf + 3, cmd_val_4, 1, 0); eeprom_optimalization(eep_szuf + 4, cmd_val_5, 1, 0); eeprom_optimalization(eep_szuf + 5, cmd_val_6, 1, 0); //EEPROM.commit(); set_alarm[2] = cmd_val_2 + '0'; set_alarm[3] = cmd_val_3 + '0'; set_alarm[4] = cmd_val_4 + '0'; set_alarm[6] = cmd_val_5 + '0'; set_alarm[7] = cmd_val_6 + '0'; set_alarm[9] = eep_buff + '0'; send_command(set_alarm, 13); } else { cmd.println("Wrong time !"); cmd.flush(); } } else { cmd.println("Wrong command structure!"); cmd.flush(); } add_log(26, eep_buff, cmd_val_5, cmd_val_6); } else if (String("alarm_help") == cmd_request || String("ah") == cmd_request) { cmd.clear(); cmd.println("write: set_alarm=y,D-H:M&F*X$A"); String y_nr_str = "y - nr. szufladki (127 = auto, 0-" + String(ile_alarmow) + " counter)"; cmd.println(y_nr_str); cmd.println("D - dzien (1-7) 8=all"); cmd.println("H - godzina (0-23)"); cmd.println("M - minuta (0-59)"); cmd.println("F - funkcja (1-6)[alarm_function=help]"); cmd.println("x - wartosc funkcji"); cmd.println(""); cmd.flush(); cmd.println("______________"); cmd.println("F: 1 - gniazdo 1 [1 or 0] (pompa)"); cmd.println(" 2 - gniazdo 2 [1 or 0]"); cmd.println(" 3 - pasek led [0 - 100]"); cmd.println(" 4 - karmnik [none]"); cmd.flush(); cmd.println(" 5 - auto time syncing [none]"); cmd.println(" 6 - display brightness [0 - 3]"); cmd.println(" 7 - reset device [0-2:arduino/esp/all]"); cmd.flush(); add_log(55, 1, 999, 999); } else if (cmd_request.startsWith("rm_alarm=") || cmd_request.startsWith("rma=")) { uint8_t index_val_1; int cmd_val_1; index_val_1 = cmd_request.indexOf('='); cmd_val_1 = cmd_request.substring(index_val_1 + 1).toInt(); if (cmd_val_1 < 0 || cmd_val_1 >= ile_alarmow) { cmd.print("Wrong alarm number - range(0-") ; cmd.print(ile_alarmow); cmd.println(")"); add_log(56, cmd_val_1, 0, 999); } else { cmd.print("Removing alarm nr. "); cmd.println(cmd_val_1); rm_single_alarm[3] = cmd_val_1 + '0'; int n = cmd_val_1; n = n * 5; eeprom_optimalization(n + 1, 127, 1, 0); eeprom_optimalization(n + 2, 127, 1, 0); eeprom_optimalization(n + 3, 127, 1, 0); eeprom_optimalization(n + 4, 127, 1, 0); eeprom_optimalization(n + 5, 127, 1, 0); //EEPROM.commit(); send_command(rm_single_alarm, 8); add_log(56, cmd_val_1, 1, 999); } } else if (String("rm_all_alarm") == cmd_request) { send_command(rm_all_alarms, 8); for (int n = 1; n <= ((ile_alarmow + 1) * 5) - 1; n++) { eeprom_optimalization(n, 127, 0, 0); //EEPROM.commit(); if (n % 10 == 0) { cmd.print("Removed case from "); cmd.print(n - 10); cmd.print(" to "); cmd.println(n); } } eeprom_optimalization(0, 0, 1, 0); //EEPROM.commit(); add_log(57, 1, 999, 999); } else if (String("millis_usage") == cmd_request || String("mu") == cmd_request) { float x, z; cmd.println(); cmd.print("ESP8266: "); x = millis() / (1000 * 60); int millis_esp = millis() / (1000 * 60); cmd.print(millis() / (1000 * 60)); cmd.print(" min. / 71,580 min. - "); z = x / 71580; cmd.print(z, 3); cmd.println(" %"); send_command(disp_millis, 6); add_log(58, millis_esp, 999, 999); } else if (cmd_request.startsWith("read_ard_eeprom") || cmd_request.startsWith("rae")) { //eeprom_action[11] = {0x7E, 'E', 0, 'P', '.', 0, 0, 'm', 0, 'A', 0, 0x7F}; uint8_t index_val_1; int cmd_val_1; index_val_1 = cmd_request.indexOf('='); cmd_val_1 = cmd_request.substring(index_val_1 + 1).toInt(); if (cmd_val_1 > 1023 || cmd_val_1 < 0) { cmd.println("Wrong value (0-1023)"); add_log(59, cmd_val_1, 0, 999); } else { byte Bdata_a = 0, Bdata_b = 0; Bdata_a = highByte(cmd_val_1); Bdata_b = lowByte(cmd_val_1); cmd.print("highByte = "); cmd.print(Bdata_a); cmd.print(" - lowByte = "); cmd.println(Bdata_b); //eeprom_action[12] = {0x7E, 'E', 0, 'P', '.', 0, 0, 'm', 0, 'A', 0, 0x7F}; eeprom_action[2] = 1 + '0'; eeprom_action[5] = Bdata_a; eeprom_action[6] = Bdata_b; eeprom_action[8] = 0 + '0'; send_command(eeprom_action, 12); add_log(59, cmd_val_1, 1, 999); } } else if (cmd_request.startsWith("write_ard_eeprom=") || cmd_request.startsWith("wae=")) { uint8_t index_val_1, index_val_2; int cmd_val_1, cmd_val_2; index_val_1 = cmd_request.indexOf('='); cmd_val_1 = cmd_request.substring(index_val_1 + 1).toInt(); index_val_2 = cmd_request.indexOf(','); cmd_val_2 = cmd_request.substring(index_val_2 + 1).toInt(); if (cmd_val_1 > 1023 || cmd_val_1 < 0 || cmd_val_2 > 255 || cmd_val_2 < 0) { cmd.println("Wrong value (val1: 0-1023 val2: 0-255)"); add_log(60, cmd_val_1, cmd_val_2, 0); } else { byte Bdata_a = 0, Bdata_b = 0; Bdata_a = highByte(cmd_val_1); Bdata_b = lowByte(cmd_val_1); cmd.print("highByte = "); cmd.print(Bdata_a); cmd.print(" - lowByte = "); cmd.println(Bdata_b); //eeprom_action[12] = {0x7E, 'E', 0, 'P', '.', 0, 0, 'm', 0, 'A', 0, 0x7F}; cmd.print("Writening ard eeprom case "); cmd.print(cmd_val_1); cmd.print(" with value of "); cmd.println(cmd_val_2); eeprom_action[2] = 2 + '0'; eeprom_action[5] = Bdata_a; eeprom_action[6] = Bdata_b; eeprom_action[8] = cmd_val_2; send_command(eeprom_action, 12); add_log(60, cmd_val_1, cmd_val_2, 1); } } else if (cmd_request.startsWith("sync_time=")) { //set_time[12] = {0x7E, 'T', 0, 0, 0, '/', 0, 0, 0, 's' 0, 0x7F}; //H:Min-D/M.Y uint8_t i_val_1, i_val_2, i_val_3, i_val_4, i_val_5; int val_1, val_2, val_3, val_4, val_5; i_val_1 = cmd_request.indexOf('='); val_1 = cmd_request.substring(i_val_1 + 1).toInt(); i_val_2 = cmd_request.indexOf(':'); val_2 = cmd_request.substring(i_val_2 + 1).toInt(); i_val_3 = cmd_request.indexOf('-'); val_3 = cmd_request.substring(i_val_3 + 1).toInt(); i_val_4 = cmd_request.indexOf('/'); val_4 = cmd_request.substring(i_val_4 + 1).toInt(); i_val_5 = cmd_request.indexOf('.'); val_5 = cmd_request.substring(i_val_5 + 1).toInt(); String currentTime = String(val_1) + ":" + String(val_2) + ":" + String(second()); String currentDate = String(val_3) + " " + String(val_4) + " " + String(val_5 + 2000); cmd.print(currentTime); cmd.print(" - "); cmd.println(currentDate); cmd.flush(); set_time[2] = val_1 + '0'; set_time[3] = val_2 + '0'; set_time[4] = second() + '0'; set_time[6] = val_3 + '0'; set_time[7] = val_4 + '0'; set_time[8] = val_5 + '0'; send_command(set_time, 12); add_log(61, val_1, val_2, val_3); } else if (String("auto_sync_time") == cmd_request || String("ast") == cmd_request) { String currentTime = String(hour()) + ":" + minute() + ":" + second(); String currentDate = String(day()) + " " + month() + " " + year(); cmd.print(currentTime); cmd.print(" - "); cmd.println(currentDate); set_time[2] = hour() + '0'; set_time[3] = minute() + '0'; set_time[4] = second() + '0'; set_time[6] = day() + '0'; set_time[7] = month() + '0'; set_time[8] = year() - 2000 + '0'; send_command(set_time, 12); add_log(62, hour(), minute(), day()); } else if (cmd_request.startsWith("Arduino_info=") || cmd_request.startsWith("ai=")) { uint8_t index_val_1; int cmd_val_1; index_val_1 = cmd_request.indexOf('='); cmd_val_1 = cmd_request.substring(index_val_1 + 1).toInt(); ard_info[3] = (byte)cmd_val_1 + '0'; send_command(ard_info, 8); add_log(63, cmd_val_1, 999, 999); } else if (String("arduino_info_help") == cmd_request || String("aih") == cmd_request) { cmd.println("1 - show arduino alarm syncing counter"); cmd.println("2 - print arduino eeprom in serial monitor"); cmd.println("3 - reset alarm syncing counter"); cmd.println("4 - show arduino eeprom succesful writing counter"); cmd.flush(); cmd.println("5 - show arduino eeprom fail writing counter"); cmd.println("6 - reset arduino eeprom succesful writing counter"); cmd.println("7 - reset arduino eeprom fail writing counter"); cmd.flush(); cmd.println("8 - reset arduino eeprom succesful and fail writing counter"); cmd.flush(); add_log(64, 1, 999, 999); } else if (cmd_request.startsWith("read_esp_alarm") || cmd_request.startsWith("rea")) { uint8_t index_val_1; int cmd_val_1; index_val_1 = cmd_request.indexOf('='); cmd_val_1 = cmd_request.substring(index_val_1 + 1).toInt(); int n = 0; int p = 0; byte ac_2_log = 0; byte log_score = (byte)cmd_val_1; if (cmd_val_1 == 0 || cmd_val_1 == 1) { }else{ cmd.println("Wrong argument (0 or 1)"); cmd.flush(); add_log(65, log_score, ac_2_log, 999); return; } while (n <= ile_alarmow) { if (cmd_val_1 == 0) { if (EEPROM.read((n * 5) + 1) != 127 && n <= ile_alarmow) { p = n * 5; cmd.print("Szuf."); cmd.print(n); cmd.print(" ->f(x): "); cmd.print(EEPROM.read(p + 4)); //1 cmd.print(" |D: "); cmd.print(EEPROM.read(p + 1)); //2 cmd.print("|H: "); cmd.print(EEPROM.read(p + 2)); //3 cmd.print("|MIN: "); cmd.print(EEPROM.read(p + 3)); //4 cmd.print("|X: "); cmd.println(EEPROM.read(p + 5)); //5 cmd.flush(); ac_2_log += 1; } } else { p = n * 5; cmd.print("Szuf."); cmd.print(n); cmd.print(" ->f(x): "); cmd.print(EEPROM.read(p + 1)); cmd.print(" |D: "); cmd.print(EEPROM.read(p + 2)); cmd.print("|H: "); cmd.print(EEPROM.read(p + 3)); cmd.print("|MIN: "); cmd.print(EEPROM.read(p + 4)); cmd.print("|X: "); cmd.println(EEPROM.read(p + 5)); cmd.flush(); ac_2_log += 1; } n++; } add_log(65, log_score, ac_2_log, 999); } else if (String("rm_esp_all_alarm") == cmd_request) { int n = 0; for (n = 1; n <= ((ile_alarmow + 1) * 5) - 1; n++) { eeprom_optimalization(n, 127, 0, 0); //EEPROM.commit(); if (n % 10 == 0) { cmd.print("Removed case from "); cmd.print(n - 10); cmd.print(" to "); cmd.println(n); } } //EEPROM.commit(); add_log(66, 1, 999, 999); } else if (cmd_request.startsWith("write_esp_eeprom=") || cmd_request.startsWith("wee=")) { uint8_t index_val_1; int cmd_val_1; uint8_t index_val_2; int cmd_val_2; index_val_1 = cmd_request.indexOf('='); cmd_val_1 = cmd_request.substring(index_val_1 + 1).toInt(); index_val_2 = cmd_request.indexOf(','); cmd_val_2 = cmd_request.substring(index_val_2 + 1).toInt(); if (cmd_val_1 > -1 && cmd_val_1 < 1000 && cmd_val_2 > -1 && cmd_val_2 < 256) { eeprom_optimalization(cmd_val_1, cmd_val_2, 0, 0); //EEPROM.commit(); } else { cmd.print("Error value! Val1 = Range (0-1000) / Val2 = Range(0-255)"); cmd.flush(); add_log(67, cmd_val_1, cmd_val_2, 0); return; } cmd.print("Writening to Esp eeprom case "); cmd.print(cmd_val_1); cmd.print(" value "); cmd.println(cmd_val_2); cmd.flush(); add_log(67, cmd_val_1, cmd_val_2, 1); } else if (cmd_request.startsWith("read_esp_eeprom=") || cmd_request.startsWith("ree=")) { uint8_t index_val_1; int cmd_val_1; index_val_1 = cmd_request.indexOf('='); cmd_val_1 = cmd_request.substring(index_val_1 + 1).toInt(); if (cmd_val_1 > -1 && cmd_val_1 < 1000) { cmd.print("Reading Esp eeprom case "); cmd.print(cmd_val_1); cmd.print(" = "); cmd.println(EEPROM.read(cmd_val_1)); //EEPROM.commit(); cmd.flush(); add_log(68, cmd_val_1, EEPROM.read(cmd_val_1), 1); } else { cmd.println("Wrong value range(0-1000)!"); cmd.flush(); add_log(68, cmd_val_1, 0, 0); } } else if (String("clear_arduino_eeprom") == cmd_request) { cmd.println("Clearing Arduino eeprom with value 127"); cmd.flush(); send_command(clear_ard_eeprom, 8); add_log(69, 1, 999, 999); } else if (String("clear_esp_eeprom") == cmd_request) { for (int n = 0; n < 4096; n++) { eeprom_optimalization(n, 127, 0, 1); } //EEPROM.commit(); add_log(70, 1, 999, 999); } else if (String("what's_time") == cmd_request || String("wt") == cmd_request) { //what_is_time[9] = {0x7E, 'W', 'H', 'a', '|', 'S', 0, 0x7F}; send_command(what_is_time, 8); add_log(71, 1, 999, 999); } else if (String("debug_help") == cmd_request || String("dh") == cmd_request) { cmd.clear(); cmd.flush(); cmd.println("Debuging option: "); cmd.println("write: debug_mode=x"); cmd.println(); cmd.println("______________"); cmd.println(" [0]-disable all debuging info"); cmd.flush(); cmd.println(" [1]-enable recive data debug"); cmd.println(" [2]-enable check control sum debug"); cmd.println(" [3]-enable send command debug"); cmd.println(" [4]-enable sync alarms debug"); cmd.println(" [5]-enable thingspeak data debug"); cmd.flush(); cmd.println(" [6]-enable feeding info debug"); cmd.println(" [7]-enable data about syncing pwm value"); cmd.println(" [8]-enable show free ram space"); cmd.println(" [9]-enable show add log result"); cmd.flush(); cmd.println(" [10]-enable check data is ok debug"); cmd.println(" [11]-enable send check is new day command debug"); cmd.flush(); /* bool D_recive_data_single_char = 0; bool D_check_control_sum = 0; bool D_sync_alarms = 0; bool D_send_command = 0; */ add_log(72, 1, 999, 999); } else if (cmd_request.startsWith("debug_mode") || cmd_request.startsWith("dm")) { uint8_t index_val_1; int cmd_val_1; index_val_1 = cmd_request.indexOf('='); cmd_val_1 = cmd_request.substring(index_val_1 + 1).toInt(); byte D_ok = 1; if (cmd_val_1 == 0) { D_recive_data_single_char = 0; D_check_control_sum = 0; D_sync_alarms = 0; D_send_command = 0; D_thingspeak_info = 0; D_feeding_info = 0; D_pwm_syncing_value = 0; D_show_ram = 0; D_add_log = 0; D_check_is_data_ok = 0; D_check_is_data_error = 0; D_checkday_change = 0; } else if (cmd_val_1 == 1) { D_recive_data_single_char = 1; } else if (cmd_val_1 == 2) { D_check_control_sum = 1; } else if (cmd_val_1 == 3) { D_send_command = 1; } else if (cmd_val_1 == 4) { D_sync_alarms = 1; } else if (cmd_val_1 == 5) { D_thingspeak_info = 1; } else if (cmd_val_1 == 6) { D_feeding_info = 1; } else if (cmd_val_1 == 7) { D_pwm_syncing_value = 1; } else if (cmd_val_1 == 8) { D_show_ram = 1; } else if (cmd_val_1 == 9) { D_add_log = 1; } else if (cmd_val_1 == 10) { D_check_is_data_ok = 1; D_check_is_data_error = 1; } else if(cmd_val_1 == 11) { D_checkday_change = 1; } else { cmd.println("Wrong argument!"); cmd.flush(); D_ok = 0; } add_log(73, cmd_val_1, D_ok, 999); } else if (String("feed_info") == cmd_request || String("fi") == cmd_request) { feeding_time = EEPROM.read(510); //510 retraction_time = EEPROM.read(511); //511 feeding_today = EEPROM.read(509); //509 F_pump_pin = EEPROM.read(508); cmd.print("1. Today feeding "); cmd.print(feeding_today); cmd.println(" times"); cmd.println(); cmd.flush(); cmd.println("2. Feeding settings: "); cmd.print(" a) Feeding time = "); cmd.print(feeding_time); cmd.println(" s"); cmd.print(" b) Retraction time = "); cmd.print(retraction_time); cmd.println(" s"); cmd.flush(); cmd.print(" c) Max fedding per day = "); cmd.println(max_daily_feeding_counter); cmd.flush(); cmd.print(" d) OFF this socket, when feeding (0-none/1-blue/2-red/3-all)= "); cmd.println(F_pump_pin); cmd.flush(); cmd.print(" e) time to turn on socket (min)= "); cmd.println(time_to_return); cmd.flush(); cmd.print(" f) feeding status: "); if (EEPROM.read(513) == 0) { cmd.println("Ok"); } else if (EEPROM.read(513) == 1) { cmd.println("No feeding today"); } else if (EEPROM.read(513) == 2) { cmd.println("Feeding is permanently Off"); } else { cmd.println("Error with value! out of range(0-2)"); add_log(74, 3, 999, 999); return; } cmd.flush(); add_log(74, EEPROM.read(513), 999, 999); } else if (cmd_request.startsWith("feed_sett=") || cmd_request.startsWith("fs=")) { uint8_t index_val_1; uint8_t cmd_val_1; uint8_t index_val_2; uint8_t cmd_val_2; index_val_1 = cmd_request.indexOf('='); cmd_val_1 = cmd_request.substring(index_val_1 + 1).toInt(); index_val_2 = cmd_request.indexOf(','); cmd_val_2 = cmd_request.substring(index_val_2 + 1).toInt(); if (cmd_val_1 == 1) { if (cmd_val_2 > 0 && cmd_val_2 < 30) { eeprom_optimalization(510, cmd_val_2, 1, 0); //EEPROM.commit(); feeding_time = EEPROM.read(510); cmd.print("Setting feeding time to "); cmd.print(feeding_time); cmd.println(" s"); cmd.flush(); add_log(75, 1, cmd_val_2, 1); } else { cmd.print("Wrong argument 1s - 29s"); cmd.flush(); add_log(75, 1, cmd_val_2, 0); } } else if (cmd_val_1 == 2) { if (cmd_val_2 > 0 && cmd_val_2 < 30) { eeprom_optimalization(511, cmd_val_2, 1, 0); //EEPROM.commit(); retraction_time = EEPROM.read(511); cmd.print("Setting retraction time to "); cmd.print(retraction_time); cmd.println(" s"); cmd.flush(); add_log(75, 2, cmd_val_2, 1); } else { cmd.print("Wrong argument 1s - 29s"); cmd.flush(); add_log(75, 2, cmd_val_2, 0); } } else if (cmd_val_1 == 3) { bool add_log75 = 1; if (cmd_val_2 == 1) { cmd.println("Setting socket number to blue, number 1"); eeprom_optimalization(508, 1, 1, 0); //EEPROM.commit(); F_pump_pin = EEPROM.read(508); } else if (cmd_val_2 == 2) { cmd.println("Setting socket number to red, number 2"); eeprom_optimalization(508, 2, 1, 0); //EEPROM.commit(); F_pump_pin = EEPROM.read(508); } else if (cmd_val_2 == 3) { cmd.println("Setting socket number to red and blue, number 3"); eeprom_optimalization(508, 3, 1, 0); //EEPROM.commit(); F_pump_pin = EEPROM.read(508); } else if (cmd_val_2 == 0) { cmd.println("Setting socket number to none, number 0"); eeprom_optimalization(508, 0, 1, 0); //EEPROM.commit(); F_pump_pin = EEPROM.read(508); } else { cmd.println("Error value! range(1-blue / 2-red / 3-red and blue / 0-none)"); add_log75 = 0; } add_log(75, 3, cmd_val_2, add_log75); } else if (cmd_val_1 == 4) { bool add_log75 = 1; if (cmd_val_2 > 0 && cmd_val_2 <= 60) { eeprom_optimalization(507, cmd_val_2, 1, 0); //EEPROM.commit(); time_to_return = EEPROM.read(507); cmd.print("Setting time_to_return value to "); cmd.print(time_to_return); cmd.println(" min"); } else { cmd.println("Wrong value. range(1-60)"); add_log75 = 0; } cmd.flush(); add_log(75, 4, cmd_val_2, add_log75); } else { cmd.println("Wrong function number: from 1 to 4"); cmd.flush(); add_log(75, 5, 999, 999); } } else if (String("feed_sett_info") == cmd_request || String("fsi") == cmd_request) { cmd.clear(); cmd.println(" feed_sett=x,y"); cmd.println(""); cmd.println("---------------------------------"); cmd.flush(); cmd.println(""); cmd.println("--> 1 - setting feeding time in seconds (y = 1-60)"); cmd.println(""); cmd.println("--> 2 - setting retraction time in seconds (y = 1-60)"); cmd.println(""); cmd.println("--> 3 - setting socket color to off when feeding (y: 0= none | 1= blue | 2= red | 3= all)"); cmd.flush(); add_log(76, 1, 999, 999); } else if (cmd_request.startsWith("set_display_brightnes=") || cmd_request.startsWith("sdb=")) { uint8_t index_val_1; uint8_t cmd_val_1; index_val_1 = cmd_request.indexOf('='); cmd_val_1 = cmd_request.substring(index_val_1 + 1).toInt(); if (cmd_val_1 > -1 && cmd_val_1 <= 3) { cmd.print("Setting display brightness to "); cmd.println(cmd_val_1); cmd.flush(); //set_disp_brightnes[8] = {0x7E, 'S', 'd', 0, 'B', '?', 0, 0x7F}; set_disp_brightnes[3] = cmd_val_1 + '0'; send_command(set_disp_brightnes, 8); add_log(77, cmd_val_1, 1, 999); } else { cmd.println("Wrong value - range(0-3)"); cmd.flush(); add_log(77, 4, 0, 999); } } else if (cmd_request.startsWith("reset_device=")) { uint8_t index_val_1; uint8_t cmd_val_1; index_val_1 = cmd_request.indexOf('='); cmd_val_1 = cmd_request.substring(index_val_1 + 1).toInt(); switch (cmd_val_1) { case 0: cmd.println("Resetting arduino..."); send_command(reset_ard, 9); add_log(78, cmd_val_1, 1, 999); break; case 1: cmd.println("Resetting esp..."); save_data(1); //save_data(0); delay(100); add_log(78, cmd_val_1, 1, 999); ESP.restart(); break; case 2: cmd.println("Resetting arduino and esp..."); send_command(reset_ard, 9); save_data(1); //save_data(0); delay(100); add_log(78, cmd_val_1, 1, 999); ESP.restart(); break; default: cmd.println("Wrong value! Range(0-2)(arduino/esp/all)"); add_log(78, 3, 0, 999); return; } } else if (cmd_request.startsWith("add_wifi=")) { uint8_t index_val_1 = cmd_request.indexOf('='); uint8_t cmd_val_1 = cmd_request.substring(index_val_1 + 1).toInt(); uint8_t index_val_2 = cmd_request.indexOf('-'); uint8_t index_val_3 = cmd_request.indexOf(','); String cmd_val_2 = cmd_request.substring(index_val_2 + 1, index_val_3); String cmd_val_3 = cmd_request.substring(index_val_3 + 1); if (cmd_val_1 == 1 || cmd_val_1 == 2) { int offset = 600 + (25 * (cmd_val_1 - 1)) + (cmd_val_1 - 1); for (int i = 0; i <= 25; i++) { eeprom_optimalization(offset + i, 0, 0, 1); } //EEPROM.commit(); byte len = cmd_val_2.length(); for (int i = 0; i < len; i++) { eeprom_optimalization(offset + i, cmd_val_2[i], 0, 0); } if (EEPROM.commit() == 1) { cmd.println("Writting ssid - OK!"); } else { cmd.println("Writting ssid - ERROR!"); add_log(79, cmd_val_1, 2, 999); return; } offset = 600 + (25 * (cmd_val_1 + 1)) + (cmd_val_1 + 1); for (int i = 0; i <= 25; i++) { eeprom_optimalization(offset + i, 0, 0, 0); } //EEPROM.commit(); len = cmd_val_3.length(); for (int i = 0; i < len; i++) { eeprom_optimalization(offset + i, cmd_val_3[i], 0, 0); } if (EEPROM.commit() == 1) { cmd.println("Writting pass - OK!"); } else { cmd.println("Writting pass - ERROR!"); add_log(79, cmd_val_1, 2, 999); return; } add_log(79, cmd_val_1, 1, 999); } else { cmd.println("Wrong value! Range(1-2)"); add_log(79, cmd_val_1, 0, 999); } } else if (cmd_request.startsWith("display_wifi=") || cmd_request.startsWith("dw=")) { uint8_t index_val_1 = cmd_request.indexOf('='); int cmd_val_1 = cmd_request.substring(index_val_1 + 1).toInt(); if (cmd_val_1 == 1 || cmd_val_1 == 2) { cmd.print("E_ssid->"); cmd.print(read_eeprom_wifi(cmd_val_1 - 1)); cmd.print("<- \ E_pass->"); cmd.print(read_eeprom_wifi(cmd_val_1 + 1)); cmd.println("<-"); cmd.flush(); } else if (cmd_val_1 == 0) { cmd.print("E_ssid->"); cmd.print(network0_E_ssid); cmd.print("<- \ E_pass->"); cmd.print(network0_E_pass); cmd.println("<-"); cmd.flush(); } else { cmd.println("Wrong value! Range(0-2)"); add_log(80, cmd_val_1, 0, 999); return; } add_log(80, cmd_val_1, 1, 999); } else if (String("Clear_all_wifi=AquaDzz@iadek19") == cmd_request) { for (int i = 600; i <= 703; i++) { eeprom_optimalization(i, 0, 0, 1); } //EEPROM.commit(); add_log(81, 1, 999, 999); } else if (String("daw") == cmd_request || String("display_all_wifi") == cmd_request) { for (int i = 0; i < 3; i++) { cmd.print(i); cmd.print("["); for (int j = 0; j <= 25; j++) { cmd.print((char)(EEPROM.read(600 + (25 * i) + i + j))); if (j != 25) { cmd.print(","); } } cmd.println("]"); cmd.flush(); } add_log(82, 1, 999, 999); } else if (cmd_request.startsWith("auto_feeding_off=") || cmd_request.startsWith("afo=")) { uint8_t index_val_1 = cmd_request.indexOf('='); byte cmd_val_1 = cmd_request.substring(index_val_1 + 1).toInt(); if (cmd_val_1 >= 0 && cmd_val_1 <= 2) { eeprom_optimalization(513, cmd_val_1, 1, 0); //EEPROM.commit(); if (cmd_val_1 == 0) { cmd.println("This function is off, feeding every day"); } else if (cmd_val_1 == 1) { cmd.println("feeding today is off, next feeding tomorrow"); } else if (cmd_val_1 == 2) { cmd.println("feeding every day is off, next feeding when this function will be off"); } else { cmd.println("Error"); add_log(83, cmd_val_1, 0, 999); return; } cmd.flush(); add_log(83, cmd_val_1, 1, 999); } else { cmd.println("Wrong value! range(0-2: no/one_day/every_day)"); add_log(83, 3, 0, 999); } } else if (cmd_request.startsWith("set_light_speed=") || cmd_request.startsWith("sls=")) { uint8_t index_val_1; uint8_t cmd_val_1; uint8_t index_val_2; uint8_t cmd_val_2; uint8_t sval_1 = 127; uint8_t sval_2 = 127; uint8_t sval_3 = 127; index_val_1 = cmd_request.indexOf('='); cmd_val_1 = cmd_request.substring(index_val_1 + 1).toInt(); index_val_2 = cmd_request.indexOf(','); cmd_val_2 = cmd_request.substring(index_val_2 + 1).toInt(); if (cmd_val_1 >= 1 && cmd_val_1 <= 4) { if (cmd_val_2 >= 0 && cmd_val_2 <= 255 && cmd_val_2 != 127) { //-------------------------------0----1----2---3---4---5---6----7---8---9---10--11-- //send_light_time_value[12] = {0x7E, 'L', 'I', 0, 'Q', 0, 'H', '|', 0, '@', 0, 0x7F}; if (cmd_val_1 == 1) { sval_1 = cmd_val_2; cmd.print("Setting led_step_button value to "); cmd.println(sval_1); } else if (cmd_val_1 == 2) { sval_2 = cmd_val_2; cmd.print("Setting led_step_app value to "); cmd.println(sval_2); } else if (cmd_val_1 == 3) { sval_3 = cmd_val_2; cmd.print("Setting led_step_alarm value to "); cmd.println(sval_3); } else if (cmd_val_1 == 4) { sval_1 = cmd_val_2; sval_2 = cmd_val_2; sval_3 = cmd_val_2; cmd.print("Setting led_step_button value to "); cmd.println(sval_1); cmd.print("Setting led_step_app value to "); cmd.println(sval_2); cmd.print("Setting led_step_alarm value to "); cmd.println(sval_3); } else { cmd.println("Error!"); } send_light_time_value[3] = sval_1; send_light_time_value[5] = sval_2; send_light_time_value[8] = sval_3; send_command(send_light_time_value, 12); } else { cmd.println("Wrong second value! Range(0-255) without 127"); } } else { cmd.println("Wrong value! Range (1-4)"); } cmd.flush(); add_log(84, cmd_val_1, cmd_val_2, 999); } else if (String("wifi_info") == cmd_request || String("wi") == cmd_request) { cmd.clear(); cmd.print("* Selected wifi id = "); cmd.println(network_id); if (network_id != 0) { cmd.print("* wifi eeprom SSID = "); cmd.println(read_eeprom_wifi(network_id - 1)); cmd.print("* wifi eeprom PASS = "); cmd.println(read_eeprom_wifi(network_id + 1)); } else { cmd.print("* wifi default SSID = "); cmd.println(network0_E_ssid); cmd.print("* wifi default PASS = "); cmd.println(network0_E_pass); } cmd.flush(); cmd.print("* wifi ssid value = "); cmd.println(E_ssid); cmd.print("* wifi pass value = "); cmd.println(E_pass); cmd.flush(); cmd.println("*******************************"); cmd.print("digitalRead P6 = "); cmd.println(pcf8574.digitalRead(wifi_network_sw_a)); cmd.print("digitalRead P7 = "); cmd.println(pcf8574.digitalRead(wifi_network_sw_b)); cmd.flush(); add_log(85, network_id, 999, 999); } else if (String("esp_info") == cmd_request || String("ei") == cmd_request) { cmd.clear(); cmd.print("* Firmware version: "); cmd.println(F_version); cmd.print("* EEPROM eeprom_optimalization writing succesful counter = "); cmd.println((EEPROM.read(514) << 8) + EEPROM.read(515)); cmd.print("* EEPROM eeprom_optimalization writing fail counter = "); cmd.println((EEPROM.read(516) << 8) + EEPROM.read(517)); cmd.println(ESP.getFlashChipId()); cmd.flush(); cmd.print("* Free heap: "); cmd.println(ESP.getFreeHeap()); cmd.print("* save log counter: "); cmd.println(save_log_counter()); cmd.print("* buffor log counter: "); cmd.println(log_buffor_counter()); cmd.print("* file name = "); cmd.println(read_file_name()); cmd.flush(); add_log(86, 1, 999, 999); } else if (String("display_log") == cmd_request || String("dl") == cmd_request) { //send_data_log = true; int dl_counter = display_log(); add_log(87, dl_counter, 999, 999); } else if (String("log_info") == cmd_request || String("li") == cmd_request) { //send_data_log = true; cmd.print("* LOGS counter = "); int li_counter = log_buffor_counter(); cmd.println(li_counter); cmd.print("* kolejka size = "); cmd.println(kolejka_size()); cmd.flush(); cmd.print("* kolejka empty = "); cmd.println(kolejka_empty()); cmd.print("* current_log_filename = "); cmd.println(current_log_filename); cmd.flush(); add_log(88, li_counter, kolejka_size(), (byte)kolejka_empty()); } else if (String("send_all_logs") == cmd_request) { cmd.println("ESP8266 will restart!"); EEPROM.write(522, 1); EEPROM.commit(); save_data(1); //save_data(0); delay(100); add_log(89, 1, 999, 999); ESP.restart(); } else if (cmd_request.startsWith("send_log=")) { uint8_t index_val_1; String cmd_val_1; index_val_1 = cmd_request.indexOf('='); cmd_val_1 = cmd_request.substring(index_val_1 + 1); if (!SD.begin(D8)) { send_mail_serial("initialization failed!", 1); add_log(32, 1, 999, 999); add_log(92, 0, 999, 999); return; } if (SD.exists(cmd_val_1)) { cmd.println("ESP8266 will restart!"); EEPROM.write(522, 2); EEPROM.commit(); save_file_name(cmd_val_1); cmd.println(read_file_name()); cmd.flush(); save_data(1); //save_data(0); delay(1000); ESP.restart(); } else { cmd.println("This file doesn't exist!"); cmd.flush(); } } else if (cmd_request.startsWith("change_filename=")) { uint8_t index_val_1; String cmd_val_1; index_val_1 = cmd_request.indexOf('='); cmd_val_1 = cmd_request.substring(index_val_1 + 1); save_file_name(cmd_val_1); delay(50); read_file_name(); } else if (String("sd_card_info") == cmd_request || String("sci") == cmd_request) { if (!SD.begin(D8)) { cmd.println("initialization failed!"); add_log(32, 1, 999, 999); delay(50); add_log(90, 0, 999, 999); return; } cmd.println("initialization done."); cmd.flush(); File root = SD.open("/"); printDirectory(root, 0); cmd.println("done!"); cmd.flush(); add_log(90, 1, 999, 999); } else if (cmd_request.startsWith("remove_file=")) { uint8_t index_val_1; String cmd_val_1; index_val_1 = cmd_request.indexOf('='); cmd_val_1 = cmd_request.substring(index_val_1 + 1); if (!SD.begin(D8)) { cmd.println("initialization failed!"); add_log(32, 1, 999, 999); return; } cmd.println("initialization done."); cmd.flush(); File root = SD.open("/"); if (!SD.exists(cmd_val_1)) { cmd.println("This file doesn't exist!"); cmd.flush(); add_log(91, 0, 999, 999); return; } SD.remove(cmd_val_1); if (!SD.exists(cmd_val_1)) { cmd.println("The file was deleted successfully !"); cmd.flush(); } else { cmd.println("The file could not be deleted !"); cmd.flush(); } add_log(91, 1, 999, 999); } else if (String("Remove_All_Files") == cmd_request) { if (!SD.begin(D8)) { cmd.println("initialization failed!"); add_log(32, 1, 999, 999); return; } cmd.println("initialization done."); cmd.flush(); File root = SD.open("/"); delay(100); rm(root, rootpath); if (!DeletedCount && !FailCount && !FolderDeleteCount) { add_log(17, DeletedCount, FailCount, 0); } else { cmd.print("Deleted "); cmd.print(DeletedCount); cmd.print(" file"); if (DeletedCount != 1) { cmd.print("s"); } cmd.print(" and "); cmd.print(FolderDeleteCount); cmd.print(" folder"); if (FolderDeleteCount != 1) { cmd.print("s"); } cmd.println(" from SD card."); if (FailCount > 0) { cmd.print("Failed to delete "); cmd.print(FailCount); cmd.print(" item"); if (FailCount != 1) { cmd.print("s"); } } add_log(17, DeletedCount, FailCount, 0); FailCount = 0; FolderDeleteCount = 0; DeletedCount = 0; } } else if (String("help") == cmd_request) { cmd.clear(); cmd.println("You can use this command: "); cmd.println("-------------------------------------------"); cmd.println("* cls"); cmd.println("* show_alarms or sha"); cmd.println("* alarms_counter or ac"); cmd.println("* display_alarm=[nr.szuf.] or da"); cmd.println("* set_alarm=[check in alarm_help or ah] or sa"); cmd.flush(); cmd.println("* rm_alarm=[remove alarm nr.] or rma"); cmd.println("* rm_all_alarm"); cmd.println("* millis_usage or mu"); cmd.println("* read_ard_eeprom=[nr.szuf.] or rae"); cmd.println("* write_ard_eeprom=x,y[szuf.0-1023/val.0-255] or wae"); cmd.println("* sync_time=[H:Min-D/M.Y]H=0-23|Min=0-59|Y=1-100"); cmd.flush(); cmd.println("* auto_sync_time or ast"); cmd.println("* Arduino_info=[check in arduino_info_help or aih] or ai"); cmd.println("* read_esp_alarm=[0-used/1-all] or rea"); cmd.println("* rm_esp_all_alarm"); cmd.println("* write_esp_eeprom=x,y[E.case 0-1023/val.0-255 or wee"); cmd.flush(); cmd.println("* read_esp_eeprom=x[nr.szuf.] or ree"); cmd.println("* clear_arduino_eeprom"); cmd.println("* clear_esp_eeprom"); cmd.println("* what's_time[arduino rtc time] or wt"); cmd.flush(); cmd.println("* debug_help or dh"); cmd.println("* debug_mode=[0/10] or dm"); cmd.println("* feed_info or fi"); cmd.println("* feed_sett=[check in feed_sett_info or fsi]"); cmd.println("* set_display_brightnes= x[x= 0-3] or sdb=x"); cmd.flush(); cmd.println("* add_wifi=x-y,z[nr.0-3 | ssid | pass]"); cmd.println("* display_wifi=x[nr.0-3]"); cmd.println("* reset_device=[0-arduino/1-esp/2-all]"); cmd.println("* display_all_wifi or daw"); cmd.println("* auto_feeding_off=[0-2:no/one_day/every_day]"); cmd.flush(); cmd.println("* set_light_speed=[x=1-4:button/app/alarm/all | y=0-100] or sls"); cmd.println("* wifi_info or wi"); cmd.println("* esp_info or ei"); cmd.println("* display_log or dl"); cmd.println("* log_info or li"); cmd.flush(); cmd.println("* clear_all_logs"); cmd.println("* sd_card_info or sci"); cmd.println("* send_all_logs"); cmd.println("* send_log=(log name, log0001.txt)"); cmd.flush(); cmd.println("* change_filename=(log name, log0001.txt)"); cmd.println("* remove_file=(filename - L0001.txt) or Remove_All_Files"); cmd.println("-------------------------------------------"); cmd.flush(); add_log(100, 1, 999, 999); } else { cmd.print("Wrong command: "); cmd.write(param.getBuffer(), param.getLength()); cmd.println(" <- Try command: help"); add_log(100, 0, 999, 999); } cmd.flush(); } Jak to działa? Istnieją dwie opcje, użytkownik wpisał poprawną komendę lub wpisał nie znaną komendę. W pierwszym przypadku wykonuje się fragment kodu przypisany do danej komendy, np. komenda cls odpowiada za wyczyszczenie terminala. Jeżeli podano nieznaną komendę, to na terminalu wyświetli się napis Wrong command: (podana komenda) <- Try command: help. Zaprogramowałem także coś w stylu spisu treści, po wpisaniu komendy help, na terminalu pojawią się wszystkie dostępne komendy oraz ich parametry. Spis komend: * cls * show_alarms or sha * alarms_counter or ac * display_alarm=[nr.szuf.] or da * set_alarm=[check in alarm_help or ah] or sa * rm_alarm=[remove alarm nr.] or rma * rm_all_alarm * millis_usage or mu * read_ard_eeprom=[nr.szuf.] or rae * write_ard_eeprom=x,y[szuf.0-1023/val.0-255] or wae * sync_time=[H:Min-D/M.Y]H=0-23|Min=0-59|Y=1-100 * auto_sync_time or ast * Arduino_info=[check in arduino_info_help or aih] or ai * read_esp_alarm=[0-used/1-all] or rea * rm_esp_all_alarm * write_esp_eeprom=x,y[E.case 0-1023/val.0-255 or wee * read_esp_eeprom=x[nr.szuf.] or ree * clear_arduino_eeprom * clear_esp_eeprom * what's_time[arduino rtc time] or wt * debug_help or dh * debug_mode=[0/10] or dm * feed_info or fi * feed_sett=[check in feed_sett_info or fsi] * set_display_brightnes= x[x= 0-3] or sdb=x * add_wifi=x-y,z[nr.0-3 | ssid | pass] * display_wifi=x[nr.0-3] * reset_device=[0-arduino/1-esp/2-all] * display_all_wifi or daw * auto_feeding_off=[0-2:no/one_day/every_day] * set_light_speed=[x=1-4:button/app/alarm/all | y=0-100] or sls * wifi_info or wi * esp_info or ei * display_log or dl * log_info or li * clear_all_logs * sd_card_info or sci * send_all_logs * send_log=(log name, log0001.txt) * change_filename=(log name, log0001.txt) * remove_file=(filename - L0001.txt) or Remove_All_Files Logi: Dodałem tę funkcję jedynie w celach edukacyjnych (chciałem przetestować, czy tworzenie logów ma jakikolwiek sens oraz czy jest to trudne). Okazało się jednak, że logi mogą być pomocne, dzięki nim mogłem testować, czy alarmy wykonują się poprawnie o zadanej godzinie, dlaczego i kiedy resetuje się samoczynnie sterownik itp. Tworzenie logów polega na zapisywaniu w buforze w pamięci RAM informacji o wykonaniu się jakiegoś zdarzenia, o czasie wykonania się tego zdarzenia oraz o jego parametrach. Przykładowo, zauważyłem, że karta sieciowa resetuje się samoczynnie co jakiś czas. Dzięki zapisanym logą mogę dowiedzieć się kiedy dokładnie sterownik zresetował się, (znam moment zakończenia się funkcji setup) oraz przeanalizować co mogło taki reset wykonać. void setup() { ... add_log(48, datalog_d, datalog_m, datalog_y); add_log(30, 1, 999, 999); //Karta sieciowa wystartowała check_day_change(); } Jestem w stanie sprawdzić dzień, godzinę i minutę wykonania się praktycznie każdego elementu programu sterownika lub karty sieciowej, co jest bardzo pomocne. Każdy log składa się z 8 bajtów danych: D- dzień | H- godzina | M- minuta | A- co to jest za log | Val1_lB / Val1_hB- zapisany na maksymalnie dwóch bajtach parametr | Val2- drugi parametr | Val3- trzeci, ostatni parametr przykładowy log: 100->D19H20M14A1&1#127, 127 Taki log mówi mi, że 19-stego dnia miesiąco o godzinię 20:14 ktoś wcisnął na obudowie sterownika przycisk niebieski i teraz stan na przekaźniku nr.1 jest wysoki. Wiem także, że nie podano 2 i 3 parametru ponieważ mają one wartość 127, nie wiem dokładnie dlaczego wybrałem tą liczbę a nie na przykład 255, ale stosuję tą zasadę w mechanizmie logów oraz podczas odczytywania alarmów. 100-> oznacza, że jest to setny zapisany log od czasu wyzerowania licznika logów, ten licznik zapisywany jest także w pamięci "EEPROM". Spis wszystkich logów: LOG ACTION: -> ARDUINO <- * 1 - blue switch was press in arduino (val1 = 1[on] / 2[off]) * 2 - red switch was press in arduino (val1 = 1[on] / 2[off]) * 3 - green switch was press in arduino (val1 = 0-10) * 4 - feeding button was press in arduino (val1 = None) * 5 - arduino was lock feeding option * 6 - arduino execute alarm (val1 = alarm number | val2 = alarm value) * 7 - arduino was reset * 8 - The arduino eeprom has been cleared -> ESP <- * 17 - delete all sd card files (val1= rm_file counter \ val2 = fail_counter \ val3 = status[1-ok/0-error]) * 18 - repair data function is running (val1=val2repair|val2=1-repair_automaticly/2-repair_manual/0-error value) * 19 - sending error data value log (val1 = 1) * 20 - V0 button has been pressed (val1 = 1[on] / 2[off]) * 21 - V1 button has been pressed (val1 = 1[on] / 2[off]) * 22 - v2 led pwm slider (val1 = 0-10) * 23 - feeding button has been pressed * 24 - feeding has been ended * 25 - feeding has been cancled (val1 = 1[to much feeding] | 2[feeding is off today] | 3[feeding is off] | 4[feeding is now running]) * 26 - setting alarm (val1 = number of allarm | val2 = function) * 27 - executing terminal command (val1 = command number | val2 = ?) * 28 - clear all logs * 29 - log_counter (val1 = counter) * 30 - esp8266 boot up succesful val1 =1 * 31 - write to esp eeprom (val1 = eeprom case | val2 = value | val3 = 0-error value/1-safe_mode_trigered/2-eeprom write/3-eeprom don't write) * 32 - sd card initialization failed * 33 - email was send * 34 - save log on sd card (val1= 0 Can't create file!/ 1-save ok /) val2 = send_counter * 35 - recived button blue change state command (val1 = 1-on/0-off) * 36 - recived button red change state command (val1 = 1-on/0-off) * 37 - recived button green change state command (val1 = 0-10) * 38 - recived button yellow press - feeding (val1 = 1-feeding_ok/0-feeding error/2-feeding_is_now_run | val2= feeding_today_counter) * 39 - It's new day (val1 = yesterday | val2 = today) * 40 - resetting esp trigered by alarm (val1=1) * 41 - syncing time trigered by alarm (val1 = 1) * 42 - recived alarm synchronized counter from arduino (val1 = counter) * 43 - recived millis from arduino (val1 = ard_millis) * 44 - recived arduino eeprom case value (val1 = arduino eeprom case / val2 = eeprom value) * 45 - recived send to esp eeprom succesful writing counter (1-eeprom succesful writing counter|2-eeprom fail writing counter|?-wrong value) * 46 - recived and printing in cmd (val1 = funkcja | val2 = day | val3 = value) * 47 - recived arduino rtc time (val1 = hour | val2 = day | val3 = week day) * 48 - changed log filename (val1 = day | val2 = month | val3 = year) * 49 - save_file_name (val1 = 1-ok/0-error) 51 - Action = cls | val1 = 1 | val2 = None | val3 = None 52 - Action = show_alarms | val1 = cmd_val | val2 = None | val3 = None 53 - Action = alarms_co unter | val1 = e_count | val2 = None | val3 = None 54 - Action = display_alarm | val1 = ter_val | val2 = None | val3 = None 55 - Action = alarm_help | val1 = 1 | val2 = None | val3 = None 56 - Action = rm_alarm | val1 = 1(done)/0(error) | val2 = None | val3 = None 57 - Action = rm_all_alarm | val1 = 1 | val2 = None | val3 = None 58 - Action = millis_usage | val1 = millis_esp | val2 = None | val3 = None 59 - Action = read_ard_eeprom | val1 = arduino_eeprom_case | val2 = 1(done)/0(error) | val3 = None 60 - Action = write_ard_eeprom | val1 = arduino_eeprom_case | val2 = value_to_write | val3 = 1(done)/0(error) 61 - Action = sync_time | val1 = godzina | val2 = minuta | val3 = dzien 62 - Action = auto_sync_time | val1 = godzina | val2 = minuta | val3 = dzien 63 - Action = Arduino_info | val1 = cmd_val_1 | val2 = None | val3 = None 64 - Action = arduino_info_help | val1 = 1 | val2 = None | val3 = None 65 - Action = read_esp_alarm | val1 = 0(only alarm)/1(all eeprom cases)/?(error, wrong value 0 or 1) | val2 = ile_alarmow_odczytano | val3 = None 66 - Action = rm_esp_all_alarm | val1 = 1 | val2 = None | val3 = None 67 - Action = write_esp_eeprom | val1 = EEPROM case | val2 = value to write | val3 = 0(error!Wrong value 1 or 2)/1(Done) 68 - Action = read_esp_eeprom | val1 = EEPROM case | val2 = read value | val3 = 0(error!Wrong eeprom case)/1(Done) 69 - Action = clear_arduino_eeprom | val1 = 1 | val2 = None | val3 = None 70 - Action = clear_esp_eeprom | val1 = 1 | val2 = None | val3 = None 71 - Action = what's_time | val1 = 1 | val2 = None | val3 = None 72 - Action = debug_help | val1 = 1 | val2 = None | val3 = None 73 - Action = debug_mode | val1 = debug_option | val2 = (0-error value/1-ok) | val3 = None 74 - Action = feed_info | val1 = (0-feeding\1-not today\2-off\3-wrong value) | val2 = None | val3 = None 75 - Action = feed_sett | val1 = 1-5 | val2 = cmd_val_2 | val3 = 1(Done)/0(Error) 76 - Action = feed_sett_info | val1 = 1 | val2 = None | val3 = None 77 - Action = set_display_brightnes | val1 = 0-3 | val2 = 1(Done)/0(Error) | val3 = None 78 - Action = reset_device | val1 = 0-2 | val2 = 1(Done)/0(Error) | val3 = None 79 - Action = add_wifi | val1 = wifi_number | val2 = 1(Done)/0(Error)/2(commit error) | val3 = None 80 - Action = display_wifi | val1 = number of wifi to displayed | val2 = 1(Done)/0(Error) | val3 = None 81 - Action = Clear_all_wifi=AquaDzz@iadek19 | val1 = 1 | val2 = None | val3 = None 82 - Action = display_all_wifi | val1 = 1 | val2 = None | val3 = None 83 - Action = auto_feeding_off | val1 = 1 | val2 = None | val3 = None 84 - Action = set_light_speed | val1 = cmd_val_1 | val2 = cmd_val_2 | val3 = None 85 - Action = wifi_info | val1 = network_id | val2 = None | val3 = None 86 - Action = esp_info | val1 = 1 | val2 = None | val3 = None 87 - Action = display_log | val1 = display_log_counter | val2 = None | val3 = None 88 - Action = log_info | val1 = log_counter | val2 = kolejka_size | val3 = kolejka_empty 89 - Action = send_all_logs | val1 = 1 | val2 = None | val3 = None 90 - Action = sd_card_info | val1 = (1-sd card read ok\ 0-sd card read error) | val2 = None | val3 = None 91 - Action = remove_file | val1 = 1-delete ok/0-error | val2 = None | val3 = None 92 - Action = send_log | val1 = 1-ok/0-error | val2 = None | val3 = None 93 - Action = | val1 = 1 | val2 = None | val3 = None 94 - Action = | val1 = 1 | val2 = None | val3 = None 95 - Action = | val1 = 1 | val2 = None | val3 = None 96 - Action = | val1 = 1 | val2 = None | val3 = None 97 - Action = | val1 = 1 | val2 = None | val3 = None 98 - Action = | val1 = 1 | val2 = None | val3 = None 99 - Action = | val1 = 1 | val2 = None | val3 = None 100 - Action = help or kommand unknown | val1 = (1-help ok/0-command unknown) | val2 = None | val3 = None Funkcja dodająca logi: void add_log(byte action, int val1, int val2, int val3) { /* if (kolejka_size() >= 400) { cmd.println("Buffor is full! Saving data on sd card!"); cmd.flush(); save_data(1); //save_data(0); } */ if (val1 < 0 || val1 > 65000) { cmd.print("Error! Log_add function - val1 out of range(0-65000) ->"); cmd.println(val1); cmd.flush(); return; } byte val1_hb = 0, val1_lb = 0; val1_hb = highByte(val1); val1_lb = lowByte(val1); byte log_D = day(); byte log_H = hour(); byte log_M = minute(); if (log_D < 0 || log_D > 31) { cmd.print("Error! add_log function - problem with day() value! Out of range(0-31) ->"); cmd.println(log_D); return; } if (log_H < 0 || log_H > 23) { cmd.print("Error! add_log function - problem with hour() value! Out of range(0-23) ->"); cmd.println(log_H); return; } if (log_M < 0 || log_M > 59) { cmd.print("Error! add_log function - problem with minute() value! Out of range(0-59) ->"); cmd.println(log_M); return; } int push_case = kolejka_push(log_D); kolejka_push(log_H); kolejka_push(log_M); kolejka_push(action); kolejka_push(val1_hb); kolejka_push(val1_lb); if (val2 > 255 || val2 < 0) { kolejka_push(127); } else { kolejka_push(val2); } if (val3 > 255 || val3 < 0) { kolejka_push(127); } else { kolejka_push(val3); } if (D_add_log == 1) { cmd.print("Setting log case "); cmd.print(push_case / 8); cmd.print("-"); cmd.print(kolejka_size()); cmd.print(" at "); cmd.print(message_aray[push_case + 1]); cmd.print(":"); cmd.flush(); cmd.print(message_aray[push_case + 2]); cmd.print(", day "); cmd.print(message_aray[push_case]); cmd.print(" -> Action "); cmd.print(message_aray[push_case + 3]); cmd.print(" with val1: "); cmd.flush(); cmd.print((message_aray[push_case + 4] << 8) + message_aray[push_case + 5]); cmd.print(" with val2: "); cmd.print(message_aray[push_case + 6]); cmd.print(" with val3: "); cmd.println(message_aray[push_case + 7]); cmd.flush(); } } Zapisywanie danych na karcie SD: Tworzenie logów już działa, ale co z ich przechowywaniem w pamięci RAM? Jak wiadomo pamięć RAM kiedyś się skończy, nie mogę więc zapisywać w niej setek czy tysięcy logów, kolejnym problemem jest to, że po utracie zasilania wszystkie moje logi przepadną. W pierwszym momencie planowałem zapisywać logi w pamięci EEPROM, szybko zrezygnowałem jednak z tego pomysłu, ponieważ ograniczał mnie rozmiar tej pamięci. Zdecydowałem się na użycie karty pamięci jako magazynu na dane. Nie zrezygnowałem jednak z buffera na dane, powodem tej decyzji jest to, że dla karty SD nie jest zdrowe ciągłe otwieranie i zamykanie plików, np. 30 razy na minutę, 24 godziny na dobę przez cały rok. Lepszym pomysłem jest hurtowe zapisywanie kilku logów podczas jednego otwarcia i zamknięcia pliku. Aktualnie zapisuję na karcie pamięci 75 logów na jedno otwarcie pliku, buffer może pomieścić maksymalnie 110 logów, mam więc zapas w razie problemu z zapisem w danej chwili. wywołanie funkcji zapisującej na karcie SD: if (kolejka_size() >= 600) { save_data(1); } Co 5 sekund urządzenie sprawdza czy w bufferze znajduje się więcej niż 74 logi (ponieważ 600 komórek / 8 komórek na log = 75 logów). Jeżeli tak to następuje próba zapisu. Próba zapisania danych występuje także podczas resetowania się karty sieciowej przy użyciu alarmu lub komendy (sterownik główny lub karta sieciowa mogą resetować się auto magicznie, jeżeli ustawie taki alarm), ma to na celu zapobieganie przed utraceniem danych. funkcja zapisująca dane: void save_data(bool datafile_all) { if (!SD.begin(D8)) { cmd.println("initialization failed!"); cmd.flush(); add_log(32, 1, 999, 999); return; } else { sd_card_work = true; } long log_counter = readLongFromEEPROM(518); cmd.println("Saving data from buffer to sd card!"); cmd.print("Saving "); cmd.print(kolejka_size()); cmd.println(" logs !"); cmd.print("Log name = "); cmd.println(current_log_filename); cmd.print("Log counter = "); cmd.println(log_counter); cmd.flush(); File dataFile; String full_datalog_name = "DATALOG.txt"; if (datafile_all == 1) { full_datalog_name = current_log_filename; } if (!SD.exists(full_datalog_name)) { cmd.println(full_datalog_name + " doesn't exist! Creating file in progress... "); dataFile = SD.open(full_datalog_name, FILE_WRITE); dataFile.close(); if (!SD.exists(full_datalog_name)) { cmd.println("Can't create file!"); cmd.flush(); add_log(34, 0, 999, 999); return; } else { cmd.println("File was created!"); cmd.flush(); } } else { cmd.println("File " + full_datalog_name + " existing!"); cmd.flush(); } log_counter = readLongFromEEPROM(518); byte wd = 0; String dataString = ""; int send_counter = 0; byte k_size = kolejka_size(); while (kolejka_empty() == false) { if (wd == 0) { dataString = ""; dataFile = SD.open(full_datalog_name, FILE_WRITE); if (!dataFile) { cmd.println("Error When opening file " + full_datalog_name); dataFile.close(); return; } } dataString += String(log_counter); dataString += "->D"; dataString += String(kolejka_pop()); dataString += 'H'; dataString += String(kolejka_pop()); dataString += 'M'; dataString += String(kolejka_pop()); dataString += 'A'; dataString += String(kolejka_pop()); dataString += '&'; byte war1, war2; war1 = kolejka_pop(); war2 = kolejka_pop(); int wartosc = (war1 << 8) + war2; dataString += String(wartosc); dataString += '#'; dataString += String(kolejka_pop()); dataString += '$'; dataString += String(kolejka_pop()); dataString += '\n'; send_counter++; if (wd == 25 || kolejka_empty() == true) { dataFile.println(dataString); cmd.println("Writing " + full_datalog_name + " OK!"); dataFile.close(); dataFile = SD.open("DATALOG.txt", FILE_WRITE); if (!dataFile) { cmd.println("Error When opening file DATALOG.txt"); dataFile.close(); return; } dataFile.println(dataString); cmd.println("Writing DATALOG.txt OK!"); dataFile.close(); wd = -1; } wd++; log_counter++; } writeLongIntoEEPROM(518, log_counter); cmd.print("Log counter -> "); cmd.println(readLongFromEEPROM(518)); cmd.print("Succesful save "); cmd.print(send_counter); cmd.println(" logs"); cmd.flush(); if (dataString.length() > 0) { cmd.println("String is no empty!"); cmd.flush(); dataFile.close(); dataFile = SD.open(full_datalog_name, FILE_WRITE); if (!dataFile) { cmd.println("Error When opening file " + full_datalog_name); dataFile.close(); return; } dataFile.println(dataString); cmd.println("Writing " + full_datalog_name + " OK!"); dataFile.close(); dataFile = SD.open("DATALOG.txt", FILE_WRITE); if (!dataFile) { cmd.println("Error When opening file DATALOG.txt"); dataFile.close(); return; } dataFile.println(dataString); cmd.println("Writing DATALOG.txt OK!"); dataFile.close(); } SD.end(); add_log(34, 1, send_counter, 999); } Na początku sprawdzam, czy mogę odczytać / zapisać coś na karcie pamięci, jeżeli tak to wypisuje na terminalu w aplikacji Blynk informację o nazwie pliku do którego zostaną zapisane dane, ilość zapisywanych logów, licznik wszystkich zapisanych logów. Następnie sprawdzane jest czy plik o podanej nazwie istnieje, ta nazwa powstaje podczas uruchamiania się karty sieciowej na podstawie aktualnej daty (przykładowa nazwa L010321.txt). Jeżeli nie istnieje, to jest tworzony. Kolejnym krokiem jest odczytanie wszystkich komórek z buffera, zapisując je jednocześnie w postaci stringów z dodanymi przerywaczami w postaci litery i znaków na karcie SD. Na koniec na terminalu wypisuje status zapisu, zamykam plik, dodaje log z informacją o zapisie oraz liczbie zapisanych logów. Wysyłanie danych w wiadomości email: Podczas programowania tego urządzenia chciałem w jakiś sposób przesyłać dane z karty pamięci na zewnętrzny serwer, pocztę, dysk google lub jako dane w tabelkach w google spreedsheet online. Testowałem właśnie to ostatnie rozwiązanie, korzystałem z tego poradnika. Działało to dość słabo, bez problemu mogłem wysyłać dane do 7 komórek na raz (jeden alarm), ale wysyłanie trwało około 5 sekund. Wysłanie 75 alarmów zajęło by około 7 minut, niestety nie był to jedyny problem. Podczas hurtowego wysyłania alarmów, esp często się zawieszało lub niektóre alarmy były pomijane. Zrezygnowałem więc z tego pomysłu. Kolejnym pomysłem było wysyłanie logów jako stringi w wiadomości email, postanowiłem użyć widżetu email z aplikacji Blynk. Pozwala on na proste wysyłanie wiadomości na podane adres email (nadal korzystam z tego rozwiązania podczas wysyłania kodów z błędami). Jedną z wad tego rozwiązania było ograniczenie do maks. 1200 znaków na wiadomość, jeden alarm może składać się maksymalnie z 33 znaków (np. 34432->D28H13M44A45&12345#127$127), czyli teoretycznie w jednej wiadomości mogę wysłać 36 logów, w praktyce mniej ponieważ tytuł wiadomość też zabiera znaki. Pomyślałem, że mogę wysyłać właśnie tak pocięte wiadomości odczytywane z karty SD, jednak jest to trochę karkołomne rozwiązanie. W jeden dzień jestem w stanie wyprodukować nawet 600 logów, wysyłanie ich wszystkich na pocztę jest trochę mało wygodne. Szukałem rozwiązania pozwalającego na wysłanie pliku txt na pocztę lub serwer, i tak tworze taki plik na karcie SD, więc dlaczego go nie wykorzystać. Znalazłem projekt EMailSender, jest moim zdaniem świetny. Pozwala on na przesyłanie różnych plików, np. .txt, .jpg na pocztę gmail. Działa on na esp8266, esp32, czy też arduino z podłączoną kartą sieciową. Pliki na poczcie otrzymuję jako załącznik. Jedyną wadą jest długi czas wysyłani się plików. Podczas wysyłania plików na pocztę, karta sieciowa resetuje się i wchodzi w tryb wysyłania wiadomości. Podczas tego trybu aplikacja Blynk ani inne funkcje karty sieciowej nie działają, działa jednak wysyłanie komend do sterownika głównego. Zmodyfikowałem trochę bibliotekę EMailSender, dzięki temu mogę obliczyć ile procent wiadomości zostało wysłane, te informację wysyłam do sterownika głównego, a on pokazuje je na wyświetlaczu. Podczas wysyłania wiadomości na serial monitorze mogą pojawić się dane, np. o błędzie podczas wysyłania, błędzie podczas logowania się lub informacja o pomyślnym wysłaniu wiadomości, wszystkie te dane zapisuję w bufferze, a po połączeniu się esp z aplikacją Blynk, wyświetlam je na terminalu. void send_mail(bool send_own_file) { String datafile_name = "DATALOG.txt"; send_mail_serial("Starting!", 1); send_mail_serial("Initializing SD card...", 1); if (!SD.begin(D8)) { send_mail_serial("initialization failed!", 1); add_log(32, 1, 999, 999); return; } if (send_own_file == 1) { datafile_name = read_file_name(); } send_mail_serial("initialization done.", 1); File root = SD.open("/"); delay(100); if (WiFi.status() != WL_CONNECTED) { static uint16_t attempt = 0; send_mail_serial("Connecting to ", 0); WiFi.begin(E_ssid.c_str(), E_pass.c_str()); send_mail_serial(String(E_ssid.c_str()), 1); uint8_t i = 0; while (WiFi.status() != WL_CONNECTED && i++ < 50) { delay(200); send_mail_serial(".", 0); } ++attempt; send_mail_serial("", 1); if (i == 51) { send_mail_serial("Connection: TIMEOUT on attempt: ", 0); send_mail_serial(String(attempt), 1); if (attempt % 2 == 0) send_mail_serial("Check if access point available or SSID and Password", 1); return; } send_mail_serial("Connection: ESTABLISHED", 1); //send_mail_serial("Got IP address: ", 0); //send_mail_serial(String(WiFi.localIP()), 1); } byte datalog_d, datalog_m, datalog_y; datalog_d = EEPROM.read(523); datalog_m = EEPROM.read(524); datalog_y = EEPROM.read(525); String dd_str = ""; if (datalog_d < 10) { dd_str = "0"; } dd_str += String(datalog_d); String dm_str = ""; if (datalog_m < 10) { dm_str = "0"; } dm_str += String(datalog_m); String dy_str = ""; if (datalog_y < 10) { dy_str = "0"; } dy_str += String(datalog_y); current_log_filename = "L" + dd_str + dm_str + dy_str + ".txt"; EMailSender::EMailMessage message; message.subject = "Logi z sterownika"; message.message = "Pobierz plik z logami :)<br>"; message.mime = MIME_TEXT_PLAIN; byte message_size = 0; if (send_own_file == 0) { message_size = 2; } else { message_size = 1; } EMailSender::FileDescriptior fileDescriptor[message_size]; fileDescriptor[0].filename = (datafile_name); fileDescriptor[0].url = ("/" + datafile_name); fileDescriptor[0].storageType = EMailSender::EMAIL_STORAGE_TYPE_SD; if (message_size > 1) { fileDescriptor[1].filename = (current_log_filename); fileDescriptor[1].url = ("/" + current_log_filename); fileDescriptor[1].storageType = EMailSender::EMAIL_STORAGE_TYPE_SD; } EMailSender::Attachments attachs = {message_size, fileDescriptor}; EMailSender::Response resp = emailSend.send("jkgrzelak1@gmail.com", message, attachs); send_mail_serial("Sending status: ", 1); send_mail_serial(String(resp.status), 1); send_mail_serial(String(resp.code), 1); send_mail_serial(String(resp.desc), 1); SD.end(); EEPROM.write(522, 3); EEPROM.commit(); add_log(33, 1, 999, 999); } kod karty sieciowej (Zalecam użyć dołączonych prze zemnie bibliotek): kod_karty_sieciowej.zip Automatyczny karmnik: Do budowy automatycznego karmnika wykorzystałem projekt ze strony thingiverse. Dodałem tylko własną obudowę na silnik oraz zrezygnowałem z górnego mieszalnika. W środku obudowy znajduje się: mostek h (LM293D), odpowiada on za sterowanie silniczkiem (4,5V) z przekładnią z zabawki 3 diody led, pierwsza odpowiada za informowanie o zasilaniu karmnika (odłączyłem ją ponieważ niepotrzebnie świeciła cały dzień), 2 i 3 dioda świeci odpowiednio podczas procesu podawania pokarmu lub powrotu do pojemnika Do działania karmnika potrzebny jest także stabilizator napięcia 12v na 5v, znajduję się on jednak poza obudową karmnika. Postanowiłem umieścić go w małej prostokątnej obudowie wraz z bezpiecznikiem i gniazdem LAN. Tak karmnik jest połączony ze sterownikiem głównym poprzez moduł z gniazdem LAN na skrętce komputerowej Wykorzystuje tam 6 żył na zasilanie oraz po jednej żyle na sterowanie mostkiem h. Aktualnie moduł karmnika oraz moduł z gniazdem LAN dla jego zasilania, odłączone są od sterownika głównego (mam zamiar wykorzystać je w swoim sterowniku). Obudowa: Obudowa sterownika głównego to puszka natynkowa hermetyczna z wyciętym otworem wentylacyjnym na wieczku oraz przyklejonej w tym miejscu kratki wydrukowanej na drukarce 3D, otworem na bezpiecznik z prawej strony, otworem do podwójnego gniazdka 230v, otworem i ramką dla wyświetlacza oraz otworami dla przycisków w wieczku obudowy. Obudowa dla karty sieciowe została w pełni wydrukowana na drukarce 3D, jest ona dość spora, ale i tak ledwo zmieściłem się ze wszystkimi potrzebnymi elementami. W pierwotnej wersji z sterownika głównego wystawał przymocowany na krótkim przewodzie prostokątny moduł z elektroniką sterującą automatycznym karmnikiem, znajdował się w niej bezpiecznik, stabilizator napięcia oraz złącze do podłączenia karmnika na długim przewodzie. Usunąłem go jednak, ponieważ dziadek nie korzystał z karmnika Nie jestem zadowolony z finalnego efektu, następnym razem wykorzystam dużo większą obudowę w której zmieszczę całą elektronikę sterownika głównego oraz karty sieciowej. Podsumowanie Jestem zadowolony z działania tego urządzenia, podczas testowania go w swoim akwarium z rybami nie sprawiał mi problemów. Co nie wyszło? moim zdaniem większość kodu mogła by być napisana lepiej, aktualnie widzę kilka błędów np. możliwość wykonania się tylko jednego alarmu na minutę, brak odpowiednich zabezpieczeń podczas wpisywania komend w terminalu, lekki bałagan w kodzie Moduł karmnika powinien być podłączony do sterownika a nie do karty sieciowej, takie podłączenie czasami uniemożliwia karmienie podczas braku dostępu do internetu lub podczas startu karty sieciowej Wysyłanie kilku dniowych plików z logami trwa nawet kilka godzin, nie jest to jednak do końca wada, ponieważ mogę wysyłać taki plik w nocy Automatyczny karmnik nie jest wykorzystywany przez dziadka, trochę mu nie ufa wygląd sterownika jest moim zdaniem ok, ale wygląd karty sieciowej jest dość śmieszny, tym bardziej doklejonej na gorący klej anteny od routera podczas testów nigdy nie miałem problemów z działaniem karty sieciowej nawet gdy była oddalona od routera w lini prostej na 15 metrów przez 3 ściany i podłogę, jednak podczas próby uruchomienia całego urządzenia u dziadka okazało się, że nowy router z światłowodem oddalony o 6 metrów w linii prostej przez 3 ściany nie jest w stanie połączyć się z kartą sieciową, problemem mogły być także inne sieci WiFi ponieważ mieszkanie znajduje się w bloku. Dlatego na szybko musiałem dodać zewnętrzną antenę ze starego routera TP-linka, po tej modyfikacji wszystko działa bez problemu Co wyszło? Urządzenie jest stabilne, nie zdarzają się mu już samo resety lub zawieszanie się Sterownik główny jest autonomiczny, działa nawet kiedy karta sieciowa jest wyłączona Alarmy działają bez problemu Pasek LED podłączony do sterownika działa bardzo dobrze, powoli rozjaśniające i ściemniające się światło nie straszy ryb Logi tworzone przez sterownik pozwalają na sprawdzenie co działa nie tak jak powinno, jest to moim zdaniem fajne narzędzie które będę używać w przyszłych projektach Najważniejsza rzecz, mój dziadek jest zadowolony z działania tego urządzenia, nie musi już codziennie kilka razy sięgać do listwy i wkładać/wyciągać wtyczkę od pompki, napowietrzacza oraz zasilacza do oświetlenia LED Plany na przyszłość Podczas budowy i programowania tego urządzenia nauczyłem się sporo rzeczy, aktualnie pracuję nad czymś w stylu uniwersalnego sterownika do oświetlenia zewnętrznego / systemu nawadniania / sterownika do mojego akwarium. Ma on mieć możliwość dodawania alarmów poprzez wyświetlacz lcd 20x4 i menu z 4 przyciskami, sterowania wyjściami poprzez telefon, odczytywanie danych z czujników itp. Chcę wykorzystać i ulepszyć kilka pomysłów z tego sterownik w moim nowym urządzeniu. Dziękuje za przeczytanie tego artykułu, mam nadzieję że ktoś z mojego rozwiązania coś wykorzysta. Powodzenia we wszystkich waszych przyszłych projektach
×
×
  • Utwórz nowe...

Ważne informacje

Ta strona używa ciasteczek (cookies), dzięki którym może działać lepiej. Więcej na ten temat znajdziesz w Polityce Prywatności.