From 8704b93623e38007a3a3bf34d10076f8973dc5ed Mon Sep 17 00:00:00 2001 From: JOAO LOPES Date: Mon, 4 Mar 2019 14:59:46 -0300 Subject: [PATCH] Version 2.1.0 - Create precompiler DEBUG_DISABLED to compile for production/release, equal that have in SerialDebug - Adjustments in examples --- README.md | 13 +- .../RemoteDebug_Advanced.ino | 299 +++++++++++------- .../RemoteDebug_Basic/RemoteDebug_Basic.ino | 84 ++--- library.json | 2 +- library.properties | 2 +- src/RemoteDebug.cpp | 31 +- src/RemoteDebug.h | 76 +++-- 7 files changed, 330 insertions(+), 177 deletions(-) mode change 100755 => 100644 examples/RemoteDebug_Advanced/RemoteDebug_Advanced.ino mode change 100755 => 100644 examples/RemoteDebug_Basic/RemoteDebug_Basic.ino diff --git a/README.md b/README.md index 483ba83..cd7584a 100755 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Library for Arduino to debug devices over WiFi (telnet) with Print commands like ![logo](extras/readme_media/logo.png) -[![build badge](https://img.shields.io/badge/version-v2.0.2-blue.svg)](#releases) +[![build badge](https://img.shields.io/badge/version-v2.1.0-blue.svg)](#releases) [![Codacy Badge](https://api.codacy.com/project/badge/Grade/3eadfd19246f4808907cf53599a6b9f0)](https://www.codacy.com/app/JoaoLopesF/RemoteDebug?utm_source=github.com&utm_medium=referral&utm_content=JoaoLopesF/RemoteDebug&utm_campaign=Badge_Grade) [![platform badge](https://img.shields.io/badge/platform-Arduino_Espressif-orange.svg)](https://github.com/arduino) [![GitHub](https://img.shields.io/github/license/mashape/apistatus.svg)](https://github.com/JoaoLopesF/RemoteDebug/blob/master/LICENSE.txt) @@ -346,6 +346,7 @@ debugHandle(); // Equal to SerialDebug In any place of you code: ```cpp +#ifndef DEBUG_DISABLED if (Debug.isActive(Debug.)) { Debug.printf("bla bla bla: %d %s", number, str); // OR Debug.printf("bla bla bla: %d %s", number, str.c_str()); // Note: if type is String need c_str() // OR @@ -355,8 +356,12 @@ if (Debug.isActive(Debug.)) { // you can use my ArduinoUtil library -> https://github.com/JoaoLopesF/ArduinoUtil Debug.printf("float: %s\n", Util.formatFloat(value, 0, 5).c_str()); } +#endif ``` +Note: Using isActive, you need surround the code by DEBUG_DISABLE precompile condition, + to avoid compile it for production/release + Or short way (equal to SerialDebug) (prefered if only one debug at time): ```cpp @@ -462,6 +467,12 @@ In advanced sample, I used WifiManager library, ArduinoOTA and mDNS, please see ## Releases +### 2.1.0 - 2019-03-04 + + - Create precompiler DEBUG_DISABLED to compile for production/release, + equal that have in SerialDebug + - Adjustments in examples + ### 2.0.1 - 2019-03-01 - Adjustments for the debugger: it still disable until dbg command, equal to SerialDebug diff --git a/examples/RemoteDebug_Advanced/RemoteDebug_Advanced.ino b/examples/RemoteDebug_Advanced/RemoteDebug_Advanced.ino old mode 100755 new mode 100644 index 79367e6..983c257 --- a/examples/RemoteDebug_Advanced/RemoteDebug_Advanced.ino +++ b/examples/RemoteDebug_Advanced/RemoteDebug_Advanced.ino @@ -1,23 +1,25 @@ //////// -// Libraries Arduino -// // Library: Remote debug - debug over telnet - for Esp8266 (NodeMCU) or ESP32 -// Author: Joao Lopes +// Author : Joao Lopes +// File : RemoteDebug_Advanced.ino +// Notes : // // Attention: This library is only for help development. Please not use this in production // -// Sample to show how to use it - advanced one +// Sample to show how to use advanced features of Arduino and RemoteDebug library // // Example of use: // +//#ifndef DEBUG_DISABLED // if (Debug.isActive(Debug.)) { // <--- This is very important to reduce overheads and work of debug levels // Debug.printf("bla bla bla: %d %s\n", number, str); // Debug.println("bla bla bla"); // } +//#endif // // Or short way (prefered if only one debug at time) // -// debugAln("This is a any (always showed) - var %d", var); +// debugA("This is a any (always showed) - var %d", var); // // debugV("This is a verbose - var %d", var); // debugD("This is a debug - var %d", var); @@ -27,31 +29,74 @@ // // debugV("This is println"); // -// /////// -// Libraries +////// Defines + +// Host name (please change it) + +#define HOST_NAME "remotedebug" + +// Board especific libraries -#if defined (ESP8266) +#if defined ESP8266 || defined ESP32 + +// Use mDNS ? (comment this do disable it) + +#define USE_MDNS true + +// Arduino OTA (uncomment this to enable) + +//#define USE_ARDUINO_OTA true + +#else -#define USE_MDNS true // Use the MDNS ? +// RemoteDebug library is now only to Espressif boards, +// as ESP32 and ESP82266, +// If need for another WiFi boards, +// please add an issue about this +// and we will see if it is possible made the port for your board. +// access: https://github.com/JoaoLopesF/RemoteDebug/issues -#include //https://github.com/esp8266/Arduino +#error "The board must be ESP8266 or ESP32" + +#endif // ESP + +// Web server (uncomment this to need this) + +//#define WEB_SERVER_ENABLED true + +// Disable all debug ? +// +// Important to compile for prodution/release +// Disable all debug ? Good to release builds (production) +// as nothing of RemoteDebug is compiled, zero overhead :-) +// For it just uncomment the DEBUG_DISABLED +// On change it, if in another IDE than Arduino IDE, like Eclipse or VSCODE, +// please clean the project, before compile + +//#define DEBUG_DISABLED true + +////// Includes + +#if defined ESP8266 + +// Includes of ESP8266 + +#include #ifdef USE_MDNS #include #include #endif -//#include // Discomment if you need web server` - -#define USE_ARDUINO_OTA true - -#elif defined(ESP32) +#ifdef WEB_SERVER_ENABLED +#include +#endif -#define USE_MDNS true // Use the MDNS ? +#elif defined ESP32 -// Includes do ESP32 +// Includes of ESP32 #include @@ -60,69 +105,79 @@ #include "ESPmDNS.h" #endif -#define USE_ARDUINO_OTA true +#ifdef WEB_SERVER_ENABLED +#include +#endif #else -#error "The board must be ESP8266 or ESP32" +#error "Now RemoteDebug support only boards Espressif, as ESP8266 and ESP32" #endif // ESP -// ArduinoOTA +// Arduino OTA #ifdef USE_ARDUINO_OTA #include #endif -// Production +// HTTP Web server -//#define PRODUCTION true +#ifdef WEB_SERVER_ENABLED -// HTTP Web server - discomment if you need this -//#if defined ESP8266 -//ESP8266WebServer HTTPServer(80); -//#elif defined ESP32 -//WebServer HTTPServer(80); -//#endif +#if defined ESP8266 + +ESP8266WebServer HTTPServer(80); -// Remote debug over telnet - not recommended for production, only for development -// I put it to show how to do code clean to development and production +#elif defined ESP32 + +WebServer HTTPServer(80); + +#endif + +#endif // WEB_SERVER_ENABLED -#ifndef PRODUCTION // Not in PRODUCTION +// Remote debug over telnet - not recommended for production/release, only for development #include "RemoteDebug.h" //https://github.com/JoaoLopesF/RemoteDebug +#ifndef DEBUG_DISABLED // Only if debug is not disabled (for production/release) + // Instance of RemoteDebug RemoteDebug Debug; #endif -// Host name +// WiFi credentials +// Note: if commented, is used the smartConfig +// That allow to it in mobile app +// See more details in http://www.iotsharing.com/2017/05/how-to-use-smartconfig-on-esp32.html -#define HOST_NAME "rem-debug" // PLEASE CHANGE IT +//#define WIFI_SSID "..." // your network SSID (name) +//#define WIFI_PASS "..." // your network key + +/////// Variables // Time -uint32_t mLastTime = 0; +uint32_t mTimeToSec = 0; uint32_t mTimeSeconds = 0; -// Buildin Led ON ? - -boolean mLedON = false; - ////// Setup void setup() { - // Initialize the Serial (educattional use only, not need in production) + // Initialize the Serial (use only in setup codes) - Serial.begin(115200); + Serial.begin(230400); - // Buildin led of ESP + // Buildin led +#ifdef LED_BUILTIN pinMode(LED_BUILTIN, OUTPUT); digitalWrite(LED_BUILTIN, LOW); +#endif // Connect WiFi @@ -142,34 +197,40 @@ void setup() { // Register host name in mDNS -#if defined (USE_MDNS) && defined(HOSTNAME) +#if defined USE_MDNS && defined HOST_NAME + if (MDNS.begin(HOST_NAME)) { Serial.print("* MDNS responder started. Hostname -> "); Serial.println(HOST_NAME); } + // Register the services - // MDNS.addService("http", "tcp", 80); // Web server - discomment if you need this +#ifdef WEB_SERVER_ENABLED + MDNS.addService("http", "tcp", 80); // Web server +#endif +#ifndef DEBUG_DISABLED MDNS.addService("telnet", "tcp", 23);// Telnet server RemoteDebug #endif +#endif // MDNS + // HTTP web server - // Discomment if you need this - // - // HTTPServer.on("/", handleRoot); - // - // HTTPServer.onNotFound(handleNotFound); - // - // HTTPServer.begin(); -// -// #ifndef PRODUCTION // Not in PRODUCTION -// Serial.println("* HTTP server started"); -// #endif - // Initialize the telnet server of RemoteDebug +#ifdef WEB_SERVER_ENABLED + HTTPServer.on("/", handleRoot); + + HTTPServer.onNotFound(handleNotFound); -#ifndef PRODUCTION // Not in PRODUCTION + HTTPServer.begin(); + + Serial.println("* HTTP server started"); +#endif + +#ifndef DEBUG_DISABLED // Only for development + + // Initialize the telnet server of RemoteDebug Debug.begin(HOST_NAME); // Initiaze the telnet server @@ -177,23 +238,21 @@ void setup() { Debug.setResetCmdEnabled(true); // Enable the reset command - //Debug.showDebugLevel(false); // To not show debug levels - //Debug.showTime(true); // To show time - //Debug.showProfiler(true); // To show profiler - time between messages of Debug - // Good to "begin ...." and "end ...." messages + Debug.showProfiler(true); // Profiler (Good to measure times, to optimize codes) - Debug.showProfiler(true); // Profiler Debug.showColors(true); // Colors // Debug.setSerialEnabled(true); // if you wants serial echo - only recommended if ESP is plugged in USB + // Project commands + String helpCmd = "bench1 - Benchmark 1\n"; helpCmd.concat("bench2 - Benchmark 2"); Debug.setHelpProjectsCmds(helpCmd); Debug.setCallBackProjectCmds(&processCmdRemoteDebug); - // This sample + // End of setup - show IP Serial.println("* Arduino RemoteDebug Library"); Serial.println("*"); @@ -215,29 +274,28 @@ void setup() { void loop() { -#ifndef PRODUCTION // Not in PRODUCTION +#ifndef DEBUG_DISABLED // Time of begin of this loop uint32_t timeBeginLoop = millis(); #endif // Each second - if ((millis() - mLastTime) >= 1000) { + if (millis() >= mTimeToSec) { // Time - mLastTime = millis(); + mTimeToSec = millis() + 1000; mTimeSeconds++; // Blink the led - mLedON = !mLedON; - digitalWrite(LED_BUILTIN, (mLedON) ? LOW : HIGH); - -#ifndef PRODUCTION // Not in PRODUCTION +#ifdef LED_BUILTIN + digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN)); +#endif - // Debug the time (verbose level) (without shortcut) + // Debug the time (verbose level) debugV("* Time: %u seconds (VERBOSE)", mTimeSeconds); @@ -251,12 +309,25 @@ void loop() { debugW("* This is a message of debug level WARNING"); debugE("* This is a message of debug level ERROR"); - // Call a function - foo(); + // RemoteDebug isActive? Use this RemoteDebug sintaxe if you need process anything only for debug + // It is good to avoid overheads (this is only use that is suggest to use isActive) + // Note this need be surrounded by DEBUG_DISABLED precompiler condition to not compile for production/release - } +#ifndef DEBUG_DISABLED + + if (Debug.isActive(Debug.VERBOSE)) { + + Debug.println("Calling a foo function"); + Debug.printf("At time of %d sec.\n", mTimeSeconds); + + // Call a function + + foo(); + } #endif + + } } ////// Services on Wifi @@ -267,27 +338,25 @@ void loop() { ArduinoOTA.handle(); #endif - //// Web server - // Discomment if you need this - // - // HTTPServer.handleClient(); +#ifdef WEB_SERVER_ENABLED + // Web server -#ifndef PRODUCTION // Not in PRODUCTION + HTTPServer.handleClient(); +#endif +#ifndef DEBUG_DISABLED // Remote debug over telnet Debug.handle(); - #endif // Give a time for ESP yield(); +#ifndef DEBUG_DISABLED // Show a debug - warning if time of these loop is over 50 (info) or 100 ms (warning) -#ifndef PRODUCTION // Not in PRODUCTION - uint32_t time = (millis() - timeBeginLoop); if (time > 100) { @@ -310,7 +379,7 @@ void foo() { debugV("This is a println"); } -#ifndef PRODUCTION // Not in PRODUCTION +#ifndef DEBUG_DISABLED // Process commands from RemoteDebug @@ -359,6 +428,8 @@ void processCmdRemoteDebug() { } #endif +////// WiFi + void connectWiFi() { ////// Connect WiFi @@ -375,7 +446,11 @@ void connectWiFi() { // Connect with SSID and password stored +#ifndef WIFI_SSID WiFi.begin(); +#else + WiFi.begin(WIFI_SSID, WIFI_PASS); +#endif // Wait connection @@ -390,6 +465,7 @@ void connectWiFi() { if (WiFi.status() != WL_CONNECTED) { +#ifndef WIFI_SSID // SmartConfig WiFi.beginSmartConfig(); @@ -414,6 +490,10 @@ void connectWiFi() { delay(500); Serial.print("."); } +#else + Serial.println("Not possible connect to WiFi, rebooting"); + ESP.restart(); +#endif } // End @@ -488,32 +568,35 @@ void initializeOTA() { #endif +#ifdef WEB_SERVER_ENABLED + /////////// Handles -// Discomment if you need this -// -// void handleRoot() { -// -// // Root web page -// -// HTTPServer.send(200, "text/plain", "hello from esp8266 - RemoteDebug Sample!"); -// } -// -// void handleNotFound(){ -// -// // Page not Found -// -// String message = "File Not Found\n\n"; -// message.concat("URI: "); -// message.concat(HTTPServer.uri()); -// message.concat("\nMethod: "); -// message.concat((HTTPServer.method() == HTTP_GET)?"GET":"POST"); -// message.concat("\nArguments: "); -// message.concat(HTTPServer.args()); -// message.concat("\n"); -// for (uint8_t i=0; i)) { // <--- This is very important to reduce overheads and work of debug levels // Debug.printf("bla bla bla: %d %s\n", number, str); // Debug.println("bla bla bla"); // } +//#endif // // Or short way (prefered if only one debug at time) // -// debugAln("This is a any (always showed) - var %d", var); +// debugA("This is a any (always showed) - var %d", var); // // debugV("This is a verbose - var %d", var); // debugD("This is a debug - var %d", var); @@ -30,13 +32,42 @@ // /////// -// Libraries +////// Defines + +// Host name (please change it) + +#define HOST_NAME "remotedebug" + +// Board especific libraries + +#if defined ESP8266 || defined ESP32 + +// Use mDNS ? (comment this do disable it) + +#define USE_MDNS true + +// Arduino OTA (uncomment this to enable) + +//#define USE_ARDUINO_OTA true + +#else + +// RemoteDebug library is now only to Espressif boards, +// as ESP32 and ESP82266, +// If need for another WiFi boards, +// please add an issue about this +// and we will see if it is possible made the port for your board. +// access: https://github.com/JoaoLopesF/RemoteDebug/issues + +#error "The board must be ESP8266 or ESP32" + +#endif // ESP -#if defined (ESP8266) +//////// Libraries -#define USE_MDNS true // Use the MDNS ? +#if defined ESP8266 -// Includes do ESP8266 +// Includes of ESP8266 #include @@ -45,11 +76,9 @@ #include #endif -#elif defined(ESP32) - -#define USE_MDNS true // Use the MDNS ? +#elif defined ESP32 -// Includes do ESP32 +// Includes of ESP32 #include @@ -58,10 +87,6 @@ #include "ESPmDNS.h" #endif -#else - -#error "The board must be ESP8266 or ESP32" - #endif // ESP // Remote debug over telnet - not recommended for production, only for development @@ -75,38 +100,25 @@ RemoteDebug Debug; const char* ssid = "........"; const char* password = "........"; -// Host name - -#define HOST_NAME "rem-debug" // PLEASE CHANGE IT // Time uint32_t mLastTime = 0; uint32_t mTimeSeconds = 0; -// Buildin Led ON ? - -boolean mLedON = false; - ////// Setup void setup() { - // Initialize the Serial (educattional use only, not need in production) + // Initialize the Serial (use only in setup codes) - Serial.begin(115200); + Serial.begin(230400); // Buildin led of ESP pinMode(LED_BUILTIN, OUTPUT); digitalWrite(LED_BUILTIN, LOW); -#ifdef ESP32 - // ESP32 configuration // TODO: see if it is necessary - WiFi.enableSTA(true); - delay(1000); -#endif - // Debug Serial.println("**** Setup: initializing ..."); @@ -154,12 +166,11 @@ void setup() { Debug.setResetCmdEnabled(true); // Enable the reset command - //Debug.showTime(true); // To show time + Debug.showProfiler(true); // Profiler (Good to measure times, to optimize codes) - // Debug.showProfiler(true); // To show profiler - time between messages of Debug - // Good to "begin ...." and "end ...." messages + Debug.showColors(true); // Colors - // This sample (serial -> educattional use only, not need in production) + // End off setup Serial.println("* Arduino RemoteDebug Library"); Serial.println("*"); @@ -189,8 +200,7 @@ void loop() // Blink the led - mLedON = !mLedON; - digitalWrite(LED_BUILTIN, (mLedON)?LOW:HIGH); + digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN)); // Debug the time (verbose level) diff --git a/library.json b/library.json index 251271e..d8892ac 100755 --- a/library.json +++ b/library.json @@ -7,7 +7,7 @@ "type": "git", "url": "https://github.com/JoaoLopesF/RemoteDebug.git" }, - "version": "2.0.2", + "version": "2.1.0", "frameworks": "arduino", "platforms": "*" } diff --git a/library.properties b/library.properties index 94ab357..da093a8 100755 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=RemoteDebug -version=2.0.2 +version=2.1.0 author=Joao Lopes maintainer=Joao Lopes sentence=Remote debug over telnet for Arduino ESP8266 and ESP32 diff --git a/src/RemoteDebug.cpp b/src/RemoteDebug.cpp index 76720ae..e834303 100644 --- a/src/RemoteDebug.cpp +++ b/src/RemoteDebug.cpp @@ -8,6 +8,10 @@ * * Versions: * ------ ---------- ----------------- + * 2.1.0 2019-03-04 Create precompiler DEBUG_DISABLED to compile for production/release, + * equal that have in SerialDebug + * Adjustments in examples + * * 2.0.2 2019-03-03 Just to do new release, to update other files * 2.0.1 2019-03-01 Adjustments for the debugger: it still disable until dbg command, equal to SerialDebug * The callback will to be called before print debug messages now @@ -56,6 +60,12 @@ * - Add support to another Arduino WiFi boards (if have demand on it) */ +///// Debug disable for compile to production/release +///// as nothing of RemotedDebug is compiled, zero overhead :-) +//#define DEBUG_DISABLED true // Uncomment if the IDE did not recognize, to force it + +#ifndef DEBUG_DISABLED + ///// Includes #include "stdint.h" @@ -108,10 +118,6 @@ bool system_update_cpu_freq(uint8_t freq); ////// Variables -//// Self instance -// -//RemoteDebug *self; - // Telnet server WiFiServer TelnetServer(TELNET_PORT); // @suppress("Abstract class cannot be instantiated") @@ -120,14 +126,11 @@ WiFiClient TelnetClient; // @suppress("Abstract class cannot be instantiated") ////// Methods / routines // Constructor - -RemoteDebug::RemoteDebug() { - -// // Save the self instance // -// self = this; +//RemoteDebug::RemoteDebug() { +// +//} -} // Initialize the telnet server bool RemoteDebug::begin(String hostName, uint8_t startingDebugLevel) { @@ -1534,4 +1537,12 @@ void RemoteDebug::sendTelnetCommand(uint8_t command, uint8_t option) { } #endif +#else // DEBUG_DISABLED + +/////// All debug is disabled, this include is to define empty debug macros + +#include "RemoteDebug.h" // This library + +#endif // DEBUG_DISABLED + /////// End diff --git a/src/RemoteDebug.h b/src/RemoteDebug.h index 013c1bc..be16fe8 100644 --- a/src/RemoteDebug.h +++ b/src/RemoteDebug.h @@ -19,8 +19,14 @@ * */ -#ifndef RemoteDebug_h -#define RemoteDebug_h +#ifndef REMOTEDEBUG_H +#define REMOTEDEBUG_H + +///// Debug disable for compile to production/release +///// as nothing of RemotedDebug is compiled, zero overhead :-) +//#define DEBUG_DISABLED true // Uncomment if the IDE did not recognize, to force it + +#ifndef DEBUG_DISABLED //////// Includes @@ -45,13 +51,6 @@ //////// Defines -// Disable all debug ? Good to release builds (production) -// as nothing of RemotedDebug is compiled, zero overhead :-) -// For it just uncomment -//#define DEBUG_DISABLED true - -// TODO: fazer isto - // Port for telnet server (now can be defined in project too - 17/08/18) // Can be by project, just define it before include this file @@ -110,14 +109,7 @@ #define COLOR_CYAN "\x1B[0;36m" #define COLOR_WHITE "\x1B[0;37m" -//#define COLOR_DARK_BLACK "\x1B[1;30m" -//#define COLOR_DARK_RED "\x1B[1;31m" -//#define COLOR_DARK_GREEN "\x1B[1;32m" -//#define COLOR_DARK_YELLOW "\x1B[1;33m" -//#define COLOR_DARK_BLUE "\x1B[1;34m" -//#define COLOR_DARK_MAGENTA "\x1B[1;35m" -//#define COLOR_DARK_CYAN "\x1B[1;36m" -//#define COLOR_DARK_WHITE "\x1B[1;37m" +#define COLOR_DARK_BLACK "\x1B[1;30m" #define COLOR_LIGHT_RED "\x1B[1;31m" #define COLOR_LIGHT_GREEN "\x1B[1;32m" @@ -265,7 +257,7 @@ class RemoteDebug: public Print // Constructor - RemoteDebug(); +// RemoteDebug(); // Methods @@ -418,6 +410,52 @@ class RemoteDebug: public Print #endif }; -#endif +#else // DEBUG_DISABLED + +// Disable debug macros + +#define rdebugAln(...) +#define rdebugPln(...) +#define rdebugVln(...) +#define rdebugDln(...) +#define rdebugIln(...) +#define rdebugWln(...) +#define rdebugEln(...) +#define rdebug(...) + +#define DEBUG(...) + +#define DEBUG_A(...) +#define DEBUG_P(...) +#define DEBUG_V(...) +#define DEBUG_D(...) +#define DEBUG_I(...) +#define DEBUG_W(...) +#define DEBUG_E(...) + +#define debugA(...) +#define debugP(...) +#define debugV(...) +#define debugD(...) +#define debugI(...) +#define debugW(...) +#define debugE(...) + +#define debugHandle() + +// Note all of Debug. codes need uses "#ifndef DEBUG_DISABLED" +// For example, the initialization codes and: + +//#ifndef DEBUG_DISABLED +//if (Debug.isActive(Debug.VERBOSE)) { +// Debug.printf("bla bla bla: %d %s", number, str); // OR +// Debug.printf("bla bla bla: %d %s", number, str.c_str()); // Note: if type is String need c_str() // OR +// Debug.println("bla bla bla 2 ln"); +//} +//#endif + +#endif // DEBUG_DISABLED + +#endif // H /// End