Projektziele
Irgendwie stellte sich heraus, dass ich mein Haus baute, ein Skelett. In meinem Luxus-Aul gibt es kein Gas und wird in naher Zukunft nicht erwartet, deshalb habe ich mich für ein Skelett entschieden - alles andere, für mich wäre es sehr teuer, mit Strom zu heizen. Nun, auch weil es eine der billigsten Technologien ist.
Ok, ich habe Rohre um das Haus geworfen, Batterien aufgehängt, ein Boiler, es schien warm, aber etwas stimmte nicht.
Nachdem ich mir selbst zugehört hatte, wurde mir klar, dass dies eine Kröte ist, die ich nicht mag, dass die Heizung funktioniert, wenn ich nicht zu Hause bin (12-16 Stunden am Tag). Und es könnte sein, dass es nicht funktioniert. Schalten Sie es erst vor der Ankunft ein, da das Skelett eine leichte Trägheit aufweist und Sie die Temperatur schnell erhöhen können. Die gleiche Situation, wenn irgendwo für eine lange Zeit zu Hause zu verlassen. Nun, im Allgemeinen ist das Laufen, Drehen des Kesselgriffs bei Temperaturänderungen auf der Straße, irgendwie nicht koscher.
Es wurde deutlich, dass der Kessel ohne Automatisierung nirgends der einfachste ist, aber Kontakte zum Anschluss eines externen Steuerrelais hat. Natürlich könnte man sofort einen Kessel mit allen notwendigen Funktionen kaufen, aber für mich sind solche Kessel irgendwie unmenschlich. Außerdem wollte ich in die Hocke gehen, etwas für die Seele pinkeln, ein bisschen C lernen, wenn auch in der Arduino-Version.
Eigentlich über die Anforderungen:
- Solltemperaturregelung
- Kühlmitteltemperaturregelung je nach Außentemperatur oder manuell
- Zeitzonen mit unterschiedlichen Einstellungen, kälter bei Tag, heißer bei Nacht
- Automatikmodus mit automatischem Tag-Nacht-Übergang
- manueller Modus, keine automatischen Übergänge, für das Wochenende
- Nichtautomatischer Modus, in dem Sie die Kühlmitteltemperatur manuell einstellen und den Kessel ein- und ausschalten können
- Heizungssteuerung vor Ort, über Schaltflächen und Bildschirm und über die Website / mobile Anwendung
Es war am Anfang und dann litt ich und fügte hinzu:
- Straßenlaternensteuerung (LED-Strahler)
- Alarmsystem basierend auf Bewegungsmelder, Sirene und Straßenlaterne
- Messung des Energieverbrauchs des Kessels pro Tag / Monat / Jahr + für jeden Monat des Jahres
- Alarmmodus nur durch langsames Blinken einer Lampe
- Signalisierungsmodus durch schnelles Blinken einer Lampe und kurze Pieptöne einer Sirene
- Signalisierungsmodus durch schnelles Blinken einer Lampe und ständiges Heulen einer Sirene
Der Zweck dieses Artikels ist es, Erfahrungen auszutauschen und etwas auf Russisch zu beschreiben, das ich im Internet nicht finden konnte. Ich denke, dieser Artikel wird für Anfänger von Arduino-Heimwerkern nützlich sein, die bereits ein wenig mit Programmierung vertraut sind absolut grundlegende Dinge, die ich nicht beschrieben habe. Ich habe versucht, den Code so klar wie möglich zu schreiben, ich hoffe, es ist mir gelungen.
Was war am Anfang
Ursprünglich wurde das Projekt auf einem wilden Haufen Arduino Nano + ESP8266 implementiert, aber ESP ist kein Schutzschild, sondern ein separates Gerät. Warum so? Ja, weil ich das alles schon hatte, aber es gab überhaupt kein Geld von dem Wort, also wollte ich im Prinzip kein neues Eisen kaufen. Warum ist ESP kein Schild? Jetzt erinnere ich mich nicht einmal mehr.
Arduino steuerte alle Prozesse, weil es über die erforderliche Menge an GPIO verfügte, und ESP schickte alle Daten an den Blynk-Server, weil es das Internet kannte und nicht über genügend GPIO verfügte. Sie haben sich über UART verbunden und JSON mit Daten aneinander gesendet. Das Schema ist ungewöhnlich, aber es hat ein Jahr lang fast ohne Beschwerden funktioniert. Jeder Interessierte kann den Codec sehen .
Ich werde sofort eine Reservierung vornehmen. Ich war zu diesem Zeitpunkt nicht sehr geschickt (und möchte es auch jetzt noch besser machen). Daher ist es besser, wenn schwangere Frauen und Kinder nicht zuschauen. Darüber hinaus wurde alles in der Arduino IDE geschrieben, es wird bei Nacht nicht in Erinnerung bleiben, was in Bezug auf Refactoring stark eingeschränkt ist, alles ist dort sehr primitiv.
Eisen
Also, ein Jahr ist vergangen, erlaubt Finanzen ESP32 devkit v1 zu kaufen, die genug GPIO hat, kann auf das Internet zugreifen und im Allgemeinen einen Super-Controller. Zusätzlich zu den Witzen mochte ich sie am Ende der Arbeit sehr.
Eisen Liste:
- ESP32 devkit v1 noname China
- 3 Temperatursensoren DS18B20, Temperatur im Haus, außen und die Temperatur des Kühlmittels in den Rohren
- Block mit 4 Relais
- pir sensor HC-SR501
Ich werde kein Schema zeichnen, ich denke, aus den Makros mit den Namen der Stifte wird alles klar.
Warum FreeRTOS und Arduino Core?
Auf Arduino sind eine Reihe von Bibliotheken geschrieben, insbesondere derselbe Blynk, damit Sie nicht aus dem Arduino Core herauskommen.
FreeRTOS, weil es Ihnen ermöglicht, die Arbeit eines kleinen Stück Eisens ähnlich der Arbeit eines vollwertigen industriellen Controllers zu organisieren. Jede Aufgabe kann in eine eigene Aufgabe verschoben, gestoppt, gestartet, bei Bedarf erstellt, gelöscht werden - all dies ist viel flexibler als das Schreiben einer langen Scheiße Arduino-Code, wenn am Ende alles nacheinander in der Schleifenfunktion erledigt wird.
Bei Verwendung von FreeRTOS wird jede Aufgabe zu einem genau festgelegten Zeitpunkt ausgeführt, wenn nur die Prozessorleistung ausreicht. Im Gegenteil, in Arduino wird der gesamte Code in einer Funktion in einem Thread ausgeführt. Wenn sich etwas verlangsamt, werden die restlichen Tasks verzögert ausgeführt. Dies macht sich insbesondere bei der Verwaltung schneller Prozesse bemerkbar. In diesem Projekt werden das Blinken einer Taschenlampe und ein Sirenenton weiter unten erläutert.
Über Logik
Informationen zu FreeRTOS-Aufgaben
→ Link zum gesamten Codec des Projekts
Wenn Sie also FreeRTOS verwenden, spielt die Setup-Funktion die Rolle der Hauptfunktion, des Einstiegspunkts in die Anwendung. FreeRTOS-Tasks (im Folgenden Tasks) werden darin erstellt. Die Schleifenfunktion kann überhaupt nicht verwendet werden.
Betrachten Sie eine kleine Aufgabe zur Berechnung der Temperatur des Kühlmittels:
void calculate_water_temp(void *pvParameters) { while (true) { if (heating_mode == 3) {} else { if (temp_outside > -20) max_water_temp = 60; if (temp_outside <= -20 && temp_outside > -25) max_water_temp = 65; if (temp_outside <= -25 && temp_outside > -30) max_water_temp = 70; if (temp_outside <= -30) max_water_temp = 85; } vTaskDelay(1000 / portTICK_RATE_MS); } }
Es wird als eine Funktion deklariert, die _void pvParameters
annehmen pvParameters
, eine Endlosschleife ist in der Funktion organisiert, die ich while (true)
.
Eine einfache Temperaturberechnung wird vTaskDelay(1000 / portTICK_RATE_MS)
(sofern der Betriebsmodus dies zulässt) und anschließend die Aufgabe von vTaskDelay(1000 / portTICK_RATE_MS)
für 1 Sekunde eingeschläfert. In diesem Modus wird keine CPU-Zeit verbraucht. Die Variablen, mit denen die Aufgabe gearbeitet hat, dh der Kontext, werden auf dem Stapel gespeichert, um sie zum gewünschten Zeitpunkt wieder freizugeben.
Die nächste Aufgabe muss im Setup erstellt werden. Dies geschieht durch Aufrufen der xTaskCreate
Methode:
xTaskCreate(calculate_water_temp, "calculate_water_temp", 2048, NULL, 1, NULL);
Es gibt viele Argumente, aber für uns ist berechne_Wassertemperatur von Bedeutung - der Name der Funktion, die den Aufgabencode enthält, und 2048 ist die Größe des Stapels in Bytes.
Die Größe des Stapels hat anfangs alle auf 1024 Byte gesetzt. Dann habe ich die gewünschte Methode durch Eingabe berechnet. Wenn der Controller mit einem Stapelüberlauf zu fallen begann (wie aus der Ausgabe in uart ersichtlich ist), habe ich den Stapel nur um das 2-fache vergrößert, wenn es nicht geholfen hat, um das 2-fache und so weiter bis es funktioniert. Das spart natürlich nicht zu viel Speicher, aber ESP32 hat genug davon, in meinem Fall könnten Sie sich nicht darum kümmern.
Sie können auch ein Handle für die Aufgabe angeben - ein Handle, mit dem Sie die Aufgabe nach der Erstellung steuern können - Löschen. Dies ist die letzte NULL im Beispiel. Ein Handle wird wie folgt erstellt:
TaskHandle_t slow_blink_handle;
Als Nächstes wird beim Erstellen einer Aufgabe ein Zeiger auf das xTaskCreate
an den Parameter xTaskCreate übergeben:
xTaskCreate(outside_lamp_blinks, "outside_lamp_blynk", 10000, (void *)1000, 1, &slow_blink_handle);
Und wenn wir die Aufgabe entfernen wollen, machen wir das:
vTaskDelete(slow_blink_handle);
Wie dies verwendet wird, können Sie dem panic_control
Code panic_control entnehmen.
FreeRTOS Mutex Pros
Mutex wird verwendet, um Konflikte zwischen Aufgaben beim Zugriff auf Ressourcen wie uart, wifi usw. zu beseitigen. In meinem Fall brauchte ich Mutexe für WLAN und den Zugriff auf Flash-Speicher.
Erstellen Sie einen Link zum Mutex:
SemaphoreHandle_t wifi_mutex;
Erstellen Sie im setup
einen Mutex:
wifi_mutex = xSemaphoreCreateMutex();
Wenn wir Zugriff auf die Task-Ressource benötigen, wird der Mutex benötigt, wodurch der Rest der Tasks weiß, dass die Ressource belegt ist und es nicht erforderlich ist, zu versuchen, damit zu arbeiten:
xSemaphoreTake(wifi_mutex, portMAX_DELAY);
portMAX_DELAY
- Warten Sie auf unbestimmte Zeit, bis die Ressource und der Mutex von anderen Tasks freigegeben wurden. In dieser Zeit wird der Task in den Ruhezustand versetzt.
Nachdem wir mit der Ressource gearbeitet haben, geben wir den Mutex, damit andere ihn verwenden können:
xSemaphoreGive(wifi_mutex);
Sie können den Code in der send_data_to_blynk- send_data_to_blynk
anzeigen.
In der Praxis war die Nichtverwendung von Mutexen während des Betriebs des Controllers nicht erkennbar, aber während des JTAG-Debugs verschwanden ständig Fehler, die nach der Verwendung der Mutexe verschwanden.
Kurze Beschreibung der Tasok
get_temps
- Empfangen der Temperatur von den Sensoren alle 30 Sekunden, häufiger ist dies nicht erforderlich.
get_time_task
- Zeit von NTP-Servern get_time_task
. Früher stammte die Zeit vom RTC-Modul DS3231, aber nach einem Jahr Arbeit fiel sie allmählich aus, sodass ich mich entschied, sie überhaupt loszuwerden. Ich entschied, dass dies für mich keine besonderen Konsequenzen hat, hauptsächlich beeinflusst die Zeit das Umschalten der Heizzeitzone - Tag oder Nacht. Verschwindet das Internet während des Betriebs des Controllers, friert die Zeit einfach ein, die Zeitzone bleibt einfach gleich. Wenn sich der Regler ausschaltet und nach dem Einschalten kein Internet vorhanden ist, ist die Zeit immer 0:00:00 - Heizmodus nachts.
calculate_water_temp
- oben betrachtet.
detect_pir_move
- detect_pir_move
ein Bewegungssignal vom HC-SR501-Sensor. Der Sensor bildet eine logische Einheit digitalRead
V, wenn eine Bewegung erkannt wird, die über digitalRead
erkannt wird. Der Pin für die Erkennung dieses Sensors sollte pinMode(pir_pin, INPUT_PULLDOWN);
auf GND - pinMode(pir_pin, INPUT_PULLDOWN);
heating_control
- heating_control
umschalten.
out_lamp_control
- Steuerung einer Straßenlampe.
panic_control
- Sirene und Scheinwerfer steuern, wenn Bewegung erkannt wird. Um den Effekt von Sirenen und Blinklichtern zu erzeugen, werden separate Tasks verwendet, outside_lamp_blinks
und siren_beeps
. Bei Verwendung von FreeRTOS funktionieren das Blinken und die Signaltöne genau in den festgelegten Intervallen einwandfrei. Andere Aufgaben wirken sich nicht auf die Arbeit aus, da Sie leben in getrennten Strömen. FreeRTOS garantiert, dass der Code in der Task zum angegebenen Zeitpunkt ausgeführt wird. Bei der Implementierung dieser Funktionen in der loop
alles nicht so reibungslos funktioniert, weil beeinflusst durch die Ausführung von anderem Code.
guard_control
- Steuerung der Guard-Modi.
send_data_to_blynk
- Daten an die Blynk-Anwendung senden.
run_blynk
- Task zum Starten von Blynk.run()
wie im Handbuch zur Verwendung von Blynk angegeben. Soweit ich weiß, ist dies erforderlich, um Daten von der Anwendung an die Steuerung zu übertragen. Im Allgemeinen sollte sich Blynk.run()
in einer loop
, aber ich wollte im Grunde nichts dort Blynk.run()
und habe es zu einer separaten Aufgabe gemacht.
write_setting_to_pref
- write_setting_to_pref
Einstellungen und Betriebsmodi auf, um sie nach einem Neustart zu erfassen. Über Pref wird unten beschrieben.
count_heated_hours
- Zählen der Betriebszeit des Kessels. Ich habe es einfach gemacht, wenn der Kessel zum Zeitpunkt des Taskstarts (einmal alle 30 Sekunden) eingeschaltet wird, wird im Flash-Speicher der Wert für die gewünschte Taste um eins erhöht.
send_heated_hours_to_app
- In dieser Task werden die Werte extrahiert und mit 0,00833 (1/120 Stunden) multipliziert. Die empfangenen Betriebsstunden des Kessels werden an die Blynk-Anwendung gesendet.
feed_watchdog
- feed_watchdog
Watchdog. Ich musste Watchdog schreiben, weil alle paar tage konnte der controller einfrieren. Es ist nicht klar, womit es verbunden ist. Möglicherweise liegt eine Störung der Stromversorgung vor, aber die Verwendung von Watchdog löst dieses Problem. Watchdog-Timer 10 Sekunden. Ist der Controller 10 Sekunden lang nicht verfügbar, ist dies in Ordnung.
heart_beat
- Aufgabe mit einem Puls. Wenn ich den Controller übergebe, möchte ich wissen, dass er einwandfrei funktioniert. Weil Auf meinem Board gibt es keine eingebaute LED, ich musste die UART LED verwenden - installiere Serial.begin(9600);
und schreibe einen langen String in UART. Es funktioniert ziemlich gut
ESP32 NVS-Verschleißausgleich
Die folgenden Beschreibungen sind ziemlich grob, buchstäblich auf den Fingern, nur um das Wesentliche des Problems zu vermitteln. Ausführlicher
Arduino verwendet einen EEPROM-Speicher, um Daten im nichtflüchtigen Speicher zu speichern. Dies ist ein kleiner Speicher, in den jedes Byte separat geschrieben und gelöscht werden kann, während der Flash-Speicher nur nach Sektoren gelöscht wird.
Es gibt kein EEPROM in ESP32, aber normalerweise gibt es einen 4-MB-Flash-Speicher, in dem Sie Partitionen für die Controller-Firmware oder zum Speichern von Benutzerdaten erstellen können. Es gibt verschiedene Arten von Abschnitten für Benutzerdaten - NVS, FATFS, SPIFFS. Die Auswahl sollte auf der Grundlage des Datentyps erfolgen, der für die Aufzeichnung vorgesehen ist.
Weil Alle Daten, die in diesem Projekt geschrieben werden, sind vom Typ Int. Ich habe NVS - Non-Volitile Storage gewählt. Dieser Partitionstyp eignet sich gut zum Speichern kleiner, oft überschreibbarer Daten. Um zu verstehen, warum, sollten Sie etwas tiefer in die Organisation von NVS einsteigen.
Wie bei EEPROM und FLASH gibt es Einschränkungen beim Überschreiben von Daten, Bytes im EEPROM können 100.000- bis 1.000.000-mal überschrieben werden, und der FLASH-Sektor ist derselbe. Wenn wir Daten einmal pro Sekunde schreiben, erhalten wir 60 Sekunden x 60 Minuten x 24 Stunden = 86.400 Mal pro Tag. Das heißt, in diesem Modus dauert das Byte 11 Tage, was ein bisschen ist. Danach ist das Byte nicht mehr zum Schreiben und Lesen verfügbar.
Um dieses Problem zu beheben, schreiben die update()
put()
-Funktionen der Arduino EEPROM-Bibliothek nur Daten, wenn sie sich ändern. Das heißt, Sie können jede Sekunde einige Einstellungen und Moduscodes schreiben, die sich nur sehr selten ändern.
NVS verwendet eine andere Methode zur Kontrolle des Verschleißniveaus. Wie oben erwähnt, können Daten im Flash-Sektor in Teilen geschrieben werden, aber nur der gesamte Sektor kann gelöscht werden. Daher wird die Datenaufzeichnung in NVS in einer Art Journal durchgeführt, dieses Journal ist in Seiten unterteilt, die in einem Sektor des Flash-Speichers abgelegt sind. Die Daten werden in Schlüssel-Wert-Paaren geschrieben. In der Tat ist es sogar einfacher als mit EEPROM, weil Das Arbeiten mit einem aussagekräftigen Namen ist einfacher als mit einer Adresse im Speicher. Update: Die Schlüssellänge beträgt maximal 15 Zeichen!
Wenn Sie zuerst den Wert 1
in den Schlüssel somekey
und dann den Wert 2
in denselben Schlüssel schreiben, wird der erste Wert nicht gelöscht, sondern nur als gelöscht (gelöscht) markiert und ein neuer Eintrag zum Protokoll hinzugefügt:

Wenn Sie versuchen, Daten mit somekey
Schlüssel zu lesen somekey
letzte Wert dieses Schlüssels zurückgegeben. Weil Da das Protokoll gemeinsam ist, werden die Werte verschiedener Schlüssel beim Schreiben nebeneinander gespeichert.
Die Seite hat den Status Leer - leer, ohne Einträge, Aktiv - Daten werden gerade darauf geschrieben, Voll - ist voll, Sie können nicht darauf schreiben. Sobald der Speicherplatz auf der Seite erschöpft ist, ist sie von
Active wechselt zu Full und die nächste leere Seite wird Active.

Soweit ich aus der Dokumentation auf der Espressif-Website und verschiedenen Foren weiß, beginnt die Seitenbereinigung mit dem Ende der freien Seiten. Um genauer zu sein, wird demnach gelöscht, wenn nur noch 1 freie Seite übrig ist.
Wenn die Seite gelöscht werden muss, werden die aktuellen Datensätze (nicht gelöscht) auf eine andere Seite verschoben und die Seite wird überschrieben.
Daher ist der Schreib-Lösch-Vorgang für jede bestimmte Seite ziemlich selten, je mehr Seiten - desto seltener. Auf dieser Grundlage habe ich die Größe der NVS-Partition auf 1 MB erhöht. Bei meiner Aufzeichnungsrate reicht dies für 170 Jahre, was im Allgemeinen ausreicht. Informationen zum Ändern der Größe des NVS-Abschnitts finden Sie als Nächstes.
Für eine bequeme Arbeit mit NVS verfügt ESP32 für Arduino Core über eine praktische Bibliothek mit Einstellungen. Wie Sie damit arbeiten, erfahren Sie hier .
Ein bisschen über VisualGDB
Sobald ich anfing, mit der Arduino IDE zu arbeiten, war ich sofort von der miserablen Funktionalität im Vergleich zu Visual Studio überrascht. Sie sagen, dass VS auch kein Brunnen ist, obwohl es mir passt, aber etwas über 50 Zeilen in der Arduino IDE zu schreiben ist schmerzhaft schmerzhaft und schmerzhaft lang. Daher stellte sich die Frage, ob eine IDE für die Entwicklung ausgewählt werden sollte. Weil Ich bin mit VS vertraut und habe mich für VisualGDB entschieden .
Nach der Arduino IDE ist die Entwicklung für den ESP32 einfach ein Hafen. Was ist der Übergang zur Definition, die Suche nach Aufrufen im Projekt und die Möglichkeit, die Variable umzubenennen.
Ändern der ESP32-Partitionstabelle mit VisualGDB
Wie oben erwähnt, kann die Tabelle mit der ESP32-Partition geändert werden, und wir werden prüfen, wie dies getan werden kann.
Die Tabelle wird als CSV-Datei bearbeitet. Standardmäßig schreibt VisualGDB die folgende Tabelle:
Name, Type, SubType, Offset, Size, Flags nvs, data, nvs, 0x9000, 0x5000, otadata, data, ota, 0xe000, 0x2000, app0, app, ota_0, 0x10000, 0x140000, app1, app, ota_1, 0x150000,0x140000, spiffs, data, spiffs, 0x290000,0x170000,
Hier sehen wir einen Abschnitt unter NVS, zwei Abschnitte für Anwendungen und einige weitere Abschnitte. Von den Nuancen ist zu beachten, dass app0 (Ihre Anwendung) immer bei Offset 0x10000 ab der Nulladresse geschrieben werden sollte, da der Bootloader dies sonst nicht erkennt. Offsets sollten auch so ausgewählt werden, dass sich die Abschnitte nicht "überlappen". Die Partitionstabelle selbst wird mit dem Offset 0x8000 geschrieben. Wie Sie sehen können, beträgt die Größe des NVS in diesem Fall 0x5000 - 20 KB, was nicht sehr viel ist.
Ich habe die Partitionstabelle wie folgt geändert:
Name, Type, SubType, Offset, Size, Flags app0, app, ota_0, 0x10000, 0x140000, nvs, data, nvs, , 1M, otadata, data, ota, , 0x2000, spiffs, data, spiffs, , 0x170000,
Vergessen Sie nicht, vor Name ein Raster einzufügen. Wenn Sie diese Tabelle verwenden, muss diese Zeile als Kommentar betrachtet werden.
Wie Sie sehen, wird die Größe des NVS auf 1 MB erhöht. Wenn Sie keine Offsets angeben, beginnt der Abschnitt unmittelbar nach dem vorherigen, sodass es ausreicht, den Offset nur für app0 anzugeben. CSV-Dateien können im Editor als txt bearbeitet werden und ändern dann die Berechtigung für die gespeicherte Datei in csv.
Als nächstes muss die Partitionstabelle in eine Binärdatei konvertiert werden, weil es wird in dieser Form in die Steuerung eingegeben. Führen Sie dazu den Konverter aus:
c:\Users\userName\Documents\ArduinoData\packages\esp32\hardware\esp32\1.0.3\tools\gen_esp32part.exe part_table_name.csv part_table_name.bin
. Der erste Parameter ist Ihre CSV, der zweite Parameter ist die Ausgabe-Binärdatei.
Die resultierende Binärdatei sollte in c:\Users\userName\Documents\ArduinoData\packages\esp32\hardware\esp32\1.0.3\tools\partitions\part_table_name.csv
werden. c:\Users\userName\Documents\ArduinoData\packages\esp32\hardware\esp32\1.0.3\tools\partitions\part_table_name.csv
muss angegeben werden, dass er die Lösung erstellt hat Keine Standardpartitionstabelle. Sie können dies tun, indem Sie den Namen Ihrer Tabelle in die Datei c:\Users\userName\Documents\ArduinoData\packages\esp32\hardware\esp32\1.0.3\boards.txt
. In meinem Fall ist dies esp32doit-devkit-v1.build.partitions=part_table_name
Nach diesen Manipulationen nimmt VisualGDB beim Erstellen der Anwendung genau Ihre Partitionstabelle und legt sie ab
~project_folder_path\Output\board_name\Debug\project_name.ino.partitions.bin
, von wo aus es bereits in das Board geschrieben wird.
JTAG-Debugger CJMC-FT232H
Soweit ich weiß, ist dies der billigste Debugger, der mit ESP32 funktioniert. Er kostet mich ungefähr 600 Rubel. Es gibt eine Menge davon bei Aliexpress.

Wenn Sie den Debugger verbinden, installiert Windows ungeeignete Treiber, die mit dem Zadig-Programm geändert werden müssen. Dort ist alles einfach, ich werde es nicht beschreiben.
Es stellt auf folgende Weise eine Verbindung zu ESP32 devkit-v1 her:
FT232H - ESP32
AD0 - GPIO13
AD1 - GPIO12
AD2 - GPIO15
AD3 - GPIO14
Anschließend müssen Sie unter Project -> VisualGDB Project Properties
folgende Einstellungen vornehmen:

Klicken Sie dann auf Test. Manchmal kommt es vor, dass die Verbindung beim ersten Mal nicht hergestellt wird, der Prozess scheint einzufrieren, dann müssen Sie den Test unterbrechen und wiederholen. Wenn alles in Ordnung ist, dauert der Test der Verbindung ca. 5 Sekunden.
Normalerweise habe ich das Projekt zusammengestellt und über USB auf den ESP32 hochgeladen (nicht über den Debugger). Danach habe ich mit Debug -> Attach to Running Embedded Firmware
mit dem Debuggen begonnen. Im Code können Sie Haltepunkte setzen, die Werte der Variablen zum Zeitpunkt der Unterbrechung Debug -> Windows -> Threads
und im Fenster Debug -> Windows -> Threads
sehen, in welchem FreeRTOS-Code der Code gestoppt wurde. Dies ist hilfreich, wenn beim Debuggen ein Fehler auftritt. Diese Funktionen des Debuggers reichten aus, um komfortabel zu arbeiten.
Als ich anfing, mit NVS zu arbeiten, wurde das Debuggen ständig durch unklare Fehler unterbrochen. Soweit ich weiß, muss der Debugger im Standard-NVS-Bereich einen Speicherauszug erstellen, aber zu diesem Zeitpunkt wird der NVS bereits vom Controller verwendet. Dies könnte natürlich umgangen werden, indem zwei NVS-Partitionen erstellt werden, eine mit dem Standardnamen für das Debuggen und die andere für die eigenen Anforderungen. Aber da war nichts kompliziertes, im hinzugefügten Code hat es beim ersten Mal funktioniert, also habe ich es nicht überprüft.
Störungen ESP32
Wie jedes Gerät mit Aliexpress hatte mein ESP32-Board einen eigenen, nirgendwo beschriebenen Fehler. Als sie ankam, speiste ich einige Peripheriegeräte, die an I2C arbeiteten, von der Platine, aber nach einiger Zeit wurde die Platine neu gestartet, wenn verbrauchende Geräte oder sogar nur ein Kondensator an das + 5-V-Bein angeschlossen waren. Warum das so ist, ist völlig unverständlich.
Jetzt versorge ich die Platine mit der chinesischen Ladung 0,7A, die Sensoren ds18b20 am Fuß der 3,3-V-Platine und das Relais und den Bewegungssensor mit einer weiteren Ladung von 2A. Der GND-Fuß der Platine ist natürlich mit den GND-Stiften des restlichen Eisens verbunden. Billig und fröhlich ist unsere Option.
Über die Projektergebnisse
Ich hatte die Möglichkeit, die Heizung im Haus flexibel zu regeln und so Geld und Schweiß zu sparen. Wenn die Heizung den ganzen Tag über zwischen -5 und -7 Grad warm ist, beträgt die Betriebsdauer des Heizkessels derzeit etwa 11 Stunden. Wenn tagsüber 20 Grad gehalten und erst abends 23 warm werden sollen, dann sind dies bereits 9 Betriebsstunden des Kessels. Die Kesselleistung beträgt 6 kW, bei einem aktuellen Kilowattpreis von 2,2 Rubel sind dies ca. 26,4 Rubel pro Tag. Die Dauer der Heizperiode in unserer Region beträgt 200 Tage, die Durchschnittstemperatur in der Heizperiode beträgt nur etwa -5 Grad. Auf diese Weise sparen wir in der Heizperiode etwa 5000 r.
Die Kosten für die Ausrüstung überschreiten nicht 2000 R, dh die Kosten werden in wenigen Monaten zurückgezahlt, ganz zu schweigen von der Tatsache, dass ein fertiges System für eine solche Automatisierung mindestens 20 000 R kosten würde. Eine andere Sache ist, dass ich ungefähr eine Woche reine Arbeitszeit mit dem Schreiben von Firmware und dem Debuggen verbracht habe, aber im Laufe der Arbeit habe ich schließlich festgestellt, was Zeiger in C ++ sind, und eine Menge anderer Erfahrungen gesammelt (zum Beispiel die Erfahrung von vielen Stunden Debuggen von unverständlichen Störungen). Und Erfahrung ist, wie Sie wissen, schwer zu überschätzen.
Screenshots der mobilen Blynk-Anwendung:



Natürlich ist der Code im Projekt kein Meisterwerk, aber ich habe ihn unter den Bedingungen eines Zeitmangels geschrieben und mich hauptsächlich auf die Lesbarkeit konzentriert. Es ist einfach keine Zeit für eine Umgestaltung. Im Allgemeinen habe ich viele Ausreden, warum mein Code so unheimlich ist, aber das ist mein Favorit, daher werde ich darauf näher eingehen und das Thema nicht weiterentwickeln.
Wenn mein Scribble jemandem hilft, werde ich aufrichtig froh sein. Über Kommentare und Anregungen würde ich mich freuen.