Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .clang-format
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
BasedOnStyle: LLVM
ColumnLimit: 100
BinPackArguments: false
BinPackParameters: false
AllowAllArgumentsOnNextLine: false
AlignAfterOpenBracket: BlockIndent
UseTab: ForIndentation
IndentWidth: 4
TabWidth: 4
ContinuationIndentWidth: 4
AllowShortFunctionsOnASingleLine: None
11 changes: 11 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
root = true

[*]
end_of_line = lf
insert_final_newline = true
charset = utf-8

[*.{c,cc,cpp,h,hpp,ino}]
indent_style = tab
indent_size = tab
tab_width = 4
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
.venv
build/
build_prev_runner/
.vscode
19 changes: 19 additions & 0 deletions .vscode/bin/clang-format
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#!/usr/bin/env bash

set -euo pipefail

if command -v clang-format >/dev/null 2>&1; then
exec clang-format "$@"
fi

_home_dir="${HOME:-}"
if [ -n "$_home_dir" ]; then
_candidate="$(ls -1d "$_home_dir"/.vscode/extensions/ms-vscode.cpptools-*-linux-x64/LLVM/bin/clang-format 2>/dev/null | tail -n 1 || true)"
if [ -n "$_candidate" ] && [ -x "$_candidate" ]; then
exec "$_candidate" "$@"
fi
fi

echo "clang-format executable not found." >&2
echo "Install clang-format system-wide or install/update ms-vscode.cpptools." >&2
exit 127
9 changes: 9 additions & 0 deletions .vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"recommendations": [
"pioarduino.pioarduino-ide",
"xaver.clang-format"
],
"unwantedRecommendations": [
"ms-vscode.cpptools-extension-pack"
]
}
30 changes: 30 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"files.associations": {
"*.ino": "cpp"
},
"editor.defaultFormatter": "xaver.clang-format",
"C_Cpp.formatting": "Disabled",
"clang-format.style": "file",
"clang-format.executable": "${workspaceRoot}/.vscode/bin/clang-format",
"[cpp]": {
"editor.defaultFormatter": "xaver.clang-format",
"editor.detectIndentation": false,
"editor.insertSpaces": false,
"editor.tabSize": 4,
"editor.formatOnSave": true
},
"[c]": {
"editor.defaultFormatter": "xaver.clang-format",
"editor.detectIndentation": false,
"editor.insertSpaces": false,
"editor.tabSize": 4,
"editor.formatOnSave": true
},
"[arduino]": {
"editor.defaultFormatter": "xaver.clang-format",
"editor.detectIndentation": false,
"editor.insertSpaces": false,
"editor.tabSize": 4,
"editor.formatOnSave": true
}
}
12 changes: 12 additions & 0 deletions .vscode/tasks.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "Format Firmware Sources",
"type": "shell",
"command": "bash ${workspaceFolder}/scripts/format_cpp.sh",
"group": "build",
"problemMatcher": []
}
]
}
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,13 @@ ctest --test-dir build

The suite exercises buffering, log level filtering, and sync behavior. Hardware smoke tests reside in `examples/`.

## Formatting Baseline

This repository follows the firmware formatting baseline from `esptoolkit-template`:
- `.clang-format` is the source of truth for C/C++/INO layout.
- `.editorconfig` enforces tabs (`tab_width = 4`), LF endings, and final newline.
- Format all tracked firmware sources with `bash scripts/format_cpp.sh`.

## License
MIT — see [LICENSE.md](LICENSE.md).

Expand Down
107 changes: 57 additions & 50 deletions examples/basic_usage/basic_usage.ino
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,26 @@
ESPLogger logger;

class HeartbeatReporter {
public:
explicit HeartbeatReporter(ESPLogger& logger) : _logger(logger) {}

void log(uint32_t counter) {
_logger.info("APP", "Heartbeat %lu", static_cast<unsigned long>(counter));

if (counter % 5 == 0) {
_logger.warn("APP", "Simulated warning at count %lu", static_cast<unsigned long>(counter));
}

if (counter % 9 == 0) {
_logger.error("APP", "Simulated error at count %lu", static_cast<unsigned long>(counter));
}
}

private:
ESPLogger& _logger;
public:
explicit HeartbeatReporter(ESPLogger &logger) : _logger(logger) {
}

void log(uint32_t counter) {
_logger.info("APP", "Heartbeat %lu", static_cast<unsigned long>(counter));

if (counter % 5 == 0) {
_logger
.warn("APP", "Simulated warning at count %lu", static_cast<unsigned long>(counter));
}

if (counter % 9 == 0) {
_logger
.error("APP", "Simulated error at count %lu", static_cast<unsigned long>(counter));
}
}

private:
ESPLogger &_logger;
};

static HeartbeatReporter heartbeat(logger);
Expand All @@ -30,47 +33,51 @@ static HeartbeatReporter heartbeat(logger);
static std::vector<Log> lastSyncedLogs;
static bool loggerStopped = false;

void logSyncCallback(const std::vector<Log>& logs) {
lastSyncedLogs = logs;
Serial.printf("Synced %u log entries\n", static_cast<unsigned>(logs.size()));
void logSyncCallback(const std::vector<Log> &logs) {
lastSyncedLogs = logs;
Serial.printf("Synced %u log entries\n", static_cast<unsigned>(logs.size()));
}

void setup() {
Serial.begin(115200);
Serial.begin(115200);

// Quick start: omit the config entirely and call logger.init() to use all defaults.
LoggerConfig config;
config.syncIntervalMS = 3000; // sync every 3 seconds
config.maxLogInRam = 25; // keep a small buffer in RAM
config.consoleLogLevel = LogLevel::Info;
// Quick start: omit the config entirely and call logger.init() to use all defaults.
LoggerConfig config;
config.syncIntervalMS = 3000; // sync every 3 seconds
config.maxLogInRam = 25; // keep a small buffer in RAM
config.consoleLogLevel = LogLevel::Info;

if (!logger.init(config)) {
Serial.println("Failed to initialise logger!");
return;
}
if (!logger.init(config)) {
Serial.println("Failed to initialise logger!");
return;
}

logger.onSync(logSyncCallback);
logger.onSync(logSyncCallback);

logger.info("INIT", "ESPLogger ready. Max buffer: %u", static_cast<unsigned>(config.maxLogInRam));
logger
.info("INIT", "ESPLogger ready. Max buffer: %u", static_cast<unsigned>(config.maxLogInRam));
}

void loop() {
static uint32_t counter = 0;
if (loggerStopped) {
delay(1000);
return;
}

logger.debug("LOOP", "This debug message only shows when consoleLogLevel <= Debug (%lu)",
static_cast<unsigned long>(counter));

heartbeat.log(counter);

counter++;
if (counter >= 30) {
logger.deinit();
loggerStopped = true;
Serial.println("Logger deinitialized after demo run");
}
delay(1000);
static uint32_t counter = 0;
if (loggerStopped) {
delay(1000);
return;
}

logger.debug(
"LOOP",
"This debug message only shows when consoleLogLevel <= Debug (%lu)",
static_cast<unsigned long>(counter)
);

heartbeat.log(counter);

counter++;
if (counter >= 30) {
logger.deinit();
loggerStopped = true;
Serial.println("Logger deinitialized after demo run");
}
delay(1000);
}
105 changes: 54 additions & 51 deletions examples/custom_sync/custom_sync.ino
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,17 @@
ESPLogger logger;

class SensorSampler {
public:
explicit SensorSampler(ESPLogger& logger) : _logger(logger) {}
public:
explicit SensorSampler(ESPLogger &logger) : _logger(logger) {
}

void logReading() {
const float reading = analogRead(A0) / 1023.0f;
_logger.debug("DATA", "Sensor reading: %0.2f", reading);
}
void logReading() {
const float reading = analogRead(A0) / 1023.0f;
_logger.debug("DATA", "Sensor reading: %0.2f", reading);
}

private:
ESPLogger& _logger;
private:
ESPLogger &_logger;
};

static SensorSampler sampler(logger);
Expand All @@ -24,57 +25,59 @@ constexpr uint32_t kManualSyncIntervalMS = 5000;
uint32_t lastSyncMs = 0;
uint32_t sampleCount = 0;
bool loggerStopped = false;
} // namespace

void persistLogs(const std::vector<Log>& logs) {
Serial.printf("Persisting %u buffered entries\n", static_cast<unsigned>(logs.size()));
for (const auto& entry : logs) {
Serial.printf(" [%u][%ld][%s] %s\n",
static_cast<unsigned>(entry.millis),
static_cast<long>(entry.timestamp),
entry.tag.c_str(),
entry.message.c_str());
}
} // namespace

void persistLogs(const std::vector<Log> &logs) {
Serial.printf("Persisting %u buffered entries\n", static_cast<unsigned>(logs.size()));
for (const auto &entry : logs) {
Serial.printf(
" [%u][%ld][%s] %s\n",
static_cast<unsigned>(entry.millis),
static_cast<long>(entry.timestamp),
entry.tag.c_str(),
entry.message.c_str()
);
}
}

void setup() {
Serial.begin(115200);
Serial.begin(115200);

// Defaults are fine for many cases; call logger.init() without a config to use them.
LoggerConfig config;
config.enableSyncTask = false; // we will drive sync manually
config.maxLogInRam = 50;
config.consoleLogLevel = LogLevel::Debug;
// Defaults are fine for many cases; call logger.init() without a config to use them.
LoggerConfig config;
config.enableSyncTask = false; // we will drive sync manually
config.maxLogInRam = 50;
config.consoleLogLevel = LogLevel::Debug;

if (!logger.init(config)) {
Serial.println("ESPLogger init failed");
return;
}
if (!logger.init(config)) {
Serial.println("ESPLogger init failed");
return;
}

logger.onSync(persistLogs);
logger.onSync(persistLogs);

logger.info("SYNC", "Manual sync example ready");
logger.info("SYNC", "Manual sync example ready");
}

void loop() {
if (loggerStopped) {
delay(1000);
return;
}

sampler.logReading();
sampleCount++;

if (millis() - lastSyncMs >= kManualSyncIntervalMS) {
lastSyncMs = millis();
logger.sync(); // trigger persistence callback immediately
}

if (sampleCount >= 40) {
logger.deinit();
loggerStopped = true;
Serial.println("Logger deinitialized after manual-sync demo");
}

delay(250);
if (loggerStopped) {
delay(1000);
return;
}

sampler.logReading();
sampleCount++;

if (millis() - lastSyncMs >= kManualSyncIntervalMS) {
lastSyncMs = millis();
logger.sync(); // trigger persistence callback immediately
}

if (sampleCount >= 40) {
logger.deinit();
loggerStopped = true;
Serial.println("Logger deinitialized after manual-sync demo");
}

delay(250);
}
Loading