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
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": []
}
]
}
12 changes: 1 addition & 11 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,19 +134,9 @@ Please keep these in mind when contributing:
- **I/O**: Use `StreamUtils::WriteBufferingStream` for buffered writes.
- **Validation**: Run schema hooks on create/update; on failure revert and return a validation error.
- **Naming**: lowerCamelCase for methods/vars, UpperCamelCase for types, ALL_CAPS for simple constants/enums.
- **Formatting**: Use a consistent `clang-format` (LLVM/Google); keep lines ≤ 120 cols.
- **Formatting**: Follow the repository `.clang-format` + `.editorconfig` baseline from `esptoolkit-template` (LLVM-derived style, `ColumnLimit: 100`, tabs with width `4`, `BinPackArguments/Parameters: false`, `AllowShortFunctionsOnASingleLine: None`).
- **Allocations**: Avoid hidden allocations in hot paths and inside event callbacks & sync loops.

Optional `.clang-format` starter (Google-like):
```yaml
BasedOnStyle: Google
IndentWidth: 4
ColumnLimit: 120
DerivePointerAlignment: false
PointerAlignment: Left
AllowShortFunctionsOnASingleLine: Empty
```

---

## Commit messages & branches
Expand Down
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,13 @@ if (ESPStoreCodec::decodeLocalDateTime(doc["localTime"], local)) {
- `examples/ClearAndReseed` – clear the store and seed with defaults.
- `examples/LocalDateTime` – store LocalDateTime as `{ epochSeconds, offsetMinutes }`.

## 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
55 changes: 29 additions & 26 deletions examples/ClearAndReseed/ClearAndReseed.ino
Original file line number Diff line number Diff line change
Expand Up @@ -5,40 +5,43 @@ ESPJsonDB db;
ESPStore store;

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

if (!db.init("/db").ok()) {
Serial.println("DB init failed");
return;
}
if (!db.init("/db").ok()) {
Serial.println("DB init failed");
return;
}

store.init(&db, "systemConf");
store.init(&db, "systemConf");

JsonDocument cfg;
cfg["mode"] = "normal";
cfg["retries"] = 3;
JsonDocument cfg;
cfg["mode"] = "normal";
cfg["retries"] = 3;

auto st = store.set(cfg.as<JsonVariantConst>());
Serial.printf("Seed set: %s\n", st.ok() ? "OK" : st.message);
auto st = store.set(cfg.as<JsonVariantConst>());
Serial.printf("Seed set: %s\n", st.ok() ? "OK" : st.message);

st = store.syncNow();
Serial.printf("Sync: %s\n", st.ok() ? "OK" : st.message);
st = store.syncNow();
Serial.printf("Sync: %s\n", st.ok() ? "OK" : st.message);

st = store.clear();
Serial.printf("Clear: %s\n", st.ok() ? "OK" : st.message);
st = store.clear();
Serial.printf("Clear: %s\n", st.ok() ? "OK" : st.message);

JsonDocument fallback;
fallback["mode"] = "safe";
fallback["retries"] = 1;
JsonDocument fallback;
fallback["mode"] = "safe";
fallback["retries"] = 1;

bool usedDefault = false;
auto res = store.getOr(fallback.as<JsonVariantConst>(), &usedDefault);
Serial.printf("After clear getOr: %s (%s)\n",
res.ok() ? "OK" : res.message(),
usedDefault ? "default" : "stored");
bool usedDefault = false;
auto res = store.getOr(fallback.as<JsonVariantConst>(), &usedDefault);
Serial.printf(
"After clear getOr: %s (%s)\n",
res.ok() ? "OK" : res.message(),
usedDefault ? "default" : "stored"
);

serializeJsonPretty(res.data, Serial);
store.deinit();
serializeJsonPretty(res.data, Serial);
store.deinit();
}

void loop() {}
void loop() {
}
119 changes: 61 additions & 58 deletions examples/CodecAll/CodecAll.ino
Original file line number Diff line number Diff line change
@@ -1,69 +1,72 @@
#include <ESPDate.h>
#include <ESPJsonDB.h>
#include <ESPStore.h>
#include <ESPStoreCodec.h>
#include <ESPDate.h>

ESPJsonDB db;
ESPStore store;
ESPDate date;

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

if (!db.init("/db").ok()) {
Serial.println("DB init failed");
return;
}

store.init(&db, "codecDemo");

JsonDocument doc;

IPAddress ip(192, 168, 1, 42);
ESPStoreCodec::encodeIpString(doc["ipString"], ip);
ESPStoreCodec::encodeIpArray(doc["ipArray"], ip);

DateTime dt{};
dt.epochSeconds = 1730000000; // fixed sample epoch
ESPStoreCodec::encodeDateTimeEpoch(doc["timeEpoch"], dt);
ESPStoreCodec::encodeDateTimeIso(doc["timeIso"], dt, date);

LocalDateTime local = date.nowLocal();
ESPStoreCodec::encodeLocalDateTime(doc["localTime"], local);

auto st = store.set(doc.as<JsonVariantConst>());
Serial.printf("Store set: %s\n", st.ok() ? "OK" : st.message);

auto res = store.get();
if (!res.ok()) {
Serial.printf("Store get failed: %s\n", res.message());
return;
}

IPAddress ipStr;
IPAddress ipArr;
ESPStoreCodec::decodeIpString(res.data["ipString"], ipStr);
ESPStoreCodec::decodeIpArray(res.data["ipArray"], ipArr);

DateTime dtEpoch{};
DateTime dtIso{};
ESPStoreCodec::decodeDateTimeEpoch(res.data["timeEpoch"], dtEpoch);
ESPStoreCodec::decodeDateTimeIso(res.data["timeIso"], dtIso, date);

LocalDateTime localDecoded{};
bool localOk = ESPStoreCodec::decodeLocalDateTime(res.data["localTime"], localDecoded);

Serial.printf("IP string: %s\n", ipStr.toString().c_str());
Serial.printf("IP array: %s\n", ipArr.toString().c_str());
Serial.printf("Epoch: %lld\n", static_cast<long long>(dtEpoch.epochSeconds));
Serial.printf("ISO epoch: %lld\n", static_cast<long long>(dtIso.epochSeconds));
if (localOk) {
Serial.printf("Local epoch: %lld offset: %d\n",
static_cast<long long>(localDecoded.utc.epochSeconds),
localDecoded.offsetMinutes);
}

store.deinit();
Serial.begin(115200);

if (!db.init("/db").ok()) {
Serial.println("DB init failed");
return;
}

store.init(&db, "codecDemo");

JsonDocument doc;

IPAddress ip(192, 168, 1, 42);
ESPStoreCodec::encodeIpString(doc["ipString"], ip);
ESPStoreCodec::encodeIpArray(doc["ipArray"], ip);

DateTime dt{};
dt.epochSeconds = 1730000000; // fixed sample epoch
ESPStoreCodec::encodeDateTimeEpoch(doc["timeEpoch"], dt);
ESPStoreCodec::encodeDateTimeIso(doc["timeIso"], dt, date);

LocalDateTime local = date.nowLocal();
ESPStoreCodec::encodeLocalDateTime(doc["localTime"], local);

auto st = store.set(doc.as<JsonVariantConst>());
Serial.printf("Store set: %s\n", st.ok() ? "OK" : st.message);

auto res = store.get();
if (!res.ok()) {
Serial.printf("Store get failed: %s\n", res.message());
return;
}

IPAddress ipStr;
IPAddress ipArr;
ESPStoreCodec::decodeIpString(res.data["ipString"], ipStr);
ESPStoreCodec::decodeIpArray(res.data["ipArray"], ipArr);

DateTime dtEpoch{};
DateTime dtIso{};
ESPStoreCodec::decodeDateTimeEpoch(res.data["timeEpoch"], dtEpoch);
ESPStoreCodec::decodeDateTimeIso(res.data["timeIso"], dtIso, date);

LocalDateTime localDecoded{};
bool localOk = ESPStoreCodec::decodeLocalDateTime(res.data["localTime"], localDecoded);

Serial.printf("IP string: %s\n", ipStr.toString().c_str());
Serial.printf("IP array: %s\n", ipArr.toString().c_str());
Serial.printf("Epoch: %lld\n", static_cast<long long>(dtEpoch.epochSeconds));
Serial.printf("ISO epoch: %lld\n", static_cast<long long>(dtIso.epochSeconds));
if (localOk) {
Serial.printf(
"Local epoch: %lld offset: %d\n",
static_cast<long long>(localDecoded.utc.epochSeconds),
localDecoded.offsetMinutes
);
}

store.deinit();
}

void loop() {}
void loop() {
}
45 changes: 23 additions & 22 deletions examples/Defaults/Defaults.ino
Original file line number Diff line number Diff line change
Expand Up @@ -5,33 +5,34 @@ ESPJsonDB db;
ESPStore netConf;

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

JsonDocument defaults;
defaults["ssid"] = "";
defaults["password"] = "";
defaults["hostname"] = "ESP_DEVICE";
defaults["autoReconnect"] = true;
netConf.setDefault(defaults.as<JsonVariantConst>());
JsonDocument defaults;
defaults["ssid"] = "";
defaults["password"] = "";
defaults["hostname"] = "ESP_DEVICE";
defaults["autoReconnect"] = true;
netConf.setDefault(defaults.as<JsonVariantConst>());

if (!db.init("/db").ok()) {
Serial.println("DB init failed");
return;
}
if (!db.init("/db").ok()) {
Serial.println("DB init failed");
return;
}

netConf.init(&db, "netConf");
netConf.init(&db, "netConf");

bool usedDefault = false;
auto res = netConf.getOr(&usedDefault);
bool usedDefault = false;
auto res = netConf.getOr(&usedDefault);

if (!res.ok()) {
Serial.printf("Failed to read config: %s\n", res.message());
return;
}
if (!res.ok()) {
Serial.printf("Failed to read config: %s\n", res.message());
return;
}

Serial.printf("Config source: %s\n", usedDefault ? "default" : "stored");
serializeJsonPretty(res.data, Serial);
netConf.deinit();
Serial.printf("Config source: %s\n", usedDefault ? "default" : "stored");
serializeJsonPretty(res.data, Serial);
netConf.deinit();
}

void loop() {}
void loop() {
}
Loading