Christians Webseite        << zurueck        vor >>

UART und Programmieren

Als Programmieradapter für die ESPs sind ein simpler USB-UART und 2 Taster ausreichend. Der Betrieb ist halbautomatisch. Zum Starten des Bootloaders den Taster FLASH gedrückt halten und kurz RESET drücken. Die Pin-Belegung des Steckverbinders im Beispiel ist für ein ESP-01S-Modul ausgelegt, die Signalnamen sind beim Shelly aber identisch.
Das Target darf nur aus genau einer Quelle mit Strom versorgt werden! Entweder die 3.3V des UART-Adapters oder ein externes Netzteil.
shelly shelly

Auf einigen "etwas besseren" UART-Boards ist auch das RTS-Signal herausgeführt. Damit, und mit 2 Transistoren, kann man die auf den üblichen Eval-Boards vorhandene (automatischere) Programmierbeschaltung nachbilden. Details dazu finden sich im ESP-Datenblatt.

Mit angeschlossenem USB-UART-Adapter kann man die Monitor-Daten auf der seriellen Schnittstelle (115200,8,1,N) mitlesen. Nach einem normalen Reset meldet sich der Shelly mit den folgenden Daten (gekürzt):

ESP-ROM:esp32c3-api1-20210207
Build:Feb  7 2021
rst:0x1 (POWERON),boot:0xd (SPI_FAST_FLASH_BOOT)
SPIWP:0xee
mode:DIO, clock div:1
load:0x3fcd5820,len:0x11a8
load:0x403cc710,len:0x148c
load:0x403ce710,len:0x28c0
entry 0x403cc71a
boot: Shelly OS loader 1.0.0 (Oct 11 2024 11:45:33 IDF 5.2.1-s2)
boot: chip revision: v0.4
boot.esp32c3: SPI Speed      : 80MHz
boot.esp32c3: SPI Mode       : DIO
boot.esp32c3: SPI Flash Size : 8MB
boot: Enabling RNG early entropy source...
...

Das Bild ändert sich wenn GPIO9/[15]/"FLASH" während des Reset auf Masse liegt. Der Bootloader wird aktiviert und der Programmiermodus gestartet. Das Gerät wartet auf neue Firmware:
ESP-ROM:esp32c3-api1-20210207
Build:Feb  7 2021
rst:0x1 (POWERON),boot:0x5 (DOWNLOAD(USB/UART0/1))
waiting for download

PlatformIO

PlatformIO ist eine der aktuell gängigsten Programmierungebungen. Üblicherweise beginnt man dort mit der Angabe des verwendeten Board-Typs. Dadurch werden alle für die SW-Erstellung wesentlichen Details (z.B. Port-Belegungen und Speichergrössen) gesetzt. Leider sind "die Shellys" dort unbekannt. Eine schnelle und einfache Lösung ist es ein primitives/generisches Board mit dem gleichen Prozessor als Basis zu nehmen und die Abweichungen händisch in der platformio.ini aufzulisten. Für den ESP32-C38F ist das insbesondere die Speicherkonfiguration:

[env:esp32-c3-devkitc-02]
platform = espressif32
board = esp32-c3-devkitc-02
board_upload.flash_size = "8MB"
board_upload.maximum_size = 8388608
board_upload.maximum_ram_size = 512000
board_build.partitions = default_8MB.csv
framework = arduino
monitor_speed = 115200


OTA Update

Wenn man in seinem Programm die WiFi-Funktionen des ESP nutzt dann hat man auch die Option offen die Programmierung darüber zu machen (OverTheAir). Das ist sehr schnell und benötigt keinen zusätzlichen Adapter (abgesehen von der initialen Programmierung).

Eine beispielhafte Anleitung liegt hier:
https://randomnerdtutorials.com/esp8266-ota-updates-with-arduino-ide-over-the-air/

In der platformio.ini müssen das gewünschte Protokoll und die IP-Adresse des ESP-WiFi bekannt gemacht werden:

[env:esp32-c3-devkitc-02]
...
upload_protocol = espota
upload_port = 192.168.178.42
...

Im Hauptprogramm muss das passende Include eingebunden werden und im Setup-Bereich initiiert werden.

main.cpp
...
#include <ArduinoOTA.h>            // Enable SW-Update over WiFi 

uint16_t OTA_Port = 8266;                       
const char* OTA_Hostname ="myesp8266";
...


void setup() {
  ArduinoOTA.setPort(OTA_Port);
  ArduinoOTA.setHostname(OTA_Hostname);

  ArduinoOTA.onStart([]() {
    String type;
    if (ArduinoOTA.getCommand() == U_FLASH) {
      type = "sketch";
    } else {  // U_FS
      type = "filesystem";
    }
    // NOTE: if updating FS this would be the place to unmount FS using FS.end()
    if (SendSerialDebugActive == 1)      Serial.println("Start updating " + type);
  });
  
  ArduinoOTA.onEnd([]() { if (SendSerialDebugActive == 1)  Serial.println("\nEnd");});
  ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) 
    { if (SendSerialDebugActive == 1) Serial.printf("Progress: %u%%\r", (progress/(total/100)));}
  );
  ArduinoOTA.onError([](ota_error_t error) 
  {
    if (SendSerialDebugActive == 1) {     
    Serial.printf("Error[%u]: ", error);
    if (error == OTA_AUTH_ERROR) {
      Serial.println("Auth Failed");
    } else if (error == OTA_BEGIN_ERROR) {
      Serial.println("Begin Failed");
    } else if (error == OTA_CONNECT_ERROR) {
      Serial.println("Connect Failed");
    } else if (error == OTA_RECEIVE_ERROR) {
      Serial.println("Receive Failed");
    } else if (error == OTA_END_ERROR) {
      Serial.println("End Failed");
    }
  }    
  });

  ArduinoOTA.begin();
...
}

Im "Loop-Bereich" wird regelmässig überprüft ob eine Update-Anforderung anliegt. Der Rest läuft danach vollautomatisch.
	

void loop() {
  ArduinoOTA.handle();  
...