RemoteDebug.h
15 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
/*
* Header for RemoteDebug
*
* MIT License
*
* Copyright (c) 2019 Joao Lopes
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*
*/
#ifndef REMOTEDEBUG_H
#define REMOTEDEBUG_H
#pragma once
///// RemoteDebug configuration
#include "RemoteDebugCfg.h"
// Debug enabled ?
#ifndef DEBUG_DISABLED
//////// Defines
// New color system (comment this to return to old system) - 2019-02-27
#define COLOR_NEW_SYSTEM true
// ANSI Colors
#define COLOR_RESET "\x1B[0m"
#define COLOR_BLACK "\x1B[0;30m"
#define COLOR_RED "\x1B[0;31m"
#define COLOR_GREEN "\x1B[0;32m"
#define COLOR_YELLOW "\x1B[0;33m"
#define COLOR_BLUE "\x1B[0;34m"
#define COLOR_MAGENTA "\x1B[0;35m"
#define COLOR_CYAN "\x1B[0;36m"
#define COLOR_WHITE "\x1B[0;37m"
#define COLOR_DARK_BLACK "\x1B[1;30m"
#define COLOR_LIGHT_RED "\x1B[1;31m"
#define COLOR_LIGHT_GREEN "\x1B[1;32m"
#define COLOR_LIGHT_YELLOW "\x1B[1;33m"
#define COLOR_LIGHT_BLUE "\x1B[1;34m"
#define COLOR_LIGHT_MAGENTA "\x1B[1;35m"
#define COLOR_LIGHT_CYAN "\x1B[1;36m"
#define COLOR_LIGHT_WHITE "\x1B[1;37m"
#define COLOR_BACKGROUND_BLACK "\x1B[40m"
#define COLOR_BACKGROUND_RED "\x1B[41m"
#define COLOR_BACKGROUND_GREEN "\x1B[42m"
#define COLOR_BACKGROUND_YELLOW "\x1B[43m"
#define COLOR_BACKGROUND_BLUE "\x1B[44m"
#define COLOR_BACKGROUND_MAGENTA "\x1B[45m"
#define COLOR_BACKGROUND_CYAN "\x1B[46m"
#define COLOR_BACKGROUND_WHITE "\x1B[47m"
#ifdef COLOR_NEW_SYSTEM
// New system of Colors
// Note: this colors is not equals to SerialDebug colors, due using standard 16 colors of Ansi, for compatibility
#define COLOR_VERBOSE COLOR_GREEN
#define COLOR_DEBUG COLOR_LIGHT_GREEN
//#define COLOR_INFO COLOR_YELLOW
//#define COLOR_WARNING COLOR_CYAN
//#define COLOR_ERROR COLOR_RED
#define COLOR_INFO COLOR_LIGHT_YELLOW
#define COLOR_WARNING COLOR_LIGHT_CYAN
#define COLOR_ERROR COLOR_LIGHT_RED
#define COLOR_RAW COLOR_WHITE // COLOR_MAGENTA
#endif
//////// Includes
#include "Arduino.h"
#include "Print.h"
// ESP8266 or ESP32 ?
#if defined(ESP8266)
#include <ESP8266WiFi.h>
#elif defined(ESP32)
#include <WiFi.h>
#else
#error "Only for ESP8266 or ESP32"
#endif
////// Shortcuts macros
// Auto function for debug macros?
#ifndef DEBUG_DISABLE_AUTO_FUNC // With auto func
#ifdef ESP32
// ESP32 -> Multicore - show core id ?
#define DEBUG_AUTO_CORE true // debug show core id ? (comment to disable it)
#ifdef DEBUG_AUTO_CORE
#define rdebugA(fmt, ...) if (Debug.isActive(Debug.ANY)) Debug.printf("(%s)(C%d) " fmt, __func__, xPortGetCoreID(), ##__VA_ARGS__)
#define rdebugP(fmt, ...) if (Debug.isActive(Debug.PROFILER)) Debug.printf("(%s)(C%d) " fmt, __func__, xPortGetCoreID(), ##__VA_ARGS__)
#define rdebugV(fmt, ...) if (Debug.isActive(Debug.VERBOSE)) Debug.printf("(%s)(C%d) " fmt, __func__, xPortGetCoreID(), ##__VA_ARGS__)
#define rdebugD(fmt, ...) if (Debug.isActive(Debug.DEBUG)) Debug.printf("(%s)(C%d) " fmt, __func__, xPortGetCoreID(), ##__VA_ARGS__)
#define rdebugI(fmt, ...) if (Debug.isActive(Debug.INFO)) Debug.printf("(%s)(C%d) " fmt, __func__, xPortGetCoreID(), ##__VA_ARGS__)
#define rdebugW(fmt, ...) if (Debug.isActive(Debug.WARNING)) Debug.printf("(%s)(C%d) " fmt, __func__, xPortGetCoreID(), ##__VA_ARGS__)
#define rdebugE(fmt, ...) if (Debug.isActive(Debug.ERROR)) Debug.printf("(%s)(C%d) " fmt, __func__, xPortGetCoreID(), ##__VA_ARGS__)
#endif
#endif
#ifndef DEBUG_AUTO_CORE // No auto core or for ESP8266
#define rdebugA(fmt, ...) if (Debug.isActive(Debug.ANY)) Debug.printf("(%s) " fmt, __func__, ##__VA_ARGS__)
#define rdebugP(fmt, ...) if (Debug.isActive(Debug.PROFILER)) Debug.printf("(%s) " fmt, __func__, ##__VA_ARGS__)
#define rdebugV(fmt, ...) if (Debug.isActive(Debug.VERBOSE)) Debug.printf("(%s) " fmt, __func__, ##__VA_ARGS__)
#define rdebugD(fmt, ...) if (Debug.isActive(Debug.DEBUG)) Debug.printf("(%s) " fmt, __func__, ##__VA_ARGS__)
#define rdebugI(fmt, ...) if (Debug.isActive(Debug.INFO)) Debug.printf("(%s) " fmt, __func__, ##__VA_ARGS__)
#define rdebugW(fmt, ...) if (Debug.isActive(Debug.WARNING)) Debug.printf("(%s) " fmt, __func__, ##__VA_ARGS__)
#define rdebugE(fmt, ...) if (Debug.isActive(Debug.ERROR)) Debug.printf("(%s) " fmt, __func__, ##__VA_ARGS__)
#endif
#else // Without auto func
#define rdebugA(fmt, ...) if (Debug.isActive(Debug.ANY)) Debug.printf(fmt, ##__VA_ARGS__)
#define rdebugP(fmt, ...) if (Debug.isActive(Debug.PROFILER)) Debug.printf(fmt, ##__VA_ARGS__)
#define rdebugV(fmt, ...) if (Debug.isActive(Debug.VERBOSE)) Debug.printf(fmt, ##__VA_ARGS__)
#define rdebugD(fmt, ...) if (Debug.isActive(Debug.DEBUG)) Debug.printf(fmt, ##__VA_ARGS__)
#define rdebugI(fmt, ...) if (Debug.isActive(Debug.INFO)) Debug.printf(fmt, ##__VA_ARGS__)
#define rdebugW(fmt, ...) if (Debug.isActive(Debug.WARNING)) Debug.printf(fmt, ##__VA_ARGS__)
#define rdebugE(fmt, ...) if (Debug.isActive(Debug.ERROR)) Debug.printf(fmt, ##__VA_ARGS__)
#endif
// With newline
#define rdebugAln(fmt, ...) rdebugA(fmt "\n", ##__VA_ARGS__)
#define rdebugPln(fmt, ...) rdebugP(fmt "\n", ##__VA_ARGS__)
#define rdebugVln(fmt, ...) rdebugV(fmt "\n", ##__VA_ARGS__)
#define rdebugDln(fmt, ...) rdebugD(fmt "\n", ##__VA_ARGS__)
#define rdebugIln(fmt, ...) rdebugI(fmt "\n", ##__VA_ARGS__)
#define rdebugWln(fmt, ...) rdebugW(fmt "\n", ##__VA_ARGS__)
#define rdebugEln(fmt, ...) rdebugE(fmt "\n", ##__VA_ARGS__)
// For old versions compatibility
#define rdebug(fmt, ...) rdebugA(fmt, ##__VA_ARGS__)
// Another way - for compatibility
#define DEBUG(fmt, ...) rdebugA(fmt, ##__VA_ARGS__)
#define DEBUG_A(fmt, ...) rdebugA(fmt, ##__VA_ARGS__)
#define DEBUG_P(fmt, ...) rdebugP(fmt, ##__VA_ARGS__)
#define DEBUG_V(fmt, ...) rdebugV(fmt, ##__VA_ARGS__)
#define DEBUG_D(fmt, ...) rdebugD(fmt, ##__VA_ARGS__)
#define DEBUG_I(fmt, ...) rdebugI(fmt, ##__VA_ARGS__)
#define DEBUG_W(fmt, ...) rdebugW(fmt, ##__VA_ARGS__)
#define DEBUG_E(fmt, ...) rdebugE(fmt, ##__VA_ARGS__)
// New way: To compatibility with SerialDebug (can use RemoteDebug or SerialDebug)
// This is my favorite :)
#define debugV(fmt, ...) rdebugVln(fmt, ##__VA_ARGS__)
#define debugD(fmt, ...) rdebugDln(fmt, ##__VA_ARGS__)
#define debugI(fmt, ...) rdebugIln(fmt, ##__VA_ARGS__)
#define debugW(fmt, ...) rdebugWln(fmt, ##__VA_ARGS__)
#define debugE(fmt, ...) rdebugEln(fmt, ##__VA_ARGS__)
#define debugA(fmt, ...) rdebugAln(fmt, ##__VA_ARGS__)
#define debugHandle() Debug.handle()
// Macros used by code converter for codes with several prints to only message
// due the converter cannot
// convert severals Serial.print in one debug* macro.
#define rprintV(x, ...) if (Debug.isActive(Debug.VERBOSE)) Debug.print(x, ##__VA_ARGS__)
#define rprintD(x, ...) if (Debug.isActive(Debug.DEBUG)) Debug.print(x, ##__VA_ARGS__)
#define rprintI(x, ...) if (Debug.isActive(Debug.INFO)) Debug.print(x, ##__VA_ARGS__)
#define rprintW(x, ...) if (Debug.isActive(Debug.WARNING)) Debug.print(x, ##__VA_ARGS__)
#define rprintE(x, ...) if (Debug.isActive(Debug.ERROR)) Debug.print(x, ##__VA_ARGS__)
#define rprintA(x, ...) if (Debug.isActive(Debug.ANY)) Debug.print(x, ##__VA_ARGS__)
#define rprintVln(x, ...) if (Debug.isActive(Debug.VERBOSE)) Debug.println(x, ##__VA_ARGS__)
#define rprintDln(x, ...) if (Debug.isActive(Debug.DEBUG)) Debug.println(x, ##__VA_ARGS__)
#define rprintIln(x, ...) if (Debug.isActive(Debug.INFO)) Debug.println(x, ##__VA_ARGS__)
#define rprintWln(x, ...) if (Debug.isActive(Debug.WARNING)) Debug.println(x, ##__VA_ARGS__)
#define rprintEln(x, ...) if (Debug.isActive(Debug.ERROR)) Debug.println(x, ##__VA_ARGS__)
#define rprintAln(x, ...) if (Debug.isActive(Debug.ANY)) Debug.println(x, ##__VA_ARGS__)
///// Class
class RemoteDebug: public Print
{
public:
// Constructor
RemoteDebug();
// Methods
bool begin(String hostName, uint16_t port, uint8_t startingDebugLevel = DEBUG);
bool begin(String hostName, uint8_t startingDebugLevel = DEBUG);
void setPassword(String password);
void stop();
void handle();
void disconnect(boolean onlyTelnetClient = false);
void setSerialEnabled(boolean enable);
void setResetCmdEnabled(boolean enable);
void setHelpProjectsCmds(String help);
void setCallBackProjectCmds(void (*callback)());
String getLastCommand();
void clearLastCommand();
void showTime(boolean show);
void showProfiler(boolean show, uint32_t minTime = 0);
void showDebugLevel(boolean show);
void showColors(boolean show);
void showRaw(boolean show);
void setCallBackNewClient(void (*callback)());
#ifdef ALPHA_VERSION // In test, not good yet
void autoProfilerLevel(uint32_t millisElapsed);
#endif
void setFilter(String filter);
void setNoFilter();
boolean isActive(uint8_t debugLevel = DEBUG);
void silence(boolean activate, boolean showMessage = true, boolean fromBreak = false, uint32_t timeout = 0);
boolean isSilence();
void onConnection(boolean connected);
boolean isConnected();
#ifdef DEBUGGER_ENABLED
// For Simple software debugger - based on SerialDebug Library
void initDebugger(boolean (*callbackEnabled)(), void (*callbackHandle)(const boolean), String (*callbackGetHelp)(), void (*callbackProcessCmd)());
WiFiClient* getTelnetClient();
#endif
#ifndef WEBSOCKET_DISABLED // For web socket server (app)
void wsOnReceive(const char* command);
void wsSendInfo();
void wsSendLevelInfo();
#endif
boolean wsIsConnected();
// Print
virtual size_t write(uint8_t);
virtual size_t write(const uint8_t *buffer, size_t size); // Insert due a write bug w/ latest Esp8266 SDK - 17/08/18
// Debug levels
static const uint8_t PROFILER = 0; // Used for show time of execution of pieces of code(profiler)
static const uint8_t VERBOSE = 1; // Used for show verboses messages
static const uint8_t DEBUG = 2; // Used for show debug messages
static const uint8_t INFO = 3; // Used for show info messages
static const uint8_t WARNING = 4; // Used for show warning messages
static const uint8_t ERROR = 5; // Used for show error messages
static const uint8_t ANY = 6; // Used for show always messages, for any current debug level
// Expand characters as CR/LF to \\r, \\n
String expand(String string);
// Destructor
~RemoteDebug();
private:
// Variables
String _hostName = ""; // Host name
boolean _connected = false; // Client is connected ?
String _password = ""; // Password
boolean _passwordOk = false; // Password request ? - 18/07/18
uint8_t _passwordAttempt = 0;
boolean _silence = false; // Silence mode ?
uint32_t _silenceTimeout = 0; // Silence timeout
uint8_t _clientDebugLevel = DEBUG; // Level setted by user in web app or telnet client
uint8_t _lastDebugLevel = DEBUG; // Last Level setted by active()
uint32_t _lastTimePrint = millis(); // Last time print a line
uint8_t _levelBeforeProfiler=DEBUG; // Last Level before Profiler level
uint32_t _levelProfilerDisable = 0; // time in millis to disable the profiler level
uint32_t _autoLevelProfiler = 0; // Automatic change to profiler level if time between handles is greater than n millis
boolean _showTime = false; // Show time in millis
boolean _showProfiler = false; // Show time between messages
uint32_t _minTimeShowProfiler = 0; // Minimal time to show profiler
boolean _showDebugLevel = true; // Show debug Level
boolean _showColors = false; // Show colors
boolean _showRaw = false; // Show in raw mode ?
boolean _serialEnabled = false; // Send to serial too (not recommended)
boolean _resetCommandEnabled=false; // Enable command to reset the board
boolean _newLine = true; // New line write ?
String _command = ""; // Command received
String _lastCommand = ""; // Last Command received
uint32_t _lastTimeCommand = millis();// Last time command received
String _helpProjectCmds = ""; // Help of comands setted by project (sketch)
void (*_callbackProjectCmds)() = NULL; // Callable for projects commands
void (*_callbackNewClient)() = NULL; // Callable for when have a new client connected
String _filter = ""; // Filter
boolean _filterActive = false;
String _bufferPrint = ""; // Buffer of print write to WiFi
#ifdef CLIENT_BUFFERING
String _bufferSend = ""; // Buffer to send data to web app or telnet client
uint16_t _sizeBufferSend = 0; // Size of it
uint32_t _lastTimeSend = 0; // Last time command send data
#endif
#ifdef DEBUGGER_ENABLED
// // For Simple software debugger - based on SerialDebug Library
boolean (*_callbackDbgEnabled)() = NULL;// Callable for debugger enabled
void (*_callbackDbgHandle)(const boolean) = NULL; // Callable for handle of debugger
String (*_callbackDbgHelp)() = NULL; // Callable for get debugger help
void (*_callbackDbgProcessCmd)() = NULL;// Callable for process commands of debugger
#endif
//////// Privates
void showHelp();
void processCommand();
String formatNumber(uint32_t value, uint8_t size, char insert='0');
boolean isCRLF(char character);
uint32_t getFreeMemory();
#ifdef ALPHA_VERSION // In test, not good yet
void sendTelnetCommand(uint8_t command, uint8_t option);
#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 rprintA(...)
#define rprintV(...)
#define rprintD(...)
#define rprintI(...)
#define rprintW(...)
#define rprintE(...)
#define rprintAln(...)
#define rprintVln(...)
#define rprintDln(...)
#define rprintIln(...)
#define rprintWln(...)
#define rprintEln(...)
#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