Skip to content

Commit b34629e

Browse files
committed
MCU8MASS-2198 Add capability for Log module to take in flash strings and update serial example to demonstrate this
1 parent 5207dd8 commit b34629e

File tree

4 files changed

+470
-116
lines changed

4 files changed

+470
-116
lines changed

examples/serial/serial.ino

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -80,16 +80,20 @@ void setup() {
8080
Log.setLogLevel(LogLevel::INFO);
8181

8282
// The logging library can also format strings by using the f postfix in the
83-
// function name, so we don't have to use multiple print calls as with the
84-
// standard Arduino Serial. This makes printing more complex things far more
85-
// structured and work in the same way as the printf in the standard library
86-
// of C. Note we have to add a \r\n for a carriage return and new line when
87-
// the format based functions are used.
83+
// function name. Note we have to add a \r\n for a carriage return and new
84+
// line when the format based functions are used.
8885
Log.infof("This is a number: %d\r\n", 10);
8986
Log.infof("This is a string: %s\r\n", "Hello world");
9087
Log.infof("This is a hexadecimal and a string: %X - %s\r\n",
9188
31,
9289
"Hello world");
90+
91+
// The logging library also supports flash strings stored in program memory.
92+
// This is very useful for reducing memory usage
93+
Log.info(F("This is a flash string"));
94+
95+
// The flash string functionality also support formatting
96+
Log.infof(F("This is a flash string with formatting: %d\r\n"), 10);
9397
}
9498

9599
void loop() {}

src/log.cpp

Lines changed: 184 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,102 @@
66
#pragma GCC diagnostic push
77
#pragma GCC diagnostic ignored "-Wvarargs"
88

9-
#define ERR_LEVEL_FMT "[ERROR] "
10-
#define INFO_LEVEL_FMT "[INFO] "
11-
#define DEBUG_LEVEL_FMT "[DEBUG] "
12-
#define WARN_LEVEL_FMT "[WARN] "
9+
static const char ERROR_LOG_LEVEL[] PROGMEM = "[ERROR] ";
10+
static const char WARN_LOG_LEVEL[] PROGMEM = "[WARN] ";
11+
static const char INFO_LOG_LEVEL[] PROGMEM = "[INFO] ";
12+
static const char DEBUG_LOG_LEVEL[] PROGMEM = "[DEBUG] ";
13+
static const char NONE_LOG_LEVEL[] PROGMEM = "";
1314

14-
LogClass Log(&Serial3);
15+
/**
16+
* @return A pointer to the given @p level flash string.
17+
*/
18+
static const __FlashStringHelper* getLogLevelString(const LogLevel level) {
19+
20+
switch (level) {
21+
case LogLevel::ERROR:
22+
return reinterpret_cast<const __FlashStringHelper*>(ERROR_LOG_LEVEL);
23+
24+
case LogLevel::WARN:
25+
return reinterpret_cast<const __FlashStringHelper*>(WARN_LOG_LEVEL);
26+
27+
case LogLevel::INFO:
28+
return reinterpret_cast<const __FlashStringHelper*>(INFO_LOG_LEVEL);
29+
30+
case LogLevel::DEBUG:
31+
return reinterpret_cast<const __FlashStringHelper*>(DEBUG_LOG_LEVEL);
1532

16-
int16_t printf_putchar(char c, FILE* fp) {
17-
((class Print*)(fdev_get_udata(fp)))->write((uint8_t)c);
33+
default:
34+
return reinterpret_cast<const __FlashStringHelper*>(NONE_LOG_LEVEL);
35+
}
36+
}
37+
38+
/**
39+
* @brief We have to declare this outside the class scope for interoperability
40+
* with fdev_setup_stream. This is safe as we won't ever come into a situation
41+
* where this is not called atomically.
42+
*/
43+
static int16_t fdev_putchar(char data, FILE* file) {
44+
((class Print*)(fdev_get_udata(file)))->write((uint8_t)data);
1845
return 0;
1946
}
2047

21-
void LogClass::print(const char* str, const char level[]) {
22-
this->uart->printf("%s%s\r\n", level, str);
48+
/**
49+
* @brief The instance of the LogClass defined in the header file. Declared here
50+
* so that we have an Arduino style with not having to instantiate the class
51+
* explicitly.
52+
*/
53+
LogClass Log(&Serial3);
54+
55+
void LogClass::print(const LogLevel level, const char* str) {
56+
if (log_level < level) {
57+
return;
58+
}
59+
60+
uart->print(getLogLevelString(level));
61+
uart->println(str);
62+
}
63+
64+
void LogClass::print(const LogLevel level, const __FlashStringHelper* str) {
65+
if (log_level < level) {
66+
return;
67+
}
68+
69+
uart->print(getLogLevelString(level));
70+
uart->println(str);
71+
}
72+
73+
void LogClass::printf(const LogLevel level, const char* str, va_list args) {
74+
75+
if (log_level < level) {
76+
return;
77+
}
78+
79+
uart->print(getLogLevelString(level));
80+
81+
FILE file;
82+
83+
fdev_setup_stream(&file, fdev_putchar, NULL, _FDEV_SETUP_WRITE);
84+
fdev_set_udata(&file, this->uart);
85+
vfprintf(&file, str, args);
86+
fdev_close();
87+
}
88+
89+
void LogClass::printf(const LogLevel level,
90+
const __FlashStringHelper* str,
91+
va_list args) {
92+
93+
if (log_level < level) {
94+
return;
95+
}
96+
97+
uart->print(getLogLevelString(level));
98+
99+
FILE file;
100+
101+
fdev_setup_stream(&file, fdev_putchar, NULL, _FDEV_SETUP_WRITE);
102+
fdev_set_udata(&file, this->uart);
103+
vfprintf_P(&file, reinterpret_cast<const char*>(str), args);
104+
fdev_close();
23105
}
24106

25107
LogClass::LogClass(UartClass* uart) {
@@ -37,147 +119,145 @@ LogLevel LogClass::getLogLevel(void) { return log_level; }
37119

38120
bool LogClass::setLogLevelStr(const char* log_level) {
39121
LogLevel ll = LogLevel::NONE;
40-
if (strstr(log_level, "debug") != NULL) {
122+
if (strstr_P(log_level, PSTR("debug")) != NULL) {
41123
ll = LogLevel::DEBUG;
42-
} else if (strstr(log_level, "info") != NULL) {
124+
} else if (strstr_P(log_level, PSTR("info")) != NULL) {
43125
ll = LogLevel::INFO;
44-
} else if (strstr(log_level, "warn") != NULL) {
126+
} else if (strstr_P(log_level, PSTR("warn")) != NULL) {
45127
ll = LogLevel::WARN;
46-
} else if (strstr(log_level, "error") != NULL) {
128+
} else if (strstr_P(log_level, PSTR("error")) != NULL) {
47129
ll = LogLevel::ERROR;
48-
}
49-
50-
if (ll == LogLevel::NONE) {
130+
} else {
51131
return false;
52132
}
53133

54134
this->setLogLevel(ll);
135+
55136
return true;
56137
}
57138

58139
void LogClass::begin(const uint32_t baud_rate) { this->uart->begin(baud_rate); }
59140

60141
void LogClass::end(void) { this->uart->end(); }
61142

62-
void LogClass::info(const char str[]) {
63-
if (log_level >= LogLevel::INFO) {
64-
this->print(str, INFO_LEVEL_FMT);
65-
}
66-
}
67-
68-
void LogClass::info(const String str) { this->info(str.c_str()); }
69-
70-
void LogClass::infof(const char* format, ...) {
71-
if (log_level >= LogLevel::INFO) {
72-
73-
// Append format with [ERROR]
74-
char nFormat[strlen(format) + sizeof(INFO_LEVEL_FMT)] = INFO_LEVEL_FMT;
75-
strcpy(nFormat + sizeof(INFO_LEVEL_FMT) - 1, format);
143+
void LogClass::error(const char str[]) { this->print(LogLevel::ERROR, str); }
76144

77-
FILE f;
78-
va_list ap;
79-
80-
fdev_setup_stream(&f, printf_putchar, NULL, _FDEV_SETUP_WRITE);
81-
fdev_set_udata(&f, this->uart);
145+
void LogClass::error(const String str) {
146+
this->print(LogLevel::ERROR, str.c_str());
147+
}
82148

83-
va_start(ap, nFormat);
84-
vfprintf(&f, nFormat, ap);
85-
va_end(ap);
86-
}
149+
void LogClass::error(const __FlashStringHelper* str) {
150+
this->print(LogLevel::ERROR, str);
87151
}
88152

89-
void LogClass::debug(const char str[]) {
90-
if (log_level >= LogLevel::DEBUG) {
91-
this->print(str, DEBUG_LEVEL_FMT);
92-
}
153+
void LogClass::errorf(const char* format, ...) {
154+
va_list args;
155+
va_start(args, format);
156+
this->printf(LogLevel::ERROR, format, args);
157+
va_end(args);
93158
}
94159

95-
void LogClass::debug(const String str) { this->debug(str.c_str()); }
160+
void LogClass::errorf(const __FlashStringHelper* format, ...) {
161+
va_list args;
162+
va_start(args, format);
163+
this->printf(LogLevel::ERROR, format, args);
164+
va_end(args);
165+
}
96166

97-
void LogClass::debugf(const char* format, ...) {
98-
if (log_level >= LogLevel::DEBUG) {
167+
void LogClass::warn(const char str[]) { this->print(LogLevel::WARN, str); }
99168

100-
// Append format with [ERROR]
101-
char nFormat[strlen(format) + sizeof(DEBUG_LEVEL_FMT)] =
102-
DEBUG_LEVEL_FMT;
103-
strcpy(nFormat + sizeof(DEBUG_LEVEL_FMT) - 1, format);
169+
void LogClass::warn(const String str) {
170+
this->print(LogLevel::WARN, str.c_str());
171+
}
104172

105-
FILE f;
106-
va_list ap;
173+
void LogClass::warn(const __FlashStringHelper* str) {
174+
this->print(LogLevel::WARN, str);
175+
}
107176

108-
fdev_setup_stream(&f, printf_putchar, NULL, _FDEV_SETUP_WRITE);
109-
fdev_set_udata(&f, this->uart);
110-
va_start(ap, nFormat);
111-
vfprintf(&f, nFormat, ap);
112-
va_end(ap);
113-
}
177+
void LogClass::warnf(const char* format, ...) {
178+
va_list args;
179+
va_start(args, format);
180+
this->printf(LogLevel::WARN, format, args);
181+
va_end(args);
114182
}
115183

116-
void LogClass::raw(const char str[]) { this->print(str, ""); }
184+
void LogClass::warnf(const __FlashStringHelper* format, ...) {
185+
va_list args;
186+
va_start(args, format);
187+
this->printf(LogLevel::WARN, format, args);
188+
va_end(args);
189+
}
117190

118-
void LogClass::raw(const String str) { this->raw(str.c_str()); }
191+
void LogClass::info(const char str[]) { this->print(LogLevel::INFO, str); }
119192

120-
void LogClass::rawf(const char* format, ...) {
121-
FILE f;
122-
va_list ap;
193+
void LogClass::info(const String str) {
194+
this->print(LogLevel::INFO, str.c_str());
195+
}
123196

124-
fdev_setup_stream(&f, printf_putchar, NULL, _FDEV_SETUP_WRITE);
125-
fdev_set_udata(&f, this->uart);
126-
va_start(ap, format);
127-
vfprintf(&f, format, ap);
128-
va_end(ap);
197+
void LogClass::info(const __FlashStringHelper* str) {
198+
this->print(LogLevel::INFO, str);
129199
}
130200

131-
void LogClass::error(const char str[]) {
132-
if (log_level >= LogLevel::ERROR) {
133-
this->print(str, ERR_LEVEL_FMT);
134-
}
201+
void LogClass::infof(const char* format, ...) {
202+
va_list args;
203+
va_start(args, format);
204+
this->printf(LogLevel::INFO, format, args);
205+
va_end(args);
135206
}
136207

137-
void LogClass::error(const String str) { this->error(str.c_str()); }
208+
void LogClass::infof(const __FlashStringHelper* format, ...) {
209+
va_list args;
210+
va_start(args, format);
211+
this->printf(LogLevel::INFO, format, args);
212+
va_end(args);
213+
}
138214

139-
void LogClass::errorf(const char* format, ...) {
140-
if (log_level >= LogLevel::ERROR) {
215+
void LogClass::debug(const char str[]) { this->print(LogLevel::DEBUG, str); }
141216

142-
// Append format with [ERROR]
143-
char nFormat[strlen(format) + sizeof(ERR_LEVEL_FMT)] = ERR_LEVEL_FMT;
144-
strcpy(nFormat + sizeof(ERR_LEVEL_FMT) - 1, format);
217+
void LogClass::debug(const String str) {
218+
this->print(LogLevel::DEBUG, str.c_str());
219+
}
145220

146-
FILE f;
147-
va_list ap;
221+
void LogClass::debug(const __FlashStringHelper* str) {
222+
this->print(LogLevel::DEBUG, str);
223+
}
148224

149-
fdev_setup_stream(&f, printf_putchar, NULL, _FDEV_SETUP_WRITE);
150-
fdev_set_udata(&f, this->uart);
151-
va_start(ap, nFormat);
152-
vfprintf(&f, nFormat, ap);
153-
va_end(ap);
154-
}
225+
void LogClass::debugf(const char* format, ...) {
226+
va_list args;
227+
va_start(args, format);
228+
this->printf(LogLevel::DEBUG, format, args);
229+
va_end(args);
155230
}
156231

157-
void LogClass::warn(const char str[]) {
158-
if (log_level >= LogLevel::WARN) {
159-
this->print(str, WARN_LEVEL_FMT);
160-
}
232+
void LogClass::debugf(const __FlashStringHelper* format, ...) {
233+
va_list args;
234+
va_start(args, format);
235+
this->printf(LogLevel::DEBUG, format, args);
236+
va_end(args);
161237
}
162238

163-
void LogClass::warn(const String str) { this->warn(str.c_str()); }
239+
void LogClass::raw(const char str[]) { this->print(LogLevel::NONE, str); }
164240

165-
void LogClass::warnf(const char* format, ...) {
166-
if (log_level >= LogLevel::WARN) {
241+
void LogClass::raw(const String str) {
242+
this->print(LogLevel::NONE, str.c_str());
243+
}
167244

168-
// Append format with [ERROR]
169-
char nFormat[strlen(format) + sizeof(WARN_LEVEL_FMT)] = WARN_LEVEL_FMT;
170-
strcpy(nFormat + sizeof(WARN_LEVEL_FMT) - 1, format);
245+
void LogClass::raw(const __FlashStringHelper* str) {
246+
this->print(LogLevel::NONE, str);
247+
}
171248

172-
FILE f;
173-
va_list ap;
249+
void LogClass::rawf(const char* format, ...) {
250+
va_list args;
251+
va_start(args, format);
252+
this->printf(LogLevel::NONE, format, args);
253+
va_end(args);
254+
}
174255

175-
fdev_setup_stream(&f, printf_putchar, NULL, _FDEV_SETUP_WRITE);
176-
fdev_set_udata(&f, this->uart);
177-
va_start(ap, nFormat);
178-
vfprintf(&f, nFormat, ap);
179-
va_end(ap);
180-
}
256+
void LogClass::rawf(const __FlashStringHelper* format, ...) {
257+
va_list args;
258+
va_start(args, format);
259+
this->printf(LogLevel::NONE, format, args);
260+
va_end(args);
181261
}
182262

183263
#pragma GCC diagnostic pop

0 commit comments

Comments
 (0)