Update 2018-07-08:

{sketch_path} in Anführungszeichen gesetzt -> "{sketch_path}"; dito für {build_path}; dito für ArduinoPrebuild.cmd

Update 2017-08-08: Verbesserte Version für ESP8266

Das ESP8266-API liefert eine Menge Informationen zum System (s. ESP8266 FAQ: Zugriff auf Systeminterna). In Verbindung mit den "APP"-Markos kann man zu Programmanfang eine System- und Applikationsinformation ausgeben. Die verbesserte Version für den ESP8266 stellt die Methode PrintAppInfo(Print & p = Serial) bereit, die folgende (Muster-) Ausgabe generiert:

Output von PrintAppInfo

Der Quellcode für AppVersion.h. Nur der erste Teil, bis zur Deklaration von PrintAppInfo muss angepasst werden.

/*
* AppVersion.h
*
* Projekt: Beispiel zu AppVersion für ESP8266
*
* -----------------------------------------------------
* Version 1.0: 2017-08-08
* - Basisversion
*/

#ifndef APPVERSION_H_
#define APPVERSION_H_

#define VERSION_MAJOR 1
#define VERSION_MINOR 0
#define VERSION_BUILD 5

#define APP_AUTHOR  "Ulli"
#define APP_NAME    "UrsAppInfo Example"
#define APP_WEBSITE "http://UllisRoboterSeite"

static void PrintAppInfo(Print & p = Serial);

// -------------------------------------------------------------------
// ---------------------- Interner Bereich ---------------------------
// -------------------------------------------------------------------

#include <FS.h>

//Zum Zusammenbau der Versionsnummer als String
#define APPVERSION_STR_HELPER(x) #x
#define APPVERSION_STR(x) APPVERSION_STR_HELPER(x)

#define VERSIONSTRING APPVERSION_STR(VERSION_MAJOR) "." APPVERSION_STR(VERSION_MINOR) "." APPVERSION_STR(VERSION_BUILD)
#define APP_VERSION VERSIONSTRING


// Rechtsbündige Ausgabe und Tausender-Trennzeichen "."
// Value: auszugebender Wert
// Width: Anzahl minimal auszugebender Zeichen (max. 18)
// Fill: Füllzeichen für linksbündige Füllung
static String PrintDotted(uint32_t Value, uint8_t Width, char Fill = ' ') {
  char buf[20];
  char fmt[20];

  if (Width > 18) Width = 18; // Breite begrenzen

  itoa(Value, buf, 10);  // Normale Ausgabe in Puffer

  // Rückwärts umkopieren, dabei Punkte einfügen
  int8_t l = strlen(buf); // Länge des Puffers
  int8_t i = 0;           // Index Ziel
  int8_t j = 0;           // Ziffernindex für Punkte
  int8_t k = l - 1;       // Index Quelle

  while (k >= 0) {
    fmt[i++] = buf[k--];
    if ((++j % 3) == 0)
      if (j < l)
        fmt[i++] = '.';
  }
  while (i < Width) // Auf die gewünschte Länge auffüllen
    fmt[i++] = Fill;

  fmt[i] = 0; // String-Ende

  // Wieder in die richtige Reihenfolge bringen
  l = strlen(fmt);
  for (i = 0, k = l - 1; i < l; i++, k--)
    buf[i] = fmt[k];
  buf[i] = 0;

  return String(buf);
}

static const char Bytes[] PROGMEM = " Bytes";
static void PrintAppInfo(Print & p)  {
#define FC 183 // Füllzeichen "." für rechtsbündige Zahlenausgabe
#define FL 9   // Breite für Zahlenausgabe
  p.println(F(APP_NAME " Version " VERSIONSTRING));
  p.println(F("Author: " APP_AUTHOR " " APP_WEBSITE));
  p.println(F("-------------------------------------------"));
  p.print(F(" Last reset reason: ")); p.println(ESP.getResetReason().c_str());
  p.print(F("            ChipID: ")); p.println(ESP.getChipId());
  p.print(F("     CPU frequency: ")); p.print(PrintDotted(ESP.getCpuFreqMHz(), FL, FC)); p.println(F(" MHz"));
  p.print(F("Memory size  (SDK): ")); p.print(PrintDotted(ESP.getFlashChipSize(), FL, FC)); p.println(FPSTR(Bytes));
  p.print(F("Memory size (CHIP): ")); p.print(PrintDotted(ESP.getFlashChipRealSize(), FL, FC)); p.println(FPSTR(Bytes));
  p.print(F("         Free heap: ")); p.print(PrintDotted(ESP.getFreeHeap(), FL, FC)); p.println(FPSTR(Bytes));
  p.print(F("       Sketch size: ")); p.print(PrintDotted(ESP.getSketchSize(), FL, FC)); p.println(FPSTR(Bytes));
  p.print(F("  Free sketch size: ")); p.print(PrintDotted(ESP.getFreeSketchSpace(), FL, FC)); p.println(FPSTR(Bytes));

  // SPIFFS
  if (SPIFFS.begin()) { // Der ggf. mehrfach erfolgende Aufruf von SPIFFS.begin() ist unkritisch.
    FSInfo fs_info;
    SPIFFS.info(fs_info);
    p.println(F("File system"));
    p.print(F("        total size: ")); p.print(PrintDotted(fs_info.totalBytes, FL, FC)); p.println(FPSTR(Bytes));
    p.print(F("         used size: ")); p.print(PrintDotted(fs_info.usedBytes, FL, FC)); p.println(FPSTR(Bytes));
    p.print(F("        block size: ")); p.print(PrintDotted(fs_info.blockSize, FL, FC)); p.println(FPSTR(Bytes));
    p.print(F("         page size: ")); p.print(PrintDotted(fs_info.pageSize, FL, FC)); p.println(FPSTR(Bytes));
    p.print(F("   max path length: ")); p.print(PrintDotted(fs_info.maxPathLength, FL, FC)); p.println(FPSTR(Bytes));
    p.print(F("    max open files: ")); p.println(PrintDotted(fs_info.maxOpenFiles, FL, FC));
  }
}
#endif /* APPVERSION_H_ */

Ein kleines Programm, das die Verwendung zeigt:

// ESP8266_AppInfo.ino

#include "AppVersion.h"

void setup() {
  Serial.begin(115200);
  Serial.println();
  PrintAppInfo();
}

void loop() {
  delay(10000); // Alle 10 Sekunden
  Serial.println(APP_NAME " " APP_VERSION);
}

AppVersion.h und ESP8266_AppInfo.ino zum Download.

Update 2016-09-14

Leider habe ich übersehen, dass die Dateien zuerst in das Build-Verzeichnis kopiert werden und dann erst die "prebuild hooks" ausgeführt werden. Die Modifikationen werden also jeweils erst beim nächsten Kompilierungsvorgang wirksam. Außerdem löst die Arduino-IDE den Parameter '{sketch_path}' nicht auf. Das folgende Batch-Programm umgeht das Problem indem die Modifikationen an den Dateikopien durchgeführt werden und dann die Originaldateien mit den modifizierten Kopien überschrieben werden. Es eignet sich sowohl für die Übersetzung mit Visual Micro als auch für die Arduino IDE.

Unfortunately, I have overlooked that the files are first copied into the build directory, and thereafter the "prebuild hooks" are executed. That the modifications become effective only at the next compilation process. In addition, the Arduino IDE does not resolve the parameter '{sketch_path}'. The following batch program gets around the problem by doing the modifications to the copies of the files and then overwrites the original files with the modified copies. It is suitable for the translatios with Visual Micro as well as for the Arduino IDE.
@echo off
REM ArduinoPrebuild.cmd

REM ---------- Deutsche Anleitung -----------------------------------------------------------------------------------
REM Dieses Batch-Programm dient dazu, eine Datei beim Build-Prozess vor dem Kompilieren dauerhaft zu modifizieren, zB. um eine Versionsnummer hochzuzählen.
REM Der Aufruf der Datei erfolgt durch recipe.hooks.sketch.prebuild.#.pattern in der Datei 'platform.txt'.
REM AppVersion.h ist die Datei, die modifiziert werden soll. Das Programm, das modifiziert ist 'IncrementVersion.exe'.

REM Beim Kompilieren setzt die Arduino-IDE die CurrentDirectory auf das Sketch-Verzeichnis, z.B. <user>\documents\Arduino.
REM Visual Micro steht im Arduino Programm-Verzeichnis, z.B. C:\Program Files (x86)\Arduino.

REM Der Parameter {sketch_path} wird von Visual Micro aufgelöst und zeigt auf das Sketch-Verzeichnis (ohne Backslash am Ende).
REM Die Arduino-IDE liefert den Text "{sketch_path}" zurück.

REM Der Parameter {build.path} zeigt bei Visual Micro direkt auf den Ordner mit den Source-Dateien(ohne Backslash am Ende).
REM VM kopiert alle Quellen dorthin.
REM Beispiel: <Temp>\VMicroBuilds\<project-name>\<board-name>

REM Der Parameter {build.path} zeigt bei der Arduino-IDE in einen Sammel-Ordner (mit Backslash am Ende). 
REM Die Quellen befinden sich im Unterverzeichnis 'sketch'.
REM Beispiel: <Temp>\buildc8b6589afc1136b3b8ee8306c9cc9978.tmp\sketch

REM Syntax:recipe.hooks.sketch.prebuild.#.pattern = <path>ArduinoPrebuild.cmd "{sketch_path}" "{build.path}\AppVersion.h"
REM                                                             %0                 %1               %2

REM Wenn %~1 den Text "{sketch_path}" enthält, erfolgt die Kompilierung durch die Arduino-IDE


REM ---------- English instructions -----------------------------------------------------------------------------------
REM This batch program is used to modify a file permanently during the build process before compiling, e.g. a version number increment.
REM It is called by recipe.hooks.sketch.prebuild.#.pattern in the file 'platform.txt'.
REM AppVersion.h is the file that is to be modified. The modifying program is 'IncrementVersion.exe'.

REM Compiling with the Arduino IDE the CurrentDirectory is set to the directory of the sketch, e.g. <user>\documents\Arduino.
REM Visual Micros CurrentDirectory is the Arduini program directory, e.g. C:\Program Files (x86)\Arduino.

REM The {sketch_path} parameter is resolved by Visual Micro and points to the directory of the sketch (without a backslash at the end).
REM The Arduino IDE simply returns the text '{sketch_path}'.

REM of parameters {build.path} shows Visual micro directly to the folder with the source Dateien(ohne Backslash andem an Ende).
REM VM copies all sources there.
REM example: <Temp>\VMicroBuilds\<project-name>\<board-name>

REM The parameter {build.path} of the Arduino IDE points to a bulk folder (with backslash at the end).
REM The source files are located in the subdirectory 'sketch'.
REM Example: <temp>\buildc8b6589afc1136b3b8ee8306c9cc9978.tmp\sketch

REM Syntax: recipe.hooks.sketch.prebuild.#.pattern = <path>ArduinoPrebuild.cmd "{sketch_path}" "{build.path}\AppVersion.h"
REM                                                               %0                %1               %2

REM If % ~ 1 contains the text '{sketch_path}', the compilation is done by the Arduino IDE


IF "%~1" == "{sketch_path}" GOTO Arduino

REM -- Visual Micro -------------------------------
ECHO ***** Executing Visual Micro
Set DEST=%~1
Set SOURCE=%~2
goto DoIt

REM -- Arduino-IDE --------------------------------
:Arduino
ECHO ***** Executing Arduino
Set DEST=.
REM <Prozent>~dp fügt einen Backslash an!
Set SOURCE=%~dp2sketch\%~nx2

REM -- Ausführung --------------------------------
:DoIt
echo SOURCE: %SOURCE%
echo DEST: %DEST%
IncrementVersion %SOURCE% -M
IF EXIST "%SOURCE%" copy "%SOURCE%" "%DEST%"

Update 23.4.2016

Wegen der leicht unterschiedlichen Verzeichnisstruktur den verschiedenen AtmelStudio-Versionen und der vollständig anderen Aufruf-Systematik des Arduino-Plugin 'VisualMicro' für das 'VisualStudio', habe ich einen Ordner 'UllisTools' im Programm-Verzeichnis angelegt, die Environment-Variable Path um dieses Verzeichnis ergänzt und 'IncrementVersion.exe' und andere Programmdateien dorthin kopiert und. Nun kann ich sämtliche selbst geschriebenen Programme ohne Angabe eines Pfades aufrufen.

Bei dem Arduino-Plugin 'VisualMicro' für das 'VisualStudio', wie auch in der Arduino-IDE wird der Build-Vorgang durch die Konfigurationsdatei "platform.txt" gesteuert. Hier kann man über sog. "recipe.hooks" zu diversen Zeitpunkten im Build-Vorgang zusätzliche Programme starten. IncrementVersion kann man durch das anfügen folgender Zeile ausführen lassen:

recipe.hooks.sketch.prebuild.1.pattern=IncrementVersion.exe "{sketch_path}\appversion.h" -M

Diese Einstellung wirkt auf alle Projekte. Leider nicht einfach möglich ist, projektspezifische Einstellungen vorzunehmen. Bei Projekte, die keine Datei "appversion.h" mit dem passenden Inhalt besitzen, würde das Aufrufen von IncrementVersion zu einem Fehler führen. Die Version 1.2 besitzt deshalb einen zusätzlichen Kommandozeilenparameter "-M" (oder "-m" oder "/M" oder "/m"), der bei fehlender Datei "appversion.h" eine fehlerfreie Ausführung zurückmeldet.

 


 

Ab dem AVR Studio 5 (ebenso im ATMEL Studio 6 & 7) besteht die Möglichkeit, in den Buildprozess einzugreifen. Diese Möglichkeit gab es bei den vorhergehenden Version im Prinzip auch, indem man eine externe Makefile erstellte. Jedoch musste man dann bei jeder Änderung im Projekt die Makefile händisch anpassen, insbesondere durfte man das nicht vergessen.

Beim AVR Studio 5 kann man nun sogenannte Pre-Build- und Post-Build-Ereignisse definieren. Das sind Programme, die vor bzw. nach dem eigentlichen Build-Vorgang ausgeführt werden. Somit kann man also nahezu beliebige Dinge für den Buildvorgang vorbereiten, ohne die durch das AVR Studio aktuell gehaltene Makefile anpassen zu müssen.

Mir ging es häufig so, dass ich nicht genau wusste, ob die in die aktuell überarbeitete und kompilierte Programmversion bereits auf den Chip geflasht wurde. Dies wäre insbesondere beim Debuggen eine nützliche Information. Ich habe es mit dem __DATE__ und dem __DATE__ Makro versucht, jedoch basieren die auf dem Zeitstempel der Datei. Wenn die Datei nicht geändert wird, liefern diese Makros immer den gleichen Wert.

Für das AVR Studio 5 habe ich mich ein kleines Programm geschrieben, dass in einer Datei ein vorbestimmten Text sucht, der eine Versions- bzw. eine Build-Nummer enthält, und diese inkrementiert. Dieses Programm habe ich als Pre-Build-Event eingebunden. Damit das Programm während des Build-Vorgangs gefunden werden kann, habe ich es in das "GlobalToolchain"-Verzeichnis kopiert. Dies ist bei mir: "... \Atmel\AVR Studio 5.0\AVR ToolChain\bin\".

Bei jedem "Erstellen"-Aufruf im AVR Studio wird nun als erstes die Build-Nummer erhöht und dann mit der eigentlichen Kompilierung begonnen. Der Erkennungstext ist: "#define VERSIONSTRING "x.x.b". "x." kann beliebig häufig auftauchen und kann alphanumerisch sein. "b" muss numerisch sein.

Ich binde in jedes Projekt eine Header-Datei mit dem Namen "AppVersion.h" ("version.h" gibt es bereits in der avr-lib) und folgendem Aufbau ein:

/*
 * AppVersion.h
 *
 * Project: CNC-Control
 * Created: 04.11.2011
 *  Author: Ulli
 */ 
#ifndef APPVERSION_H_
#define APPVERSION_H_
#define VERSIONSTRING "1.1.1"
/*
-----------------------------------------------------
Version 1.1: (15.1.2012)
- Spindelsteuerung eingebaut
-----------------------------------------------------
Version 1.0: (4.11.2011)
- Basisversion
*/

#endif /* APPVERSION_H_ */

Mit der Version 1.1 (seit 16.11.2013) bearbeitet das Programm auch die folgende Dateistruktur. Inkrementiert wird die Zahl die mit "#define VERSION_BUILD" beginnt. Diese Strukur hat den Vorteil, dass die Komponenten der Versionsnummer einzeln abrufbar sind. Über die beiden zusätzlich definierten Makros kann aus den Zahlenwerten ein String zusammengebaut werden.

/*
 * AppVersion.h
 *
 * Project: CNC-Control
 * Created: 04.11.2011
 *  Author: Ulli
 */ 
#ifndef APPVERSION_H_
#define APPVERSION_H_

#define VERSION_MAJOR 1
#define VERSION_MINOR 0
#define VERSION_BUILD 202

//Zum Zusammenbau der Versionsnummer als String
#define APPVERSION_STR_HELPER(x) #x
#define APPVERSION_STR(x) APPVERSION_STR_HELPER(x)

#define VERSIONSTRING APPVERSION_STR(VERSION_MAJOR) "." APPVERSION_STR(VERSION_MINOR) "." APPVERSION_STR(VERSION_BUILD)

/*
-----------------------------------------------------
Version 1.1: (15.1.2012)
- Spindelsteuerung eingebaut
-----------------------------------------------------
Version 1.0: (4.11.2011)
- Basisversion
*/

#endif /* APPVERSION_H_ */

Im AVR Studio muss dann noch das Pre-Build-Ereignis definiert werden. Dies geschieht bei den Projekt-Eigenschaften.

Screenshot Projekt-Eigenschaften

Hier der Text für Copy&Paste: <<"$(GlobalToolchainPath)\IncrementVersion.exe" "$(MSBuildProjectDirectory)\AppVersion.h">> <<"IncrementVersion.exe" "$(MSBuildProjectDirectory)\AppVersion.h">> (s.o.). Die Anführungszeichen, die das Kommando und den Parameter einschließen, sind notwendig, wenn die Pfade Leerzeichen enthalten.

Das Binary und die Quellen zum download (Version 1.2).