diff --git a/.env b/.env new file mode 100644 index 0000000..7daa3ab --- /dev/null +++ b/.env @@ -0,0 +1,3 @@ +export GITHUB_REPOSITORY=mp-se/kegmon@master +export GITHUB_TOKEN="$(gh auth token)" +export SECRETS_GITHUB_TOKEN="$(gh auth token)" diff --git a/.github/workflows/doc-build.yaml b/.github/workflows/doc-build.yaml index afc21af..ccdc1c9 100644 --- a/.github/workflows/doc-build.yaml +++ b/.github/workflows/doc-build.yaml @@ -1,16 +1,17 @@ name: Sphinx Build on: + pull_request: push: branches: - master jobs: - build: + doc-build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - uses: ammaraskar/sphinx-action@master with: @@ -41,6 +42,7 @@ jobs: git commit -m "Update documentation" -a || true # The above command will fail if no changes were present, so we ignore # the return code. + - name: Push changes uses: ad-m/github-push-action@master with: diff --git a/.github/workflows/pio-build.yaml b/.github/workflows/pio-build.yaml index 8bd3675..223f875 100644 --- a/.github/workflows/pio-build.yaml +++ b/.github/workflows/pio-build.yaml @@ -1,51 +1,43 @@ name: PlatformIO CI on: + pull_request: push: branches: - dev jobs: - build: + pio-build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - - name: Cache pip - uses: actions/cache@v2 + - name: Setup PlatformIO + uses: n-vr/setup-platformio-action@v1.0.1 + + #- name: Run PlatformIO + # run: pio run -e kegmon-release -e kegmon32s2-release + + - name: PlatformIO Run + uses: karniv00l/platformio-run-action@v1 with: - path: ~/.cache/pip - key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} - restore-keys: | - ${{ runner.os }}-pip- - - - name: Cache PlatformIO - uses: actions/cache@v2 - with: - path: ~/.platformio - key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }} - - - name: Set up Python - uses: actions/setup-python@v2 - - - name: Install PlatformIO - run: | - python -m pip install --upgrade pip - pip install --upgrade platformio - git config --global advice.detachedHead false - - - name: Run PlatformIO - run: pio run -e kegmon-release -e kegmon32s2-release - - - uses: EndBug/add-and-commit@v7 # You can change this to use a specific version. https://github.com/marketplace/actions/add-commit + environments: "kegmon-release,kegmon32s2-release" + #targets: "kegmon-release,kegmon32s2-release" + #project-dir: "./some_dir" + #project-conf: "./some_dir/custom.ini" + jobs: 6 + silent: false + verbose: true + disable-auto-clean: false + + + - uses: EndBug/add-and-commit@v9 # You can change this to use a specific version. https://github.com/marketplace/actions/add-commit + if: ${{ github.event_name != 'pull_request' }} with: add: 'bin' author_name: GitHub Action author_email: mp-se@noreply.github.com - - branch: dev - default_author: github_actor message: 'GitHub Action Build' pathspec_error_handling: ignore diff --git a/.github/workflows/pre-commit.yaml b/.github/workflows/pre-commit.yaml index 9d89c67..11c19c5 100644 --- a/.github/workflows/pre-commit.yaml +++ b/.github/workflows/pre-commit.yaml @@ -11,9 +11,10 @@ jobs: pre-commit: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: actions/setup-python@v2 + - uses: actions/checkout@v4 + - uses: actions/setup-python@v4 - name: clang format support run: | + sudo apt update sudo apt install clang-format cppcheck - - uses: pre-commit/action@v2.0.3 + - uses: pre-commit/action@v3.0.0 diff --git a/bin/firmware.bin b/bin/firmware.bin index c203e59..abb20cc 100644 Binary files a/bin/firmware.bin and b/bin/firmware.bin differ diff --git a/bin/firmware32s2.bin b/bin/firmware32s2.bin index bbb0b2a..0574c91 100644 Binary files a/bin/firmware32s2.bin and b/bin/firmware32s2.bin differ diff --git a/bin/littlefs.bin b/bin/littlefs.bin new file mode 100644 index 0000000..3cb3899 Binary files /dev/null and b/bin/littlefs.bin differ diff --git a/html/backup.htm b/html/backup.htm index 9c766b3..3ab6326 100644 --- a/html/backup.htm +++ b/html/backup.htm @@ -1,4 +1,4 @@ - + @@ -16,7 +16,7 @@ - + - - + -
+
- + - + $("#alert-btn").click(function(e){ + $('.alert').addClass('hide').removeClass('show').addClass('d-none'); + }); + -
+
-
-

- -

-
-
+
+

+ +

+
+
-
-
Backup configuration to file
-
+
+
Backup configuration to file
+
- - + + +
-
-
-

- -

-
-
- -
-
Restore configuration from file
-
+
+

+ +

+
+
-
-
- -
+
Restore configuration from file
-
-
- + + +
+
+ +
+
+
+ +
+
+ + +
+
- -
-
-
-
- -

Backup configuration to file

Restore configuration from file

Backup configuration to file

Restore configuration from file
(C) Copyright 2021-23 Magnus Persson
\ No newline at end of file + }
(C) Copyright 2021-23 Magnus Persson
\ No newline at end of file diff --git a/platformio.ini b/platformio.ini index 34f98d5..1986aa7 100644 --- a/platformio.ini +++ b/platformio.ini @@ -33,8 +33,8 @@ build_flags = -D PUSH_INFLUX_BUCKET=\""\"" -D PUSH_INFLUX_ORG=\""\"" -D PUSH_INFLUX_TOKEN=\""\"" - -D CFG_GITREV=\""beta1\"" - #!python script/git_rev.py + #-D CFG_GITREV=\""beta2\"" + !python script/git_rev.py lib_deps = https://github.com/mp-se/SimpleKalmanFilter#v0.2 https://github.com/mp-se/Statistic#1.0.5 @@ -43,14 +43,14 @@ lib_deps = https://github.com/mp-se/Adafruit_Sensor#1.1.11 https://github.com/mp-se/incbin#v1.0.0 #https://github.com/mp-se/Arduino-Log#1.1.1 - https://github.com/mp-se/Arduino-Log + https://github.com/mp-se/Arduino-Log#1.1.2 https://github.com/mp-se/ArduinoJson#v6.21.3 https://github.com/mp-se/arduino-mqtt#v2.5.1 https://github.com/mp-se/tinyexpr#v1.0.0 https://github.com/mp-se/NAU7802_Arduino_Library#v1.0.4 https://github.com/mp-se/LiquidCrystal_I2C#1.0.0 https://github.com/mp-se/espframework#0.6.4 - https://github.com/mp-se/Adafruit_BME280_Library#2.2.2 + https://github.com/mp-se/Adafruit_BME280_Library#2.2.3 html_files = html/upload.min.htm html/config.min.htm @@ -101,8 +101,8 @@ build_flags = -D LOG_LEVEL=5 -D USE_ASYNC_WEB lib_deps = - https://github.com/mp-se/ESPAsyncWebServer - https://github.com/mp-se/ESPAsyncTCP + https://github.com/mp-se/ESPAsyncWebServer#0.1.0 + https://github.com/mp-se/ESPAsyncTCP#0.1.0 ${common_env_data.lib_deps} board = d1_mini build_type = release @@ -151,7 +151,7 @@ build_flags = ${common_env_data.build_flags} -D LOG_LEVEL=6 lib_deps = - https://github.com/bxparks/AUnit#v1.6.1 + https://github.com/bxparks/AUnit#v1.7.1 ${common_env_data.lib_deps} board = d1_mini build_type = release @@ -176,8 +176,8 @@ build_flags = #-D CORE_DEBUG_LEVEL=5 ${common_env_data.build_flags} lib_deps = - https://github.com/mp-se/ESPAsyncWebServer - https://github.com/mp-se/AsyncTCP + https://github.com/mp-se/ESPAsyncWebServer#0.1.0 + https://github.com/mp-se/AsyncTCP#0.1.0 ${common_env_data.lib_deps} build_type = release board_build.partitions = part32.csv @@ -202,8 +202,8 @@ build_flags = #-D CORE_DEBUG_LEVEL=5 ${common_env_data.build_flags} lib_deps = - https://github.com/mp-se/ESPAsyncWebServer - https://github.com/mp-se/AsyncTCP + https://github.com/mp-se/ESPAsyncWebServer#0.1.0 + https://github.com/mp-se/AsyncTCP#0.1.0 ${common_env_data.lib_deps} build_type = debug board_build.partitions = part32_coredump.csv @@ -227,9 +227,10 @@ build_flags = -D CORE_DEBUG_LEVEL=5 ${common_env_data.build_flags} lib_deps = - https://github.com/mp-se/ESPAsyncWebServer - https://github.com/mp-se/AsyncTCP + https://github.com/mp-se/ESPAsyncWebServer#0.1.0 + https://github.com/mp-se/AsyncTCP#0.1.0 ${common_env_data.lib_deps} build_type = debug +board_build.filesystem = littlefs board_build.partitions = part32.csv board_build.embed_txtfiles = ${common_env_data.html_files} diff --git a/script/merge_firmware.py b/script/merge_firmware.py index 9e543df..925f410 100644 --- a/script/merge_firmware.py +++ b/script/merge_firmware.py @@ -10,6 +10,9 @@ def merge_bin(source, target, env): # the final application binary flash_images = env.Flatten(env.get("FLASH_EXTRA_IMAGES", [])) + ["$ESP32_APP_OFFSET", APP_BIN] + flash_images.append( "0x390000" ) + flash_images.append( "bin/littlefs.bin" ) + # Run esptool to merge images into a single binary env.Execute( " ".join( diff --git a/src/kegconfig.cpp b/src/kegconfig.cpp index 5256479..1a2afe7 100644 --- a/src/kegconfig.cpp +++ b/src/kegconfig.cpp @@ -56,37 +56,50 @@ void KegConfig::createJson(DynamicJsonDocument& doc, bool skipSecrets) { doc[PARAM_SCALE_TEMP_FORMULA1] = getScaleTempCompensationFormula(UnitIndex::U1); - doc[PARAM_SCALE_FACTOR1] = getScaleFactor(UnitIndex::U1); + doc[PARAM_SCALE_FACTOR1] = + serialized(String(getScaleFactor(UnitIndex::U1), 5)); doc[PARAM_SCALE_OFFSET1] = getScaleOffset(UnitIndex::U1); - doc[PARAM_KEG_WEIGHT1] = convertOutgoingWeight(getKegWeight(UnitIndex::U1)); - doc[PARAM_KEG_VOLUME1] = - getKegVolume(UnitIndex::U1); // Dont convert this part (drop down in UI) - doc[PARAM_GLASS_VOLUME1] = getGlassVolume( - UnitIndex::U1); // Dont convert this part (drop down in UI) + doc[PARAM_KEG_WEIGHT1] = + serialized(String(convertOutgoingWeight(getKegWeight(UnitIndex::U1)), + getWeightPrecision())); + doc[PARAM_KEG_VOLUME1] = serialized(String( + getKegVolume(UnitIndex::U1), + getWeightPrecision())); // Dont convert this part (drop down in UI) + doc[PARAM_GLASS_VOLUME1] = serialized(String( + getGlassVolume(UnitIndex::U1), + getWeightPrecision())); // Dont convert this part (drop down in UI) doc[PARAM_BEER_NAME1] = getBeerName(UnitIndex::U1); - doc[PARAM_BEER_ABV1] = getBeerABV(UnitIndex::U1); - doc[PARAM_BEER_FG1] = getBeerFG(UnitIndex::U1); + doc[PARAM_BEER_ABV1] = serialized(String(getBeerABV(UnitIndex::U1), 2)); + doc[PARAM_BEER_FG1] = serialized(String(getBeerFG(UnitIndex::U1), 2)); doc[PARAM_BEER_EBC1] = getBeerEBC(UnitIndex::U1); doc[PARAM_BEER_IBU1] = getBeerIBU(UnitIndex::U1); doc[PARAM_SCALE_TEMP_FORMULA2] = getScaleTempCompensationFormula(UnitIndex::U2); - doc[PARAM_SCALE_FACTOR2] = getScaleFactor(UnitIndex::U2); + doc[PARAM_SCALE_FACTOR2] = + serialized(String(getScaleFactor(UnitIndex::U2), 5)); doc[PARAM_SCALE_OFFSET2] = getScaleOffset(UnitIndex::U2); - doc[PARAM_KEG_WEIGHT2] = convertOutgoingWeight(getKegWeight(UnitIndex::U2)); - doc[PARAM_KEG_VOLUME2] = - getKegVolume(UnitIndex::U2); // Dont convert this part (drop down in UI) - doc[PARAM_GLASS_VOLUME2] = getGlassVolume( - UnitIndex::U2); // Dont convert this part (drop down in UI) + doc[PARAM_KEG_WEIGHT2] = + serialized(String(convertOutgoingWeight(getKegWeight(UnitIndex::U2)), + getWeightPrecision())); + doc[PARAM_KEG_VOLUME2] = serialized(String( + getKegVolume(UnitIndex::U2), + getWeightPrecision())); // Dont convert this part (drop down in UI) + doc[PARAM_GLASS_VOLUME2] = + serialized(String(getGlassVolume(UnitIndex::U2), + 2)); // Dont convert this part (drop down in UI) doc[PARAM_BEER_NAME2] = getBeerName(UnitIndex::U2); - doc[PARAM_BEER_ABV2] = getBeerABV(UnitIndex::U2); - doc[PARAM_BEER_FG2] = getBeerFG(UnitIndex::U2); + doc[PARAM_BEER_ABV2] = serialized(String(getBeerABV(UnitIndex::U2), 2)); + doc[PARAM_BEER_FG2] = serialized(String(getBeerFG(UnitIndex::U2), 2)); doc[PARAM_BEER_EBC2] = getBeerEBC(UnitIndex::U2); doc[PARAM_BEER_IBU2] = getBeerIBU(UnitIndex::U2); - doc[PARAM_SCALE_DEVIATION_INCREASE] = getScaleDeviationIncreaseValue(); - doc[PARAM_SCALE_DEVIATION_DECREASE] = getScaleDeviationDecreaseValue(); - doc[PARAM_SCALE_DEVIATION_KALMAN] = getScaleKalmanDeviationValue(); + doc[PARAM_SCALE_DEVIATION_INCREASE] = + serialized(String(getScaleDeviationIncreaseValue(), 2)); + doc[PARAM_SCALE_DEVIATION_DECREASE] = + serialized(String(getScaleDeviationDecreaseValue(), 2)); + doc[PARAM_SCALE_DEVIATION_KALMAN] = + serialized(String(getScaleKalmanDeviationValue(), 2)); doc[PARAM_SCALE_READ_COUNT] = getScaleReadCount(); doc[PARAM_SCALE_READ_COUNT_CALIBRATION] = getScaleReadCountCalibration(); doc[PARAM_SCALE_STABLE_COUNT] = getScaleStableCount(); diff --git a/src_docs/source/development.rst b/src_docs/source/development.rst new file mode 100644 index 0000000..d9c850c --- /dev/null +++ b/src_docs/source/development.rst @@ -0,0 +1,46 @@ +.. _development: + +Development +=========== + +Development of this project is done in VSCode with the PlatformIO extension using Arduino for ESP8266 and ESP32. + +For the documentation we use SPINX and github pages for publishing. + +Compliance to coding standards PRE-COMMIT is used with their standard rules for formatting and good cpp code. Any PR needs +to pass this validation in order to be considered for merging. + +The pipeline is build based on github actions and all firmware builds and documentation publishing is done using the +defined github actions. + +For testing there is a UNIT TEST target defined which currently needs to be run on hardware. The plan is to move this to +WOKWI and their github action to run these after a completed build. There is also a python script that can be used to validate the +output of the available API's to ensure they deliver what is wanted. + +Future +------ + +The following additions is being worked on. + +* WOKWI simulator, so that the software can be run wihtout the need for hardware. +* WOKWI CI, to run the unit test and API tests during builds. +* Update PR checks to include builds and unit testing. + +Links +----- +If you want to start contributing these are links to the tools I use. + +* https://code.visualstudio.com/ (Code Editor) +* https://platformio.org/ (Arduino development) +* https://wokwi.com/ (Simulator for ESP32) +* https://marketplace.visualstudio.com/items?itemName=HookyQR.minify (Extension for making HTML files smaller) +* https://marketplace.visualstudio.com/items?itemName=platformio.platformio-ide (Platform IO extension) +* https://marketplace.visualstudio.com/items?itemName=Wokwi.wokwi-vscode (Wokwi simulator extension) +* https://pre-commit.com/ (Code validation, need to run this in Linux/WSL) +* https://cli.github.com/ (Source code management) +* https://gitforwindows.org/ (Git for Windows) + +Other projects that are of interrest for development + +* https://github.com/nektos/act (Run github actions locally) + diff --git a/src_docs/source/index.rst b/src_docs/source/index.rst index 80c3a83..fc6f7e8 100644 --- a/src_docs/source/index.rst +++ b/src_docs/source/index.rst @@ -147,6 +147,7 @@ Thanks to the following projects. installation configuration license + development q_and_a diff --git a/test/dependecy.py b/test/dependecy.py new file mode 100644 index 0000000..6159816 --- /dev/null +++ b/test/dependecy.py @@ -0,0 +1,138 @@ +import requests +import configparser +import json + +debug = False +useFiles = False + +githubUrl = "https://api.github.com/repos/" + +def get_github_latest_tag(repo): + if repo == "": + return "" + + if debug: print("Debug:", githubUrl + repo + "/tags") + response = requests.get(githubUrl + repo + "/tags") + if debug: print("Debug:", "Return code:", response.status_code) + + if response.status_code == 403: + print( "Error:", "Probably rate limiting has kicked in.....") + + if response.status_code == 200: + json = response.json() + if debug: write_json("debug_tag.json", json) + + if len(json) > 0: + if debug: print("Debug:", repo, json[0]["name"]) + return json[0]["name"] + + # print("Error:", "No releases found for:", repo) + return "" + +def get_github_parent(repo): + if repo == "": + return "" + + if debug: print("Debug:", githubUrl + repo) + response = requests.get(githubUrl + repo) + if debug: print("Debug:", "Return code:", response.status_code) + + if response.status_code == 200: + json = response.json() + if "parent" in json: + if debug: print("Debug:", repo, json["parent"]["full_name"]) + if debug: write_json("debug_parent.json", json) + return json["parent"]["full_name"] + + return "" + +def get_github_repo_version(repoList): + deps = [] + + for repo in repoList: + tag = get_github_latest_tag(repo) + parent = get_github_parent(repo) + ptag = get_github_latest_tag(parent) + dep = { "repo": repo, "tag": tag, "parent": parent, "parentTag": ptag } + deps.append(dep) + + return deps + +def resolve_platformio_key(config, key): + key = key.removeprefix("${") + key = key.removesuffix("}") + libs = config.get(key.split(".")[0], key.split(".")[1]) + return libs.split() + +def find_platformio_libs(): + config = configparser.ConfigParser() + config.read('platformio.ini') + + projectRepos = [] + + for section in config.sections(): + if section.startswith("env:"): + libs = config.get(section, "lib_deps" ) + for lib in libs.split(): + if lib.startswith("${") and lib.endswith("}"): + projectRepos = projectRepos + resolve_platformio_key(config, lib) + else: + projectRepos.append(lib) + + # Remove duplicated entries + projectRepos = list(set(projectRepos)) + + # list of the following objects { "repo": "", "tag": "" } + dependecies = [] + + for dep in projectRepos: + s = dep.removeprefix("https://github.com/").split("#") + if len(s) == 2: + e = { "repo": s[0], "tag": s[1] } + dependecies.append(e) + else: + e = { "repo": s[0], "tag": "" } + dependecies.append(e) + + # Entires have format; repository # tag + return dependecies + +def write_json(file, data): + f = open(file, "w") + f.write( json.dumps(data, indent=2) ) + f.close() + +def read_json(file): + f = open(file, "r") + data = json.loads( "".join(f.readlines()) ) + f.close() + return data + +if __name__ == '__main__': + print("Dependency checker") + + print("Reading platformio configuration") + if useFiles: + depLibs = read_json("project_deps.json") + else: + depLibs = find_platformio_libs() + write_json("project_deps.json", depLibs) + + print("Fetching data from github on latest tags") + if useFiles: + repos = read_json("repos.json") + else: + repoList = [] + for d in depLibs: + repoList.append(d["repo"]) + + repos = get_github_repo_version(repoList) + write_json("repos.json", repos) + + for i in depLibs: + for j in repos: + if i["repo"] == j["repo"]: + if i["tag"] != j["tag"] or j["tag"] != j["parentTag"]: + print("Check dependency for:", i["repo"], "platformio:", i["tag"], "git:", j["tag"]," => parent:", j["parentTag"] ) + + print("Done...")