-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdebugUtil.h
248 lines (210 loc) · 8.85 KB
/
debugUtil.h
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
/**************************************************************************/
/*!
@file debugUtil.h
Arduino K197Display sketch
Copyright (C) 2022 by ALX2009
License: MIT (see LICENSE)
This file is part of the Arduino K197Display sketch, please see
https://github.com/alx2009/K197Display for more information
This file defines the debugUtil class/functions
This class encapsulate a number of utility functions to support debugging
*/
/**************************************************************************/
#ifndef DEBUGUTIL_H__
#define DEBUGUTIL_H__
#include <Arduino.h>
/**************************************************************************/
/*!
@brief Debug utility class
This class encapsulate a number of utility functions useful for debugging
The function implements the Print interface like Serial, etc.
The output will go to Serial and/or the Oled (the latter via u8g2log defined
in UIManager.h), depending on the properties set with begin and/or
useSerial() and/or useOled()
Normally this class is used via the predefined object DebugOut (similarly to
how Serial is used)
PREREQUISITES: Serial.begin() and u8g2log.begin() must be called before
enabling the Serial and the Oled output respectively The latter is called
when an object of class UImanager is initialized via UImanager::begin()
*/
/**************************************************************************/
//#define PROFILE_TIMER 1 ///< when defined, add the possibility to profile
// sections of code
//#define RUNTIME_ASSERTS 1 ///< when defined, add additional runtime checks
/**************************************************************************/
/*!
@brief class implementing an object used as debug output
@details This class implements the usual Print interface and can send the
output to two parallel streams:
- Serial
- a u8g2log object of type U8G2LOG, it can be used to show the log in the
oled screen via the u8g2 library
PREREQUISITES: Serial.begin() and u8g2log.begin() must be called before
enabling the Serial and the Oled output respectively.
*/
/**************************************************************************/
class debugUtil : public Print {
bool use_serial = false; ///< enable Serial output if true
bool use_oled = false; ///< enable Oled output if true
#ifdef PROFILE_TIMER
unsigned long proftimer[PROFILE_TIMER]; ///< store the profile timers(s)
public:
static const byte PROFILE_MATH = 0; ///< profiler time slot for math stuff
static const byte PROFILE_LOOP = 1; ///< profiler time slot for loop()
static const byte PROFILE_DEVICE = 2; ///< profiler time slot for K197dev
static const byte PROFILE_DISPLAY = 3; ///< profiler time slot for uiman
#endif // PROFILE_TIMER
public:
/*!
@brief default constructor for the class.
Note that normally there is no need to declare an object explciitly, just
use the predefined DebugOut
*/
debugUtil(){};
/*!
@brief initialize the object.
It should be called before using the object, albeit in the curent
implementation it is safe to use it even before, since by default debug
output is disabled.
PREREQUISITES: Serial.begin and UImanager.begin must be called if they are
enabled
@param serial enable Serial output if true, disable if false
@param oled enable oled output (via u8g2log) if true, disable if false
*/
void begin(bool serial = false, bool oled = false) {
use_serial = serial;
use_oled = oled;
};
/*!
@brief query if Serial output is enabled
@return true if Serial output is enabled, false otherwise
*/
bool useSerial() { return use_serial; };
/*!
@brief enabled/disable Serial output.
PREREQUISITES: Serial.begin() must be called if Serial output is enabled
@param serial enable Serial output if true, disable if false
*/
void useSerial(bool serial) { use_serial = serial; };
/*!
@brief query if Oled output is enabled
@return true if Oled output is enabled, false otherwise
*/
bool useOled() { return use_oled; };
/*!
@brief enabled/disable Oled output.
PREREQUISITES: u8g2log.begin() must be called if Oled output is enabled.
Note that this is automatically done when any object of class UImanager is
initialized with UImanager::begin()
@param oled enable oled output if true, disable if false
*/
void useOled(bool oled) { use_oled = oled; };
virtual size_t write(uint8_t);
virtual size_t write(const char *str);
virtual size_t write(const uint8_t *buffer, size_t size);
virtual int availableForWrite();
virtual void flush();
#ifdef PROFILE_TIMER
/*!
@brief start a profile timer
@details a profile timer is used to measure the execution time of a given
section of code bracketed between profileStart() and profileEnd();
@param slot the slot number of this particular timer
*/
void profileStart(unsigned int slot) {
if (slot < PROFILE_TIMER)
proftimer[slot] = micros();
}
/*!
@brief Stop a profile timer
@param slot the slot number of this particular timer
*/
void profileStop(unsigned int slot) {
if (slot < PROFILE_TIMER)
proftimer[slot] = micros() - proftimer[slot];
}
/*!
@brief print a profile timer
@param slot the slot number of this particular timer
*/
void profilePrint(unsigned int slot) {
if (slot < PROFILE_TIMER)
print(proftimer[slot]);
}
/*!
@brief print a profile timer
@param slot the slot number of this particular timer
@param slotName the name of the slot
*/
void profilePrintln(unsigned int slot, const __FlashStringHelper *slotName) {
if (slot < PROFILE_TIMER) {
print(slotName);
print('=');
print(proftimer[slot]);
println(F("us"));
}
}
#endif // PROFILE_TIMER
};
extern debugUtil
DebugOut; ///< this is the predefined object that is used with print(),
///< etc. (similar to how Serial is used for debug output)
#ifdef PROFILE_TIMER
#define PROFILE_start(...) \
DebugOut.profileStart(__VA_ARGS__) ///< macro to start profiling
#define PROFILE_stop(...) \
DebugOut.profileStop(__VA_ARGS__) ///< macro to stop profiling
#define PROFILE_print(...) \
DebugOut.profilePrint(__VA_ARGS__) ///< macro to print the counter
#define PROFILE_println(...) \
DebugOut.profilePrintln(__VA_ARGS__) ///< macro to print the counter
#else
#define PROFILE_start(...) ///< Does nothing when not profiling
#define PROFILE_stop(...) ///< Does nothing when not profiling
#define PROFILE_print(...) ///< Does nothing when not profiling
#define PROFILE_println(...) ///< Does nothing when not profiling
#endif // PROFILE_TIMER
#ifdef RUNTIME_ASSERTS
/*!
\def RT_ASSERT(condition, message_string)
@brief macro, used for run time assertions
@param condition logical condition to check
@param message_string message to print if condition is false
*/
#define RT_ASSERT(condition, message_string) \
if (!(condition)) { \
DebugOut.println(F(message_string)); \
}
/*!
\def RT_ASSERT_ACT(condition, action)
@brief macro, used for run time diagnostics
@param condition logical condition to check
@param action code to be executed if condition is false
*/
#define RT_ASSERT_ACT(condition, action) \
if (!(condition)) { \
action; \
}
/*!
\def RT_ASSERT_ADD_PARAM(param)
@brief macro, used to add a parameter to a function conditionally
@details the parameter will be included only if RUNTIME_ASSERTS is defined
@param param conditional definition of the parameter
*/
#define RT_ASSERT_ADD_PARAM(param) , param
/*!
\def RT_ASSERT_ADD_STATEMENTS(statement)
@brief macro, used to add code ASSERTS are used
@details the code will be included only if RUNTIME_ASSERTS is defined
@param statement code to include conditionally
*/
#define RT_ASSERT_ADD_STATEMENTS(statement) statement
#else
#define RT_ASSERT(...) ///< Does nothing with real time assets disabled
#define RT_ASSERT_ACT(...) ///< Does nothing with real time assets disabled
#define RT_ASSERT_ADD_PARAM( \
...) ///< Does nothing with real time assets disabled
#define RT_ASSERT_ADD_STATEMENTS( \
...) ///< Does nothing with real time assets disabled
#endif // RUNTIME_ASSERTS
#endif // DEBUGUTIL_H__