Restructured full repo

This commit is contained in:
Martin Bauer 2020-06-05 21:41:16 +02:00
parent daa2454e71
commit 9bf3287944
46 changed files with 1913 additions and 107 deletions

8
.gitignore vendored
View File

@ -1,4 +1,4 @@
.pio
CMakeListsPrivate.txt
/cmake-build*
/venv
.ipynb_checkpoints
__pycache__

View File

@ -1,77 +0,0 @@
# !!! WARNING !!! AUTO-GENERATED FILE, PLEASE DO NOT MODIFY IT AND USE
# https://docs.platformio.org/page/projectconf/section_env_build.html#build-flags
#
# If you need to override existing CMake configuration or add extra,
# please create `CMakeListsUser.txt` in the root of project.
# The `CMakeListsUser.txt` will not be overwritten by PlatformIO.
cmake_minimum_required(VERSION 3.2)
project(firmware)
include(CMakeListsPrivate.txt)
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/CMakeListsUser.txt)
include(CMakeListsUser.txt)
endif()
add_custom_target(
PLATFORMIO_BUILD ALL
COMMAND ${PLATFORMIO_CMD} -f -c clion run
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)
add_custom_target(
PLATFORMIO_BUILD_VERBOSE ALL
COMMAND ${PLATFORMIO_CMD} -f -c clion run --verbose
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)
add_custom_target(
PLATFORMIO_UPLOAD ALL
COMMAND ${PLATFORMIO_CMD} -f -c clion run --target upload
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)
add_custom_target(
PLATFORMIO_CLEAN ALL
COMMAND ${PLATFORMIO_CMD} -f -c clion run --target clean
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)
add_custom_target(
PLATFORMIO_MONITOR ALL
COMMAND ${PLATFORMIO_CMD} -f -c clion device monitor
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)
add_custom_target(
PLATFORMIO_TEST ALL
COMMAND ${PLATFORMIO_CMD} -f -c clion test
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)
add_custom_target(
PLATFORMIO_PROGRAM ALL
COMMAND ${PLATFORMIO_CMD} -f -c clion run --target program
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)
add_custom_target(
PLATFORMIO_UPLOADFS ALL
COMMAND ${PLATFORMIO_CMD} -f -c clion run --target uploadfs
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)
add_custom_target(
PLATFORMIO_UPDATE_ALL ALL
COMMAND ${PLATFORMIO_CMD} -f -c clion update
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)
add_custom_target(
PLATFORMIO_REBUILD_PROJECT_INDEX ALL
COMMAND ${PLATFORMIO_CMD} -f -c clion init --ide clion
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)
add_executable(${PROJECT_NAME} ${SRC_LIST})

16
README.md Normal file
View File

@ -0,0 +1,16 @@
Swimtracker Firmware & Hardware
===============================
Firmware
--------
Run tests:
```
pio test -v -e native
```
Compile and upload:
```
pio run -e d1 -t upload
```

View File

@ -1,7 +0,0 @@
Test locally
platformio test -v -e native
Device:
platformio device monitor
platformio run -t upload

View File

@ -1,6 +0,0 @@
92 x 28 x 30
35x20
95x 50 x 30 (absolute min)
100 x 55 x 35

Binary file not shown.

Binary file not shown.

BIN
example-data/03_rebecca.st Normal file

Binary file not shown.

BIN
example-data/04.st Normal file

Binary file not shown.

BIN
example-data/05.st Normal file

Binary file not shown.

BIN
example-data/06.st Normal file

Binary file not shown.

BIN
example-data/07.st Normal file

Binary file not shown.

BIN
example-data/1589394292.st Normal file

Binary file not shown.

2
hardware/OtherBoards.md Normal file
View File

@ -0,0 +1,2 @@
https://github.com/adafruit/Adafruit-HUZZAH32-ESP32-Feather-PCB.git

254
hardware/box.svg Normal file
View File

@ -0,0 +1,254 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="210mm"
height="297mm"
viewBox="0 0 210 297"
version="1.1"
id="svg8"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
sodipodi:docname="box.svg">
<defs
id="defs2" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="2.8"
inkscape:cx="243.39841"
inkscape:cy="149.58268"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="3840"
inkscape:window-height="2051"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<rect
style="opacity:1;fill:#9d9d9d;fill-opacity:0.36078431;stroke:none;stroke-width:0.12890768;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect815"
width="54"
height="94"
x="1"
y="202" />
<g
id="g842"
transform="translate(-59.926651,52.797058)">
<circle
r="2"
cy="237.20294"
cx="68.926651"
id="path836"
style="opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.06397314;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<circle
r="1"
cy="237.20294"
cx="68.926651"
id="path838"
style="opacity:1;fill:#a7a7a7;fill-opacity:1;stroke:none;stroke-width:0.13229166;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
</g>
<g
id="g931">
<circle
r="3.3499999"
cy="292.64999"
cx="4.3499999"
id="path836-3"
style="opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.10715501;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<circle
r="1"
cy="292.64999"
cx="4.3499999"
id="path838-5"
style="opacity:1;fill:#a7a7a7;fill-opacity:1;stroke:none;stroke-width:0.13229166;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
</g>
<g
id="g842-61"
transform="translate(-59.926651,24.797058)">
<circle
r="2"
cy="237.20294"
cx="68.926651"
id="path836-8"
style="opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.06397314;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<circle
r="1"
cy="237.20294"
cx="68.926651"
id="path838-79"
style="opacity:1;fill:#a7a7a7;fill-opacity:1;stroke:none;stroke-width:0.13229166;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
</g>
<g
id="g842-61-2"
transform="translate(-59.926651,-1.2029419)">
<circle
r="2"
cy="237.20294"
cx="68.926651"
id="path836-8-0"
style="opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.06397314;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<circle
r="1"
cy="237.20294"
cx="68.926651"
id="path838-79-2"
style="opacity:1;fill:#a7a7a7;fill-opacity:1;stroke:none;stroke-width:0.13229166;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
</g>
<g
id="g842-61-2-3"
transform="translate(-59.926651,-29.202942)">
<circle
r="2"
cy="237.20294"
cx="68.926651"
id="path836-8-0-7"
style="opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.06397314;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<circle
r="1"
cy="237.20294"
cx="68.926651"
id="path838-79-2-5"
style="opacity:1;fill:#a7a7a7;fill-opacity:1;stroke:none;stroke-width:0.13229166;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
</g>
<g
transform="translate(0,-87.299994)"
id="g931-9">
<circle
r="3.3499999"
cy="292.64999"
cx="4.3499999"
id="path836-3-2"
style="opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.10715501;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<circle
r="1"
cy="292.64999"
cx="4.3499999"
id="path838-5-28"
style="opacity:1;fill:#a7a7a7;fill-opacity:1;stroke:none;stroke-width:0.13229166;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
</g>
<g
id="g842-2"
transform="matrix(-1,0,0,1,115.92665,52.797064)">
<circle
r="2"
cy="237.20294"
cx="68.926651"
id="path836-0"
style="opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.06397314;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<circle
r="1"
cy="237.20294"
cx="68.926651"
id="path838-6"
style="opacity:1;fill:#a7a7a7;fill-opacity:1;stroke:none;stroke-width:0.13229166;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
</g>
<g
id="g931-8"
transform="matrix(-1,0,0,1,56,6.1988831e-6)">
<circle
r="3.3499999"
cy="292.64999"
cx="4.3499999"
id="path836-3-9"
style="opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.10715501;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<circle
r="1"
cy="292.64999"
cx="4.3499999"
id="path838-5-26"
style="opacity:1;fill:#a7a7a7;fill-opacity:1;stroke:none;stroke-width:0.13229166;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
</g>
<g
id="g842-61-6"
transform="matrix(-1,0,0,1,115.92665,24.797064)">
<circle
r="2"
cy="237.20294"
cx="68.926651"
id="path836-8-4"
style="opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.06397314;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<circle
r="1"
cy="237.20294"
cx="68.926651"
id="path838-79-9"
style="opacity:1;fill:#a7a7a7;fill-opacity:1;stroke:none;stroke-width:0.13229166;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
</g>
<g
id="g842-61-2-5"
transform="matrix(-1,0,0,1,115.92665,-1.2029357)">
<circle
r="2"
cy="237.20294"
cx="68.926651"
id="path836-8-0-0"
style="opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.06397314;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<circle
r="1"
cy="237.20294"
cx="68.926651"
id="path838-79-2-4"
style="opacity:1;fill:#a7a7a7;fill-opacity:1;stroke:none;stroke-width:0.13229166;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
</g>
<g
id="g842-61-2-3-8"
transform="matrix(-1,0,0,1,115.92665,-29.202936)">
<circle
r="2"
cy="237.20294"
cx="68.926651"
id="path836-8-0-7-7"
style="opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.06397314;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<circle
r="1"
cy="237.20294"
cx="68.926651"
id="path838-79-2-5-1"
style="opacity:1;fill:#a7a7a7;fill-opacity:1;stroke:none;stroke-width:0.13229166;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
</g>
<g
transform="matrix(-1,0,0,1,56,-87.299988)"
id="g931-9-7">
<circle
r="3.3499999"
cy="292.64999"
cx="4.3499999"
id="path836-3-2-2"
style="opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.10715501;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<circle
r="1"
cy="292.64999"
cx="4.3499999"
id="path838-5-28-7"
style="opacity:1;fill:#a7a7a7;fill-opacity:1;stroke:none;stroke-width:0.13229166;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 8.8 KiB

BIN
hardware/data_format.ods Normal file

Binary file not shown.

235
hardware/hardware.svg Normal file
View File

@ -0,0 +1,235 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="210mm"
height="297mm"
viewBox="0 0 210 297"
version="1.1"
id="svg8"
inkscape:version="0.92.3 (2405546, 2018-03-11)"
sodipodi:docname="hardware.svg">
<defs
id="defs2" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="4"
inkscape:cx="130.51056"
inkscape:cy="1008.5137"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:window-width="3840"
inkscape:window-height="2061"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
fit-margin-top="20"
fit-margin-left="20"
fit-margin-right="20"
fit-margin-bottom="20">
<inkscape:grid
type="xygrid"
id="grid13"
units="cm"
spacingx="1"
spacingy="1" />
</sodipodi:namedview>
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Platine"
inkscape:groupmode="layer"
id="layer1">
<g
id="g863"
transform="matrix(1.0333333,0,0,1.0902484,-30.407635,-26.688372)">
<rect
style="fill:#decd87;stroke-width:0.26458332"
y="24.479166"
x="29.426744"
height="55"
width="90"
id="rect11" />
<rect
y="24.479166"
x="29.426744"
height="9.1722221"
width="9.6774197"
id="rect15"
style="fill:#ffffff;fill-opacity:1;stroke-width:0.24927546" />
<rect
y="24.479168"
x="109.74933"
height="9.1722221"
width="9.6774197"
id="rect15-3"
style="fill:#ffffff;fill-opacity:1;stroke-width:0.24927546" />
<rect
y="70.340279"
x="109.74933"
height="9.1722221"
width="9.6774197"
id="rect15-3-6"
style="fill:#ffffff;fill-opacity:1;stroke-width:0.24927546" />
<rect
y="70.340279"
x="29.426743"
height="9.1722221"
width="9.6774197"
id="rect15-3-7"
style="fill:#ffffff;fill-opacity:1;stroke-width:0.24927546" />
</g>
<circle
style="fill:#131212;fill-opacity:1;stroke-width:0.10583332"
id="path1091"
cx="26"
cy="5.6321311"
r="2" />
<circle
style="fill:#131212;fill-opacity:1;stroke-width:0.10583332"
id="path1091-2-9"
cx="75.716965"
cy="54.731785"
r="2" />
</g>
<g
inkscape:groupmode="layer"
id="layer2"
inkscape:label="ICs">
<rect
style="fill:#308100;fill-opacity:1;stroke-width:0.26458332"
id="rect884"
width="60"
height="30"
x="29"
y="13" />
<rect
style="fill:#000000;fill-opacity:1;stroke-width:0.26458332"
id="rect901"
width="3"
height="8"
x="86"
y="24" />
<flowRoot
xml:space="preserve"
id="flowRoot905-3"
style="font-style:normal;font-weight:normal;font-size:8px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#ffb8b8;fill-opacity:1;stroke:none"
transform="matrix(0.26458333,0,0,0.26458333,-27.693747,24.134435)"><flowRegion
id="flowRegion907-5"
style="font-size:8px;fill:#ffb8b8;fill-opacity:1"><rect
id="rect909-6"
width="105.82677"
height="45.354332"
x="241.88977"
y="-37.795277"
style="font-size:8px;fill:#ffb8b8;fill-opacity:1" /></flowRegion><flowPara
id="flowPara911-2">D2</flowPara></flowRoot> <flowRoot
xml:space="preserve"
id="flowRoot905-3-9"
style="font-style:normal;font-weight:normal;font-size:8px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#ffb8b8;fill-opacity:1;stroke:none"
transform="matrix(0.26458333,0,0,0.26458333,-23.261594,24.134422)"><flowRegion
id="flowRegion907-5-1"
style="font-size:8px;fill:#ffb8b8;fill-opacity:1"><rect
id="rect909-6-2"
width="105.82677"
height="45.354332"
x="241.88977"
y="-37.795277"
style="font-size:8px;fill:#ffb8b8;fill-opacity:1" /></flowRegion><flowPara
id="flowPara911-2-7">D2</flowPara></flowRoot> <flowRoot
xml:space="preserve"
id="flowRoot905-3-9-0"
style="font-style:normal;font-weight:normal;font-size:8px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#ffb8b8;fill-opacity:1;stroke:none"
transform="matrix(0.26458333,0,0,0.26458333,18.473597,24.357072)"><flowRegion
id="flowRegion907-5-1-9"
style="font-size:8px;fill:#ffb8b8;fill-opacity:1"><rect
id="rect909-6-2-3"
width="105.82677"
height="45.354332"
x="241.88977"
y="-37.795277"
style="font-size:8px;fill:#ffb8b8;fill-opacity:1" /></flowRegion><flowPara
id="flowPara911-2-7-6">G</flowPara></flowRoot> <flowRoot
xml:space="preserve"
id="flowRoot905-3-9-0-0"
style="font-style:normal;font-weight:normal;font-size:8px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#ffb8b8;fill-opacity:1;stroke:none"
transform="matrix(0.26458333,0,0,0.26458333,1.4735971,50.357061)"><flowRegion
id="flowRegion907-5-1-9-6"
style="font-size:8px;fill:#ffb8b8;fill-opacity:1"><rect
id="rect909-6-2-3-2"
width="105.82677"
height="45.354332"
x="241.88977"
y="-37.795277"
style="font-size:8px;fill:#ffb8b8;fill-opacity:1" /></flowRegion><flowPara
id="flowPara911-2-7-6-6">3V</flowPara></flowRoot> <g
id="g1158"
transform="translate(-1.0583333,1.5875)">
<rect
transform="rotate(90)"
y="-25"
x="11"
height="20"
width="34"
id="rect884-5"
style="fill:#308100;fill-opacity:1;stroke-width:0.1626225" />
<flowRoot
transform="matrix(0.00160618,-0.26457845,0.26457845,0.00160618,19.146666,81.258341)"
style="font-style:normal;font-weight:normal;font-size:8px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#ffb8b8;fill-opacity:1;stroke:none"
id="flowRoot905-3-1"
xml:space="preserve"><flowRegion
style="font-size:8px;fill:#ffb8b8;fill-opacity:1"
id="flowRegion907-5-8"><rect
style="font-size:8px;fill:#ffb8b8;fill-opacity:1"
y="-37.795277"
x="241.88977"
height="40.703068"
width="19.455503"
id="rect909-6-7" /></flowRegion><flowPara
id="flowPara911-2-9">GND</flowPara><flowPara
id="flowPara1033">DT</flowPara><flowPara
id="flowPara1035">SCK</flowPara><flowPara
id="flowPara1037">VCC</flowPara></flowRoot> <flowRoot
transform="matrix(0.00160618,-0.26457846,0.26457846,0.00160618,16.500831,106.78753)"
style="font-style:normal;font-weight:normal;font-size:8px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#ffb8b8;fill-opacity:1;stroke:none"
id="flowRoot905-3-1-2"
xml:space="preserve"><flowRegion
style="font-size:8px;fill:#ffb8b8;fill-opacity:1"
id="flowRegion907-5-8-0"><rect
style="font-size:8px;fill:#ffb8b8;fill-opacity:1"
y="-37.795277"
x="241.88977"
height="62.108101"
width="20.026527"
id="rect909-6-7-2" /></flowRegion><flowPara
id="flowPara1037-9">E+</flowPara><flowPara
id="flowPara1081">E-</flowPara><flowPara
id="flowPara1083">A-</flowPara><flowPara
id="flowPara1085">A+</flowPara><flowPara
id="flowPara1087">B-B+</flowPara><flowPara
id="flowPara1089" /></flowRoot> </g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 8.6 KiB

26
hardware/hardware.txt Normal file
View File

@ -0,0 +1,26 @@
Load Cell 50kg
https://de.aliexpress.com/item/32961462970.html?spm=a2g0o.productlist.0.0.46d91cca2fXbja&algo_pvid=b14c8be3-c372-4971-a7e6-b6ba13e03236&algo_expid=b14c8be3-c372-4971-a7e6-b6ba13e03236-1&btsid=0f841574-ad82-47f9-bbbd-fa1c46b4bba4&ws_ab_test=searchweb0_0,searchweb201602_2,searchweb201603_52
$24
Wemos ESP32 with 18650 battery holder, Groesse: 9.1 cm x 3.0 cm, usb at the side
https://de.aliexpress.com/item/32860090110.html?spm=a2g0o.productlist.0.0.67c732eeceGb5S&algo_pvid=4f5823a3-2d47-4afb-95a5-7ba85055f121&algo_expid=4f5823a3-2d47-4afb-95a5-7ba85055f121-4&btsid=ea1860aa-ff90-4cd7-bb92-ade7cfa85eac&ws_ab_test=searchweb0_0,searchweb201602_2,searchweb201603_52
$ 5
Akku
https://de.aliexpress.com/item/32908447004.html?spm=a2g0o.productlist.0.0.5f78199bMJJy0k&algo_pvid=b0eb97b9-df11-41ea-bb67-e96c1df7d292&algo_expid=b0eb97b9-df11-41ea-bb67-e96c1df7d292-0&btsid=b910d832-bcde-4807-8a97-edbbcca846df&ws_ab_test=searchweb0_0,searchweb201602_2,searchweb201603_52
$ 3
Connector
https://de.aliexpress.com/item/32899642702.html?spm=a2g0x.search0302.3.54.34441675lAu97T&ws_ab_test=searchweb0_0,searchweb201602_0_453_454_10618_536_317_537_319_10059_10696_10084_10083_10547_10304_10821_10843_10887_10307_321_10548_322_10065_10068_10103_10884_10820_10302,searchweb201603_0,ppcSwitch_0&algo_pvid=d5cf53ae-9cc8-45b9-ab1c-c77f38b7c706&algo_expid=d5cf53ae-9cc8-45b9-ab1c-c77f38b7c706-6
$1.5
HX711
https://de.aliexpress.com/item/32878181081.html?spm=a2g0o.productlist.0.0.19202f024UQZQP&algo_pvid=b4f3e85e-9190-4b8d-9b9e-0100fa84037e&algo_expid=b4f3e85e-9190-4b8d-9b9e-0100fa84037e-1&btsid=ae27657b-596a-43fa-8cc8-bc9cba1d5bcf&ws_ab_test=searchweb0_0,searchweb201602_2,searchweb201603_52
$1
Case
https://www.banggood.com/ABS-Plastic-Electronic-Enclosure-Project-Box-Black-103x64x40mm-p-951179.html?rmmds=buy&cur_warehouse=CN
$4-10

File diff suppressed because one or more lines are too long

30
python-mock/log.py Normal file
View File

@ -0,0 +1,30 @@
import matplotlib.pyplot as plt
import socket
import struct
import numpy as np
import datetime
device = '192.168.178.79'
def network_receive():
int_size = 4
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((device, 123))
number_of_elements = struct.unpack('i', s.recv(int_size, socket.MSG_WAITALL))[0]
session_start_time = struct.unpack('i', s.recv(int_size, socket.MSG_WAITALL))[0]
dtime = datetime.datetime.fromtimestamp(session_start_time)
print("Number of elements {}, session start {}".format(number_of_elements, dtime))
data_buffer = struct.unpack('{}i'.format(number_of_elements), s.recv(int_size * number_of_elements, socket.MSG_WAITALL))
s.close()
return np.array(data_buffer, dtype=np.int32)
data = network_receive()
plt.plot(data)
plt.show()
print(network_receive())
#data = []
#while True:
# value = network_receive()
# data.append(value)
# print(value, min(data), max(data))

View File

@ -0,0 +1,95 @@
import numpy as np
import msgpack
def _decoding_hook(code, data):
if code == -47 or code == 47 or code== -51:
return np.frombuffer(data, dtype=np.int16)
print(code)
return 'unknown ext'
def _encoding_hook(obj):
if isinstance(obj, np.ndarray) and obj.dtype == np.int16:
buffer = memoryview(obj).tobytes()
assert len(buffer) / 2 == len(obj)
return msgpack.ExtType(47, buffer)
raise TypeError("Cannot pack: %s of type %s" % (obj, str(type(obj))))
def deserialize(stream):
return msgpack.unpackb(stream, ext_hook=_decoding_hook, raw=False)
def serialize(obj):
return msgpack.packb(obj, default=_encoding_hook, use_bin_type=True)
def load_session_from_file(file):
with open(file, 'rb') as f:
file_contents = f.read()
return deserialize(file_contents)
def plot_session(session):
import matplotlib.pyplot as plt
y = session['values']
if 'timestamps' in session:
interval = 10
t = session['timestamps'] / interval
time_range_seconds = t[-1] - t[0]
description = f"Sparse session, {len(t)} data points, {time_range_seconds / 60} minutes"
plt.plot(session['timestamps'], session['values'], 'x-')
else:
measurement_interval = session.get('interval', 100)
t = np.arange(len(y)) * measurement_interval / 1000
description = f"Dense session, {len(t)} data points, {t[-1] / 60} minutes"
plt.plot(t, y)
plt.title(description)
def prune_overflown_session(session, max_elements=8 * 1024):
session = session.copy()
session['values'] = session['values'][:max_elements]
if 'timestamps' in session:
session['timestamps'] = session['timestamps'][:max_elements]
return session
def prune(session, beginning=10, end=10):
session = session.copy()
session['values'] = session['values'][beginning:-end]
session['timestamps'] = session['timestamps'][beginning:-end]
session['startIndex'] += beginning
return session
#def extend(session, value, beginning, end):
# session = session.copy()
# session['values'] = np.concatenate((np.ones([beginning]) * value, session['values'], np.ones([end]) * value))
# what to put in time stamps?
# return session
def start_at_index(session, index):
session = session.copy()
to_remove = index - session['startIndex']
if to_remove > 0:
session['values'] = session['values'][to_remove:]
if 'timestamps' in session:
session['timestamps'] = session['timestamps'][to_remove:]
return session
def test_serialization_deserialization(session_file):
with open(session_file, 'rb') as f:
raw_file_contents = f.read()
session = load_session_from_file(session_file)
serialized = serialize(session)
deserialized = deserialize(serialized)
np.testing.assert_equal(deserialized['values'], session['values'])
if __name__ == '__main__':
test_serialization_deserialization('../example_sessions/04.st')

16
python-mock/mock.py Normal file
View File

@ -0,0 +1,16 @@
import os
from flask import Flask, Response, request
from .measurement_session import load_session_from_file, serialize, start_at_index, prune_overflown_session
app = Flask(__name__)
SESSION_DIR = '../example_sessions'
@app.route("/api/sessionhistory/<session_id>", method=['GET'])
def session_history(session_id):
start_index = request.args.key.get('startIndex', 0)
file_name = os.path.join(SESSION_DIR, session_id) + '.st'
session = load_session_from_file(file_name)
session = prune_overflown_session(session)
session = start_at_index(session, start_index)
return Response(serialize(session), mimetype="application/x-msgpack")

View File

@ -0,0 +1,195 @@
import numpy as np
__all__ = ['PeakDetectorZScore', 'PeakDetectorSimple', 'detect_peaks']
def detect_peaks(values, detector):
for v in values:
detector.add(v)
return np.array(detector.peaks, dtype=int)
class PeakDetectorSimple:
def __init__(self, threshold):
self.peaks = []
self._queue = []
self.threshold = threshold
self._counter = 0
self._last_min = 0
def add(self, value):
self._queue.append(value)
if len(self._queue) > 3:
self._queue.pop(0)
if len(self._queue) != 3:
return
last, current, following = self._queue
is_maximum = current > following and current > last
if is_maximum and (current - self._last_min) > self.threshold:
self.peaks.append(self._counter + 1)
self._last_min = current
self._last_min = min(self._last_min, current)
self._counter += 1
class PeakDetectorZScore:
def __init__(self, lag, threshold, influence):
self._filter = ZScoreFilter(lag, threshold, influence)
self.peaks = []
self._counter = 0
self._previous_signal = 0
self._max = None
self._max_index = None
# debug
self.up_down_signal = []
def add(self, value):
signal = self._filter.add(value)
if signal is not None:
self.up_down_signal.append(signal)
rising_flank = self._previous_signal != 1 and signal == 1
falling_flank = self._previous_signal == 1 and signal != 1
if rising_flank:
self._max = -1
if signal == 1 and self._max is not None and value > self._max:
self._max = value
self._max_index = self._counter
if falling_flank:
self.peaks.append(self._max_index)
self._previous_signal = signal
self._counter += 1
class StatisticsQueue:
def __init__(self, size):
self._queue = []
self._queue_sum = 0
self._queue_sum_sq = 0
self._size = size
def add(self, value):
self._queue.append(value)
self._queue_sum += value
self._queue_sum_sq += value ** 2
if len(self._queue) > self._size:
removed = self._queue.pop(0)
self._queue_sum -= removed
self._queue_sum_sq -= removed ** 2
@property
def avg(self):
return self._queue_sum / len(self._queue)
@property
def variance(self):
exp_sq = self._queue_sum_sq / len(self._queue)
return exp_sq - self.avg ** 2
@property
def std_deviation(self):
return np.sqrt(self.variance)
@property
def filled(self):
return len(self._queue) == self._size
class ZScoreFilter:
def __init__(self, lag, threshold, influence):
self._threshold = threshold
self._influence = influence
self._stat_queue = StatisticsQueue(lag)
self._last_value = None
# debug
self.filtered = []
self.means = []
self.upper_bounds = []
def add(self, value):
sq = self._stat_queue
if not sq.filled:
sq.add(value)
self._last_value = value
return None
else:
avg = sq.avg
self.means.append(avg)
self.upper_bounds.append(avg + self._threshold * sq.std_deviation)
if abs(value - avg) > self._threshold * sq.std_deviation:
signal = 1 if value > avg else -1
filtered = self._influence * value + (1 - self._influence) * self._last_value
sq.add(filtered)
self._last_value = filtered
self.filtered.append(filtered)
return signal
else:
sq.add(value)
self._last_value = value
self.filtered.append(value)
return 0
def peak_detection_z_score(y, lag, threshold, influence):
signals = np.zeros(len(y))
filtered_y = np.array(y)
avg_filter = [0] * len(y)
std_filter = [0] * len(y)
avg_filter[lag - 1] = np.mean(y[0:lag])
std_filter[lag - 1] = np.std(y[0:lag])
for i in range(lag, len(y)):
if abs(y[i] - avg_filter[i - 1]) > threshold * std_filter[i - 1]:
if y[i] > avg_filter[i - 1]:
signals[i] = 1
else:
signals[i] = -1
filtered_y[i] = influence * y[i] + (1 - influence) * filtered_y[i - 1]
else:
signals[i] = 0
filtered_y[i] = y[i]
avg_filter[i] = np.mean(filtered_y[(i - lag + 1):i + 1])
std_filter[i] = np.std(filtered_y[(i - lag + 1):i + 1])
return dict(signals=np.asarray(signals),
avgFilter=np.asarray(avg_filter),
stdFilter=np.asarray(std_filter))
def test_zscore():
from measurement_session import load_session_from_file, prune_overflown_session, prune
import matplotlib.pyplot as plt
session_file = '../example_sessions/06.st'
session = load_session_from_file(session_file)
session = prune_overflown_session(session)
session = prune(session, 10, 50)
lag = 8
peak_detector_zscore = PeakDetectorZScore(lag=lag, threshold=2, influence=0)
peaks = detect_peaks(session['values'], peak_detector_zscore)
up_down = np.array([0] * lag + peak_detector_zscore.up_down_signal)
up_down[up_down < 0] = -10
up_down[up_down > 0] = 10000
avgs = np.array([0] * lag + peak_detector_zscore._filter.means)
filtered = np.array([0] * lag + peak_detector_zscore._filter.filtered)
plt.figure()
plt.plot(session['timestamps'], session['values'], 'x-', label='data')
plt.plot(session['timestamps'], filtered, 'x', label='filtered')
plt.plot(session['timestamps'], up_down, '-', label='up_down')
plt.plot(session['timestamps'], avgs, '-', label='avg')
# plt.plot(session['timestamps'][peaks+8], session['values'][peaks+8], 'o',
# label=f"Simple {peak_detector_simple.threshold}")
plt.title("Peak detection")
plt.show()
if __name__ == '__main__':
test_zscore()

50
python-mock/plot.py Normal file
View File

@ -0,0 +1,50 @@
import matplotlib.pyplot as plt
import numpy as np
from msgpack.fallback import unpackb
import requests
import array
def ext_hook(code, data):
#if code == -47:
if code == -51:
return np.frombuffer(data, dtype=np.int16)
else:
print(code)
return 'unknown ext'
def decode(stream):
return unpackb(stream, ext_hook=ext_hook, raw=False)
def from_file(file_name):
with open(file_name, 'rb') as f:
file_contents = f.read()
print("File size", len(file_contents))
res = decode(file_contents)
print(res)
plt.plot(res['values'], 'x-')
plt.show()
return res
def from_network(url="http://swimtrainer/api/session", start_index=0):
r = requests.get("{}?startIndex={}".format(url, start_index))
res = decode(r.content)
print(res)
plt.plot(res['timestamps'], res['values'], 'x-')
plt.show()
def analyze(data, max_size=8*1024):
time_stamps = data['timestamps'][:max_size]
values = data['values'][:max_size]
time_range_seconds = (time_stamps[-1] - time_stamps[0]) / 10
dense_time_range_seconds = max_size / 10
print("Time range {}, dense {} [minutes]".format(time_range_seconds / 60, dense_time_range_seconds / 60))
#from_network()
res = from_file('1589394292.st')
print(res)
#analyze(res)

6
readme
View File

@ -1,6 +0,0 @@
Run tests:
pio test -v -e native
Compile and upload:
pio run -e d1 -t upload

View File

@ -1,7 +0,0 @@
Backend:
- webdav
-> delete stuff
- SPIFFS remove if full, before rotate (i.e. delete oldest)
- auto-off, auto-on feature for session management