Motivation

Immer wieder musste ich nachschauen, wie man die ArduinoOTA-Klasse bedient, oder ein altes Projekt suchen, aus dem ich den Code kopieren konnte. Deshalb hier eine Klasse, die die Verwendung erleichtert.


Versionshistorie

Version Anpassungen
1.0 (2017-01-11) Basis-Version
1.1 (2017-01-12
  • nur intern benutzte Variablen als static definiert
  • Methode begin: Typ des Parameters OutDevide von &Print auf *Print geändert.
    Das erlaubt die Übergabe von Null für Systeme, in denen keine Ausgabe erfolgen soll.
  • Dokumentation angepasst.
1.2 (2017-04-26)
  • /Passwort zu den Constructor-Paramtern hinzugefügt.
  • Typ von Constructor-Paramter "HostName" von 'String' -> 'const char*' geändert.
1.3 (2017-06-03) Zweite Version von begin() mit Strings als Parameter hinzugefügt
1.4 (2017-11-25)
  • Definierbare Handler-Funktionen für Start, Ende Fortschritt und Fehler hinzugefügt.
  • Konstruktoren mit Angabe von Hostname und Ausgabe-Gerät hinzugefügt.
1.5 (2017-12-05) Bei der Initialisierung wird der Hostname mit ausgegeben.
1.6 (2018-01-31) Meldungen in deutsch
1.7 (2018-07-23) UrsOTA.begin(String) war nicht eindeutig. Default-Parameter geändert
1.8 (2018-09-11) Text-Konstanten ins Flash gelegt (printf_P benutzt)

Bibliothek UrsOTA

Das Ganze habe ich als Bibliothek UrsOTA mit Klasse UrsOTAClass und der zugehörigen Instanz UrsOTA implementiert. UrsOTA ist ein Wrapper für ArduinoOTA.

Verwendung

Die Bibliothek stellt die Instanz UrsOTA der Klasse UrsOTAClass zu bereit. Über UrsOTA.begin() in der Arduino-Methode setup() wird das OTA-System initialisiert. Der OTA-Service ist nicht interruptgetrieben. Der Aufruf von UrsOTA.handle() am Ende der Arduino-Methode loop() sorgt für eine regelmäßigen Aufruf des OTA-Services.

begin() können optional Parameter für den OTA-Hostname (Voreinstellung ist "", wird von ArduinoOTA auf "esp8266-<ChipID>" angepasst), den Port (Voreinstellung ist 0, wird von ArduinoOTA auf 8266 angepasst) und für eine Instanz einer Klasse, die von Print abgeleitet ist (Voreinstellung ist Serial), zur Meldungsausgabe mitgegeben werden.

Damit OTA funktioniert, muss eine Verbindung zum WLAN bestehen.

Die eingestellten Callback-Funktionen erzeugen die folgende Ausgabe über die hinterlegte Print-Schnittstelle.

OTA Start
Fortschritt: 10%
Fortschritt: 20%
Fortschritt: 30%
Fortschritt: 40%
Fortschritt: 50%
Fortschritt: 60%
Fortschritt: 70%
Fortschritt: 80%
Fortschritt: 90%
Fortschritt: 100%
OTA beendet

Funktionsübersicht

Die Funktionstypen UrsOtaHandlerFunction, UrsOtaHandlerFunction_Error und UrsOtaHandlerFunction_Progress dienen zur Vereinfachung:

Funktion Beschreibung Anmerkung
UrsOTA Statische Instanz der UrsOTAClass-Klasse.

Dieses Objekt kann direkt zum OTA-Update verwandt werden.

uint16_t begin(
  const char *HostName,
  const int Port,
  const char *PassWord = NULL,
  Print *OutDevice = Serial );
begin() initialisiert den OTA-Update-Service. Wird Hostname nicht angegebenen, kommt die ArduinoOTA-Voreinstellung "esp8266-<ChipID>" zum Tragen.
Wird Port nicht angegeben oder wird 0 übergeben, wird die ArduinoOTA-Voreinstellung 8266 genutzt.
Die Voreinstellung für OutDevice ist Serial. Es muss ein Objekt einer Klasse angegeben, das von Print abgeleitet wurde.
Soll keine Ausgabe erfolgen, muss NULL angegeben werden.
uint16_t begin(
  const String HostName,
  const int Port,
  const String PassWord = NULL,
  Print *OutDevice = Serial );
wie oben  
uint16_t begin(
  const char *HostName = NULL,
  Print *OutDevice = Serial );
ohne Angabe von Port und PassWord  
uint16_t begin(
  const String HostName = NULL,
  Print *OutDevice = Serial );
wie oben  
 void onStart(
UrsOtaHandlerFunction fn)
Legt eine Callback-Funktion fest, die beim OTA-Start ausgeführt wird. Die Callback-Funktion wird nach der Ausgabe der Startmeldung auf OutDevice aufgerufen.
void onProgress(
UrsOtaHandlerFunction_Progress fn)
Legt eine Callback-Funktion fest, die bei Fortschritt ausgeführt wird. (10%-Schritte). Der Aufruf erfolgt in 10%-Schritten beginnend bei 10 und endend bei 100.
Die Callback-Funktion wird vor der Ausgabe der Forschrittsmeldung auf OutDevice aufgerufen.
void onEnd(
UrsOtaHandlerFunction fn)
Legt eine Callback-Funktion fest, die beim Ende von OTA ausgeführt wird. Die Callback-Funktion wird vor der Ausgabe der Ende-Meldung auf OutDevice aufgerufen.
void onError
(UrsOtaHandlerFunction_Error fn)
Legt eine Callback-Funktion fest, die bei einem Upload-Fehler ausgeführt wird. Die Callback-Funktion wird vor der Ausgabe der Fehlermeldung auf OutDevice aufgerufen.
void handle() Ruft den OTA-Service auf. Diese Methode sollte am Ende der Arduino-Methode loop() aufgerufen werden.

Beispiel

Das folgende (simple) Beispiel zeigt die Verwendung. Beim Ende von OTA wird Pin D5 auf LOW gelegt.

#include <ESP8266WiFi.h>
#include <UrsOTA.h> // <===== OTA =====
// ...

String SSID = ".....";
const char * HostName = "OTA-Client";
String Password = ".....";

void setup()
{ Serial.begin(115200);

  // Verbindung zum WLAN
  Serial.println("Connecting to WLAN " + SSID);

  WiFi.begin(SSID.c_str(), Password.c_str());
  while (WiFi.waitForConnectResult() != WL_CONNECTED)
  { Serial.println(F("Connection Failed! Rebooting..."));
    delay(5000);
    ESP.restart();
  }

  Serial.println(F("Connected"));

  UrsOTA.begin(HostName); // <===== OTA =====
  UrsOTA.onEnd([] {digitalWrite(D5, LOW); });

  Serial.println(F("Ready"));
  Serial.print(F("Station IP address: "));
  Serial.println(WiFi.localIP());

  pinMode(D5, OUTPUT);
  digitalWrite(D5, HIGH);


  // ...
}

void loop()
{
  // ...
 
  UrsOTA.handle(); // <===== OTA =====
  delay(0);
}

Download

Das ZIP-Archiv für Bibliothek UrsOTA zum Download. Die entpackten Dateien ins Verzeichnis <user>\Documents\Arduino\libraries kopieren (siehe Installing Additional Arduino Libraries).

Das Archiv enthält die Bibliotheksdateien

Code

UrsOTA.h

// UrsOTA
//
// Autor: http://UllisRoboterSeite.de
// Doku:  http://bienonline.magix.net/public/esp8266-ota.html
// Created: 2017-04-26
//
// Version 1.0 (2017-01-11)
// - Basis-Version
// Version 1.1 (2017-01-12)
// - nur intern benutzte Variablen als 'static' definiert
// - Methode 'begin': Typ des Parameters 'OutDevide' von '&Print' auf '*Print' geändert.
//   Das erlaubt die Übergabe von Null für Systeme, in denen keine Ausgabe erfolgen soll.
// Version 1.2 (2017-04-26)
// - Passwort zu den Constructor-Paramtern hinzugefügt.
// - Typ von Constructor-Paramter "HostName" von 'String' -> 'const char*' geändert.
// Version 1.3 (2017-06-03)
// - Zweite Version von begin mit Strings als Parameter
// Version 1.4 (2017-11-25)
// - Definierbare Handler-Funktionen für Start, Ende Fortschritt und Fehler hinzugefügt.
// - Konstruktoren mit Angabe von Hostname und Ausgabe-Gerät hinzugefügt.
// Version 1.5 (2017-12-05)
// - Bei der Initialisierung wird der Hostname mit ausgegeben.
// Version 1.6 (2018-01-31)
// - Meldungen in deutsch.
// Version 1.7 (2018-07-23)
// - UrsOTA.begin(String) war mehrdeutig
// Version 1.8 (2018-09-11)
// - Text-Konstanten ins Flash gelegt (printf_P benutzt)


// Ermöglicht die vereinfachte Benutzung der ArduinoOTA-Klasse.
// - Hostname, Port und Ausgabe-Gerät können optional der Methode begin mitgegeben werden.
//   Die Voreinstellungen "" und 0 führen zu "esp8266-<ChipID>" und 8266.
//   Für OutDevice ist Serial voreingestellt.
// - onStart, onEnd, onProgress, onError sind mit Funktionen hinterlegt, 
//   die eine Ausgabe über eine von Print abgeleitete Klasse ermöglichen.
//   Die Voreinstellung ist Serial.

#ifndef _URSOTA_h
#define _URSOTA_h

#if defined(ARDUINO) && ARDUINO >= 100
	#include "arduino.h"
#else
	#include "WProgram.h"
#endif
#include <ArduinoOTA.h>


typedef std::function<void(void)> UrsOtaHandlerFunction;
typedef std::function<void(ota_error_t)> UrsOtaHandlerFunction_Error;
typedef std::function<void(uint8_t)> UrsOtaHandlerFunction_Progress;

class UrsOTAClass
{public:
  // Initialisiert den OTA-Service.
  // begin() kann nur einmal aufgerufen werden. ArduinoOTA verhindert, das Folgeaufrufe wirksam sind.
  void begin(const char *HostName, const int Port, const char *PassWord = NULL, Print *OutDevide = &Serial);
  void begin(const String HostName, const int Port, const String PassWord = "", Print *OutDevide = &Serial);
  void begin(const char *HostName = NULL, Print *OutDevide = &Serial);
  void begin(const String HostName ="", Print *OutDevide = &Serial);

  // Gibt eine Callback-Funktion an, die ...
  void onStart(UrsOtaHandlerFunction fn); //... beim OTA-Start ausgeführt wird.
  void onEnd(UrsOtaHandlerFunction fn); //... die beim Ende von OTA ausgeführt wird.
  void onProgress(UrsOtaHandlerFunction_Progress fn); // ... bei Fortschritt ausgeführt wird (10%-Schritte).
  void onError(UrsOtaHandlerFunction_Error fn); // ... bei einem Upload-Fehler ausgeführt wird.

  // Ruft den OTA-Service ab.
  void handle() {ArduinoOTA.handle();};

private:
  UrsOtaHandlerFunction _start_callback = NULL;       // Callback für Start
  UrsOtaHandlerFunction _end_callback = NULL;         // Callback für Ende
  UrsOtaHandlerFunction_Error _error_callback = NULL; // Callback für Fehlerfall
  UrsOtaHandlerFunction_Progress _progress_callback = NULL; // Callback für Fortschritt
  Print * _OutDevice = NULL; // Device zur Meldungsausgabe
  uint8_t lastPercentage = 255; // irgendein, nicht durch 10 teilbarer Wert
};

extern UrsOTAClass UrsOTA;

#endif

UrsOTA.cpp

// UrsOTA
//
// Autor: http://UllisRoboterSeite.de
// Doku:  http://bienonline.magix.net/public/esp8266-ota.html

#include <UrsOTA.h>

void UrsOTAClass::begin(const char* HostName, int Port, const char* PassWord, Print * OutDevice) {
  ArduinoOTA.setHostname(HostName);
  ArduinoOTA.setPort(Port);
  ArduinoOTA.setPassword(PassWord);
  _OutDevice = OutDevice;

    ArduinoOTA.onStart([this]() {
     if (_OutDevice) _OutDevice->println(F("\nOTA Start"));
     if (_start_callback) _start_callback();
   });

    ArduinoOTA.onEnd([this]() {
      if (_end_callback) _end_callback();
      if (_OutDevice) _OutDevice->println(F("OTA beendet"));
    });

    ArduinoOTA.onProgress([this](unsigned int progress, unsigned int total)
    { uint8_t percentage = (progress / (total / 100));
    if (percentage > 0 && (percentage % 10) == 0)
      if (percentage != lastPercentage) {
        if (_progress_callback) _progress_callback(percentage);
        if (_OutDevice) _OutDevice->printf_P(PSTR("Fortschritt: %u%%\n"), percentage);
        lastPercentage = percentage;
      }
    });

    ArduinoOTA.onError([this](ota_error_t error) {
      if (_error_callback) _error_callback(error);
      if (_OutDevice) {
        _OutDevice->printf_P(PSTR("OTA Error [%u]: "), error);
        if (error == OTA_AUTH_ERROR) _OutDevice->println(F("Auth fehlgeschlagen"));
        else if (error == OTA_BEGIN_ERROR) _OutDevice->println(F("Begin fehlgeschlagen"));
        else if (error == OTA_CONNECT_ERROR) _OutDevice->println(F("Connect fehlgeschlagen"));
        else if (error == OTA_RECEIVE_ERROR)_OutDevice->println(F("Receive fehlgeschlagen"));
        else if (error == OTA_END_ERROR) _OutDevice->println(F("End fehlgeschlagen"));
      }
    });

  ArduinoOTA.begin();

  if (_OutDevice) {
    _OutDevice->printf_P(PSTR("OTA mit Hostname '%s' initialisiert.\n"), HostName);
  }
}

void UrsOTAClass::begin(const String HostName, const int Port, const String PassWord, Print * OutDevide) {
  begin(HostName.c_str(), Port, PassWord.c_str(), OutDevide);
}

void UrsOTAClass::begin(const char * HostName, Print * OutDevide) {
  begin(HostName, 0, NULL, OutDevide);
}
void UrsOTAClass::begin(const String HostName, Print * OutDevide) {
  begin(HostName.c_str(), 0, NULL, OutDevide);
}

void UrsOTAClass::onStart(UrsOtaHandlerFunction fn) {
  _start_callback = fn;
}

void UrsOTAClass::onEnd(UrsOtaHandlerFunction fn) {
  _end_callback = fn;
}

void UrsOTAClass::onProgress(UrsOtaHandlerFunction_Progress fn) {
  _progress_callback = fn;
}

void UrsOTAClass::onError(UrsOtaHandlerFunction_Error fn) {
  _error_callback = fn;
}

UrsOTAClass UrsOTA;