This commit is contained in:
Eden Kirin
2025-05-19 10:12:53 +02:00
commit a83cc44bf8
20 changed files with 1699 additions and 0 deletions

8
.gitignore vendored Normal file
View File

@ -0,0 +1,8 @@
.pioenvs
.piolibdeps
.clang_complete
.gcc-flags.json
/.vscode
/test/a.out
/test/o

View File

@ -0,0 +1 @@
63daa49347a7d1f33ca50cf95705163a684f2e55

470
doc/soba - cvrčci.svg Normal file
View File

@ -0,0 +1,470 @@
<?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 744.09448819 1052.3622047"
id="svg2"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="soba - cvrčci.svg">
<defs
id="defs4">
<marker
inkscape:stockid="DotL"
orient="auto"
refY="0.0"
refX="0.0"
id="marker8263"
style="overflow:visible"
inkscape:isstock="true">
<path
id="path8265"
d="M -2.5,-1.0 C -2.5,1.7600000 -4.7400000,4.0 -7.5,4.0 C -10.260000,4.0 -12.5,1.7600000 -12.5,-1.0 C -12.5,-3.7600000 -10.260000,-6.0 -7.5,-6.0 C -4.7400000,-6.0 -2.5,-3.7600000 -2.5,-1.0 z "
style="fill-rule:evenodd;stroke:#ff0000;stroke-width:1pt;stroke-opacity:1;fill:#ff0000;fill-opacity:1"
transform="scale(0.8) translate(7.4, 1)" />
</marker>
<marker
inkscape:stockid="DotL"
orient="auto"
refY="0.0"
refX="0.0"
id="marker8085"
style="overflow:visible"
inkscape:isstock="true">
<path
id="path8087"
d="M -2.5,-1.0 C -2.5,1.7600000 -4.7400000,4.0 -7.5,4.0 C -10.260000,4.0 -12.5,1.7600000 -12.5,-1.0 C -12.5,-3.7600000 -10.260000,-6.0 -7.5,-6.0 C -4.7400000,-6.0 -2.5,-3.7600000 -2.5,-1.0 z "
style="fill-rule:evenodd;stroke:#ff0000;stroke-width:1pt;stroke-opacity:1;fill:#ff0000;fill-opacity:1"
transform="scale(0.8) translate(7.4, 1)" />
</marker>
<marker
inkscape:stockid="DotL"
orient="auto"
refY="0.0"
refX="0.0"
id="marker7379"
style="overflow:visible"
inkscape:isstock="true">
<path
id="path7381"
d="M -2.5,-1.0 C -2.5,1.7600000 -4.7400000,4.0 -7.5,4.0 C -10.260000,4.0 -12.5,1.7600000 -12.5,-1.0 C -12.5,-3.7600000 -10.260000,-6.0 -7.5,-6.0 C -4.7400000,-6.0 -2.5,-3.7600000 -2.5,-1.0 z "
style="fill-rule:evenodd;stroke:#ff0000;stroke-width:1pt;stroke-opacity:1;fill:#ff0000;fill-opacity:1"
transform="scale(0.8) translate(7.4, 1)" />
</marker>
<marker
inkscape:stockid="DotL"
orient="auto"
refY="0.0"
refX="0.0"
id="marker4973"
style="overflow:visible"
inkscape:isstock="true">
<path
id="path4975"
d="M -2.5,-1.0 C -2.5,1.7600000 -4.7400000,4.0 -7.5,4.0 C -10.260000,4.0 -12.5,1.7600000 -12.5,-1.0 C -12.5,-3.7600000 -10.260000,-6.0 -7.5,-6.0 C -4.7400000,-6.0 -2.5,-3.7600000 -2.5,-1.0 z "
style="fill-rule:evenodd;stroke:#ff0000;stroke-width:1pt;stroke-opacity:1;fill:#ff0000;fill-opacity:1"
transform="scale(0.8) translate(7.4, 1)" />
</marker>
<marker
inkscape:isstock="true"
style="overflow:visible"
id="marker4831"
refX="0.0"
refY="0.0"
orient="auto"
inkscape:stockid="DotL"
inkscape:collect="always">
<path
transform="scale(0.8) translate(7.4, 1)"
style="fill-rule:evenodd;stroke:#ff0000;stroke-width:1pt;stroke-opacity:1;fill:#ff0000;fill-opacity:1"
d="M -2.5,-1.0 C -2.5,1.7600000 -4.7400000,4.0 -7.5,4.0 C -10.260000,4.0 -12.5,1.7600000 -12.5,-1.0 C -12.5,-3.7600000 -10.260000,-6.0 -7.5,-6.0 C -4.7400000,-6.0 -2.5,-3.7600000 -2.5,-1.0 z "
id="path4833" />
</marker>
<marker
inkscape:isstock="true"
style="overflow:visible"
id="marker4713"
refX="0.0"
refY="0.0"
orient="auto"
inkscape:stockid="DotL">
<path
transform="scale(0.8) translate(7.4, 1)"
style="fill-rule:evenodd;stroke:#ff0000;stroke-width:1pt;stroke-opacity:1;fill:#ff0000;fill-opacity:1"
d="M -2.5,-1.0 C -2.5,1.7600000 -4.7400000,4.0 -7.5,4.0 C -10.260000,4.0 -12.5,1.7600000 -12.5,-1.0 C -12.5,-3.7600000 -10.260000,-6.0 -7.5,-6.0 C -4.7400000,-6.0 -2.5,-3.7600000 -2.5,-1.0 z "
id="path4715" />
</marker>
<marker
inkscape:isstock="true"
style="overflow:visible"
id="marker4491"
refX="0.0"
refY="0.0"
orient="auto"
inkscape:stockid="DotL"
inkscape:collect="always">
<path
transform="scale(0.8) translate(7.4, 1)"
style="fill-rule:evenodd;stroke:#ff0000;stroke-width:1pt;stroke-opacity:1;fill:#ff0000;fill-opacity:1"
d="M -2.5,-1.0 C -2.5,1.7600000 -4.7400000,4.0 -7.5,4.0 C -10.260000,4.0 -12.5,1.7600000 -12.5,-1.0 C -12.5,-3.7600000 -10.260000,-6.0 -7.5,-6.0 C -4.7400000,-6.0 -2.5,-3.7600000 -2.5,-1.0 z "
id="path4493" />
</marker>
<marker
inkscape:stockid="DotL"
orient="auto"
refY="0.0"
refX="0.0"
id="DotL"
style="overflow:visible"
inkscape:isstock="true"
inkscape:collect="always">
<path
id="path4264"
d="M -2.5,-1.0 C -2.5,1.7600000 -4.7400000,4.0 -7.5,4.0 C -10.260000,4.0 -12.5,1.7600000 -12.5,-1.0 C -12.5,-3.7600000 -10.260000,-6.0 -7.5,-6.0 C -4.7400000,-6.0 -2.5,-3.7600000 -2.5,-1.0 z "
style="fill-rule:evenodd;stroke:#ff0000;stroke-width:1pt;stroke-opacity:1;fill:#ff0000;fill-opacity:1"
transform="scale(0.8) translate(7.4, 1)" />
</marker>
<marker
inkscape:isstock="true"
style="overflow:visible"
id="marker4491-1"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="DotL">
<path
inkscape:connector-curvature="0"
transform="matrix(0.8,0,0,0.8,5.92,0.8)"
style="fill:#ff0000;fill-opacity:1;fill-rule:evenodd;stroke:#ff0000;stroke-width:1pt;stroke-opacity:1"
d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z"
id="path4493-0" />
</marker>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="7.919596"
inkscape:cx="223.77053"
inkscape:cy="981.95816"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="1920"
inkscape:window-height="1153"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
showguides="true"
inkscape:guide-bbox="true" />
<metadata
id="metadata7">
<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 />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<rect
style="opacity:1;fill:#ececec;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect3342"
width="286.88333"
height="374.26151"
x="85.862961"
y="44.735039"
inkscape:export-filename="/home/eden/text8697.png"
inkscape:export-xdpi="692.66998"
inkscape:export-ydpi="692.66998" />
<rect
style="opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:2.14391756;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect4144"
width="49.858635"
height="365.53128"
x="88.965385"
y="48.342533"
inkscape:export-filename="/home/eden/text8697.png"
inkscape:export-xdpi="692.66998"
inkscape:export-ydpi="692.66998" />
<rect
style="opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1.75549805;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect4144-3"
width="50.247055"
height="243.18617"
x="318.32834"
y="48.148323"
inkscape:export-filename="/home/eden/text8697.png"
inkscape:export-xdpi="692.66998"
inkscape:export-ydpi="692.66998" />
<rect
y="143.14488"
x="-98.535614"
height="170.73566"
width="50.527519"
id="rect4161"
style="opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1.47503293;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
transform="matrix(0,-1,1,0,0,0)"
inkscape:export-filename="/home/eden/text8697.png"
inkscape:export-xdpi="692.66998"
inkscape:export-ydpi="692.66998" />
<rect
style="opacity:1;fill:#5599ff;fill-opacity:1;fill-rule:nonzero;stroke:#5599ff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect4182"
width="17.67767"
height="14.647212"
x="220.21326"
y="61.301033"
inkscape:export-filename="/home/eden/text8697.png"
inkscape:export-xdpi="692.66998"
inkscape:export-ydpi="692.66998" />
<path
id="path4186"
style="fill:none;fill-rule:evenodd;stroke:#ff0000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 362.62188,227.69892 0,-172.862354 -122.73353,0 M 93.944187,405.31489 l 0,-350.478324 147.518163,0"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccc"
inkscape:export-filename="/home/eden/text8697.png"
inkscape:export-xdpi="692.66998"
inkscape:export-ydpi="692.66998" />
<path
style="fill:none;fill-rule:evenodd;stroke:#ff0000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 229.10714,55.040774 0,4.642857 0,0.714286"
id="path4189"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccc"
inkscape:export-filename="/home/eden/text8697.png"
inkscape:export-xdpi="692.66998"
inkscape:export-ydpi="692.66998" />
<path
style="fill:none;fill-rule:evenodd;stroke:#ff0000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#DotL)"
d="m 94.070456,405.09553 26.011424,0 0.12627,0"
id="path4191"
inkscape:connector-curvature="0"
inkscape:export-filename="/home/eden/text8697.png"
inkscape:export-xdpi="692.66998"
inkscape:export-ydpi="692.66998" />
<path
style="fill:none;fill-rule:evenodd;stroke:#ff0000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#marker4491)"
d="m 93.944187,390.13043 9.722723,0"
id="path4193"
inkscape:connector-curvature="0"
inkscape:export-filename="/home/eden/text8697.png"
inkscape:export-xdpi="692.66998"
inkscape:export-ydpi="692.66998" />
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="125.38519"
y="408.25226"
id="text4195"
sodipodi:linespacing="125%"
inkscape:export-filename="/home/eden/text8697.png"
inkscape:export-xdpi="692.66998"
inkscape:export-ydpi="692.66998"><tspan
sodipodi:role="line"
id="tspan4197"
x="125.38519"
y="408.25226"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans, Normal';text-align:start;writing-mode:lr-tb;text-anchor:start">T1</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="110.40583"
y="393.52734"
id="text4195-0"
sodipodi:linespacing="125%"
inkscape:export-filename="/home/eden/text8697.png"
inkscape:export-xdpi="692.66998"
inkscape:export-ydpi="692.66998"><tspan
sodipodi:role="line"
id="tspan4197-4"
x="110.40583"
y="393.52734"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans, Normal';text-align:start;writing-mode:lr-tb;text-anchor:start">T2</tspan></text>
<path
inkscape:connector-curvature="0"
id="path4819"
d="m 94.070456,227.09553 26.011424,0 0.12627,0"
style="fill:none;fill-rule:evenodd;stroke:#ff0000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#marker4831)"
inkscape:export-filename="/home/eden/text8697.png"
inkscape:export-xdpi="692.66998"
inkscape:export-ydpi="692.66998" />
<path
inkscape:connector-curvature="0"
id="path4821"
d="m 93.944187,212.13043 9.722723,0"
style="fill:none;fill-rule:evenodd;stroke:#ff0000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#marker4491)"
inkscape:export-filename="/home/eden/text8697.png"
inkscape:export-xdpi="692.66998"
inkscape:export-ydpi="692.66998" />
<text
sodipodi:linespacing="125%"
id="text4823"
y="230.25226"
x="125.38519"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"
inkscape:export-filename="/home/eden/text8697.png"
inkscape:export-xdpi="692.66998"
inkscape:export-ydpi="692.66998"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans, Normal';text-align:start;writing-mode:lr-tb;text-anchor:start"
y="230.25226"
x="125.38519"
id="tspan4825"
sodipodi:role="line">T3</tspan></text>
<text
sodipodi:linespacing="125%"
id="text4827"
y="215.52734"
x="110.40583"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"
inkscape:export-filename="/home/eden/text8697.png"
inkscape:export-xdpi="692.66998"
inkscape:export-ydpi="692.66998"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans, Normal';text-align:start;writing-mode:lr-tb;text-anchor:start"
y="215.52734"
x="110.40583"
id="tspan4829"
sodipodi:role="line">T4</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="234.85602"
y="92.336365"
id="text4969"
sodipodi:linespacing="125%"
inkscape:export-filename="/home/eden/text8697.png"
inkscape:export-xdpi="692.66998"
inkscape:export-ydpi="692.66998"><tspan
sodipodi:role="line"
id="tspan4971"
x="234.85602"
y="92.336365"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans, Normal';text-align:start;writing-mode:lr-tb;text-anchor:start">T6/H1</tspan></text>
<path
style="fill:none;fill-rule:evenodd;stroke:#ff0000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#marker4973)"
d="m 228.79955,77.059921 0,11.869293 0,0"
id="path7353"
inkscape:connector-curvature="0"
inkscape:export-filename="/home/eden/text8697.png"
inkscape:export-xdpi="692.66998"
inkscape:export-ydpi="692.66998" />
<path
style="fill:none;fill-rule:evenodd;stroke:#ff0000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#marker7379)"
d="m 244.36834,54.975299 0,9.72272"
id="path7373"
inkscape:connector-curvature="0"
inkscape:export-filename="/home/eden/text8697.png"
inkscape:export-xdpi="692.66998"
inkscape:export-ydpi="692.66998" />
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="251.73831"
y="68.72657"
id="text7375"
sodipodi:linespacing="125%"
inkscape:export-filename="/home/eden/text8697.png"
inkscape:export-xdpi="692.66998"
inkscape:export-ydpi="692.66998"><tspan
sodipodi:role="line"
id="tspan7377"
x="251.73831"
y="68.72657"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans, Normal';text-align:start;writing-mode:lr-tb;text-anchor:start">T5</tspan></text>
<path
style="fill:none;fill-rule:evenodd;stroke:#ff0000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#marker8085)"
d="m 362.50188,227.37101 -26.01142,0 -0.12627,0"
id="path8073"
inkscape:connector-curvature="0"
inkscape:export-filename="/home/eden/text8697.png"
inkscape:export-xdpi="692.66998"
inkscape:export-ydpi="692.66998" />
<path
style="fill:none;fill-rule:evenodd;stroke:#ff0000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#marker8263)"
d="m 362.62815,212.40591 -9.72272,0"
id="path8075"
inkscape:connector-curvature="0"
inkscape:export-filename="/home/eden/text8697.png"
inkscape:export-xdpi="692.66998"
inkscape:export-ydpi="692.66998" />
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="319.6709"
y="230.72415"
id="text8077"
sodipodi:linespacing="125%"
inkscape:export-filename="/home/eden/text8697.png"
inkscape:export-xdpi="692.66998"
inkscape:export-ydpi="692.66998"><tspan
sodipodi:role="line"
id="tspan8079"
x="319.6709"
y="230.72415"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans, Normal';text-align:start;writing-mode:lr-tb;text-anchor:start">T8</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="335.04868"
y="215.99924"
id="text8081"
sodipodi:linespacing="125%"
inkscape:export-filename="/home/eden/text8697.png"
inkscape:export-xdpi="692.66998"
inkscape:export-ydpi="692.66998"><tspan
sodipodi:role="line"
id="tspan8083"
x="335.04868"
y="215.99924"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans, Normal';text-align:start;writing-mode:lr-tb;text-anchor:start">T7</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10.40449429px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;"
x="218.98798"
y="72.412018"
id="text8693"
sodipodi:linespacing="125%"
inkscape:export-filename="/home/eden/text8697.png"
inkscape:export-xdpi="692.66998"
inkscape:export-ydpi="692.66998"><tspan
sodipodi:role="line"
id="tspan8695"
x="218.98798"
y="72.412018">ESP</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="176.94135"
y="26.552294"
id="text8697"
sodipodi:linespacing="125%"
inkscape:export-filename="/home/eden/text8697.png"
inkscape:export-xdpi="692.66998"
inkscape:export-ydpi="692.66998"><tspan
sodipodi:role="line"
id="tspan8699"
x="176.94135"
y="26.552294">Soba 1 - Cvrčci</tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 23 KiB

39
include/readme.txt Normal file
View File

@ -0,0 +1,39 @@
This directory is intended for project header files.
A header file is a file containing C declarations and macro definitions
to be shared between several project source files. You request the use of a
header file in your project source file (C, C++, etc) located in `src` folder
by including it, with the C preprocessing directive `#include'.
```src/main.c
#include "header.h"
int main (void)
{
...
}
```
Including a header file produces the same results as copying the header file
into each source file that needs it. Such copying would be time-consuming
and error-prone. With a header file, the related declarations appear
in only one place. If they need to be changed, they can be changed in one
place, and programs that include the header file will automatically use the
new version when next recompiled. The header file eliminates the labor of
finding and changing all the copies as well as the risk that a failure to
find one copy will result in inconsistencies within a program.
In C, the usual convention is to give header files names that end with `.h'.
It is most portable to use only letters, digits, dashes, and underscores in
header file names, and at most one dot.
Read more about using header files in official GCC documentation:
* Include Syntax
* Include Operation
* Once-Only Headers
* Computed Includes
https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html

36
lib/readme.txt Normal file
View File

@ -0,0 +1,36 @@
This directory is intended for the project specific (private) libraries.
PlatformIO will compile them to static libraries and link to executable file.
The source code of each library should be placed in separate directory, like
"lib/private_lib/[here are source files]".
For example, see how can be organized `Foo` and `Bar` libraries:
|--lib
| |--Bar
| | |--docs
| | |--examples
| | |--src
| | |- Bar.c
| | |- Bar.h
| |--Foo
| | |- Foo.c
| | |- Foo.h
| |- readme.txt --> THIS FILE
|- platformio.ini
|--src
|- main.c
Then in `src/main.c` you should use:
#include <Foo.h>
#include <Bar.h>
// rest H/C/CPP code
PlatformIO will find your libraries automatically, configure preprocessor's
include paths and build them.
More information about PlatformIO Library Dependency Finder
- http://docs.platformio.org/page/librarymanager/ldf.html

18
platformio.ini Normal file
View File

@ -0,0 +1,18 @@
; PlatformIO Project Configuration File
;
; Build options: build flags, source filter
; Upload options: custom upload port, speed and extra flags
; Library options: dependencies, extra library storages
; Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; http://docs.platformio.org/page/projectconf.html
[env:espduino]
platform = espressif8266
board = espduino
;board = nodemcuv2
framework = arduino
upload_speed = 230400
;upload_speed = 460800
upload_port = /dev/ttyUSB0

150
src/comm.cpp Normal file
View File

@ -0,0 +1,150 @@
#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#include "defs.h"
#include "comm.h"
#include "hw.h"
#include "measure.h"
#include "disp.h"
uint8_t last_server_comm_status = STATUS_UNKNOWN;
//---------------------------------------------------------------------------------------
void init_comm()
{
WiFi.mode(WIFI_STA);
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
}
//---------------------------------------------------------------------------------------
bool comm_is_connected() {
return WiFi.status() == WL_CONNECTED;
}
//---------------------------------------------------------------------------------------
void connect_comm()
{
uint8_t retry = 0;
while (!comm_is_connected() && retry <= 3)
{
disp_indicate_status(STATUS_CONNECTING_TO_WIFI, true);
SERIAL_PRINTLN("Connecting to WiFi...");
blink_led(LED_BLUE, 500);
disp_indicate_status(STATUS_CONNECTING_TO_WIFI, false);
delay(1000);
retry++;
}
if (!comm_is_connected())
last_server_comm_status = STATUS_ERR;
}
//---------------------------------------------------------------------------------------
unsigned long get_uptime()
{
static unsigned int _rollover_cnt = 0;
static unsigned long _last_millis = 0;
unsigned long current_millis = millis();
if (current_millis < _last_millis)
_rollover_cnt++;
_last_millis = current_millis;
return (0xFFFFFFFF / 1000) * _rollover_cnt + (_last_millis / 1000);
}
//---------------------------------------------------------------------------------------
void post_data_to_server()
{
int http_response_code;
HTTPClient http;
String post_data;
String measurement_data;
char buff[100];
unsigned long uptime = get_uptime();
set_led(LED_GREEN, true);
disp_indicate_status(STATUS_SENDING_DATA, true);
http.begin(API_URL);
measurement_data = format_measurement_data();
sprintf(
buff,
"cmd=set_sensors&api_key=%s&uptime=%ld&",
API_KEY,
uptime
);
post_data = String(buff) + measurement_data;
// content type must be included with POST method
http.addHeader("Content-Type", "application/x-www-form-urlencoded");
SERIAL_PRINT("[HTTP POST] to ");
SERIAL_PRINTLN(API_URL);
SERIAL_PRINT("[HTTP POST] data: ");
SERIAL_PRINTLN(post_data);
http_response_code = http.POST(post_data);
// http_response_code will be negative on error
if (http_response_code > 0)
{
// HTTP header has been sent and server response header has been handled
SERIAL_PRINTF("[HTTP POST] response code: %d\n", http_response_code);
if (http_response_code == HTTP_CODE_OK)
{
last_server_comm_status = STATUS_OK;
String payload = http.getString();
SERIAL_PRINT("[HTTP response] ");
SERIAL_PRINTLN(payload);
} else {
last_server_comm_status = STATUS_ERR;
}
} else {
last_server_comm_status = STATUS_ERR;
SERIAL_PRINTF(
"[HTTP] Failed, response code: %d, error: %s\n",
http_response_code,
http.errorToString(http_response_code).c_str()
);
blink_led(LED_RED, 500);
}
http.end();
set_led(LED_GREEN, false);
disp_indicate_status(STATUS_SENDING_DATA, false);
}
//---------------------------------------------------------------------------------------
uint8_t get_last_server_comm_status() {
return last_server_comm_status;
}
//---------------------------------------------------------------------------------------

28
src/comm.h Normal file
View File

@ -0,0 +1,28 @@
#ifndef COMM_H
#define COMM_H
#include "defs.h"
#ifdef LOCAL_SERVER
#define API_URL "http://192.168.1.5:8000/api/"
#define WIFI_SSID ""
#define WIFI_PASSWORD ""
#define API_KEY ""
#else
#define API_URL ""
#define WIFI_SSID ""
#define WIFI_PASSWORD ""
#define API_KEY ""
#endif
//---------------------------------------------------------------------------------------
void init_comm();
void post_data_to_server();
void connect_comm();
bool comm_is_connected();
uint8_t get_last_server_comm_status();
#endif

35
src/defs.h Normal file
View File

@ -0,0 +1,35 @@
#ifndef DEFS_H
#define DEFS_H
#define LOCAL_SERVER
#define SERIAL_DEBUG
#ifdef SERIAL_DEBUG
#define SERIAL_PRINT(x) Serial.print(x)
#define SERIAL_PRINTDEC(x) Serial.print(x, DEC)
#define SERIAL_PRINTLN(x) Serial.println(x)
#define SERIAL_PRINTF(fmt, ...) Serial.printf(fmt, ##__VA_ARGS__)
#else
#define SERIAL_PRINT(x)
#define SERIAL_PRINTDEC(x)
#define SERIAL_PRINTLN(x)
#define SERIAL_PRINTF(fmt, ...)
#endif
#define SIGNAL_LED_BLINK_INTERVAL 2000 // ms
#define DS_MEASURE_INTERVAL 1000 // ms
#define DHT_MEASURE_INTERVAL 2000 // ms
#define SERIAL_BAUD_RATE 115200
#ifdef LOCAL_SERVER
#define SEND_DATA_TO_SERVER_INTERVAL 5000 // ms
#else
#define SEND_DATA_TO_SERVER_INTERVAL 20000 // ms
#endif
#define STATUS_UNKNOWN 0
#define STATUS_OK 1
#define STATUS_ERR 2
#endif

98
src/dht_measure.cpp Normal file
View File

@ -0,0 +1,98 @@
#include "defs.h"
#include "measure.h"
#include "dht_measure.h"
#include "disp.h"
DHTSensorData dht_sensors[DHT_SENSOR_COUNT];
uint8_t current_sensor = 0;
DHT dht_sensor_0(DHT_SENSOR_PINS[0], DHTTYPE);
// DHT dht_sensor_1(DHT_SENSOR_PINS[1], DHTTYPE);
//---------------------------------------------------------------------------------------
void dht_init_measure()
{
dht_sensors[0].sensor = &dht_sensor_0;
// dht_sensors[1].sensor = &dht_sensor_1;
for (uint8_t i = 0; i < DHT_SENSOR_COUNT; i++)
{
dht_sensors[i].sensor->begin();
SERIAL_PRINTF("Setting up sensor %d on pin %d.\n", i, DHT_SENSOR_PINS[i]);
}
}
//---------------------------------------------------------------------------------------
float dht_read_sensor(uint8_t index)
{
DHTSensorData* sd = &dht_sensors[index];
float humidity = sd->sensor->readHumidity();
float temperature = sd->sensor->readTemperature();
disp_indicate_status(STATUS_SENSOR_READ, false);
if (!isnan(humidity) && !isnan(temperature))
{
assign_sensor_value(&sd->humidity, humidity);
assign_sensor_value(&sd->temperature, temperature);
return true;
} else {
sd->humidity.valid = false;
sd->temperature.valid = false;
return false;
}
}
//---------------------------------------------------------------------------------------
void dht_read_next_sensor()
{
if (DHT_SENSOR_COUNT == 0)
return;
if (current_sensor >= DHT_SENSOR_COUNT)
current_sensor = 0;
DHTSensorData* sensor_data = &dht_sensors[current_sensor];
set_led(LED_YELLOW, true);
disp_indicate_status(STATUS_SENSOR_READ, true);
if (dht_read_sensor(current_sensor))
{
SERIAL_PRINTF(
"Reading DHT sensor %d: Temperature: %0.2f, humidity: %0.2f\n",
current_sensor,
sensor_data->temperature.value,
sensor_data->humidity.value
);
} else {
SERIAL_PRINTF("Error reading DHT sensor %d.\n", current_sensor);
};
set_led(LED_YELLOW, false);
disp_indicate_status(STATUS_SENSOR_READ, false);
current_sensor++;
}
//---------------------------------------------------------------------------------------
pDHTSensorData dht_get_sensors_data()
{
return (pDHTSensorData)&dht_sensors;
}
//---------------------------------------------------------------------------------------

20
src/dht_measure.h Normal file
View File

@ -0,0 +1,20 @@
#ifndef DHT_MEASURE_H
#define DHT_MEASURE_H
#include "hw.h"
#include "measure.h"
#define DHT_SENSOR_COUNT 1
#define DHTTYPE DHT22
static uint8_t DHT_SENSOR_PINS[] = {D12, D13};
//---------------------------------------------------------------------------------------
void dht_init_measure();
void dht_read_next_sensor();
pDHTSensorData dht_get_sensors_data();
#endif

206
src/disp.cpp Normal file
View File

@ -0,0 +1,206 @@
#include <Arduino.h>
#include <U8g2lib.h>
#include "disp.h"
#include "defs.h"
#include "measure.h"
#include "dht_measure.h"
#include "comm.h"
U8G2_SSD1306_128X64_NONAME_F_HW_I2C display(U8G2_R0);
#define GLYPH_OK 0x25a0
#define GLYPH_ERR 0x2612
#define GLYPH_UNKNOWN 0x2610
#define GLYPH_STATUS_ACTIVE 0x25CF
#define STATUS_Y_OFFS 36
#define STATUS_X_OFFS_GLYPH 34
#define STATUS_Y_OFFS_GLYPH STATUS_Y_OFFS - 2
#define STATUS_X_OFFS_STATUS_GLYPH 24
#define STATUS_Y_OFFS_STATUS_GLYPH STATUS_Y_OFFS_GLYPH + 2
//---------------------------------------------------------------------------------------
void init_disp() {
display.begin();
display.setFontPosTop();
}
//---------------------------------------------------------------------------------------
void display_sensor_value(const char* caption, SensorData* sd) {
if (sd != NULL && sd->valid) {
display.printf("%s %0.1f", caption, sd->value);
} else {
display.printf("%s ERR", caption);
}
}
//---------------------------------------------------------------------------------------
void display_ds_sensor_value(const char* caption, const char* sensor_address) {
display_sensor_value(caption, get_ds_sensor_data(sensor_address));
}
//---------------------------------------------------------------------------------------
uint16_t get_status_glyph(uint8_t status) {
switch (status) {
case STATUS_OK:
return GLYPH_OK;
case STATUS_ERR:
return GLYPH_ERR;
default:
return GLYPH_UNKNOWN;
}
}
//---------------------------------------------------------------------------------------
void show_display_page(uint8_t index) {
pDHTSensorData dht_sensors_data;
char caption[8];
uint8_t values_x_offs = 50;
uint8_t lh;
display.clearBuffer();
display.setFontMode(1); // activate transparent font mode
display.setFont(u8g2_font_t0_22_mf);
lh = 16;
switch (index) {
case 0:
strcpy(caption, "P1");
display.setCursor(values_x_offs, 0 * lh);
display_ds_sensor_value("T1", T1_ADDRESS);
display.setCursor(values_x_offs, 1 * lh);
display_ds_sensor_value("T2", T2_ADDRESS);
display.setCursor(values_x_offs, 2 * lh);
display_ds_sensor_value("T3", T3_ADDRESS);
display.setCursor(values_x_offs, 3 * lh);
display_ds_sensor_value("T4", T4_ADDRESS);
break;
case 1:
dht_sensors_data = dht_get_sensors_data();
strcpy(caption, "P2");
display.setCursor(values_x_offs, 0 * lh);
display_ds_sensor_value("T5", T5_ADDRESS);
display.setCursor(values_x_offs, 1 * lh);
display_sensor_value("T6", &dht_sensors_data->temperature);
display.setCursor(values_x_offs, 2 * lh);
display_sensor_value("H1", &dht_sensors_data->humidity);
break;
case 2:
strcpy(caption, "P3");
display.setCursor(values_x_offs, 0 * lh);
display_ds_sensor_value("T7", T7_ADDRESS);
display.setCursor(values_x_offs, 1 * lh);
display_ds_sensor_value("T8", T8_ADDRESS);
break;
}
// print page caption
display.setFont(u8g2_font_profont29_mf);
display.drawStr(0, 0, caption);
// print status captions
display.setFont(u8g2_font_victoriamedium8_8u);
lh = 8;
display.setCursor(0, STATUS_Y_OFFS + lh * 0);
display.print("NET");
display.setCursor(0, STATUS_Y_OFFS + lh * 1);
display.print("COM");
display.setCursor(0, STATUS_Y_OFFS + lh * 2);
display.print("SNS");
display.setFont(u8g2_font_unifont_t_symbols);
// wifi connection status
display.drawGlyph(
STATUS_X_OFFS_GLYPH,
STATUS_Y_OFFS_GLYPH + lh * 0,
get_status_glyph(comm_is_connected())
);
// server communication status
display.drawGlyph(
STATUS_X_OFFS_GLYPH,
STATUS_Y_OFFS_GLYPH + lh * 1,
get_status_glyph(get_last_server_comm_status())
);
display.sendBuffer();
}
//---------------------------------------------------------------------------------------
void disp_indicate_status(uint8_t status, bool value) {
uint8_t line;
uint8_t lh = 8;
display.setFont(u8g2_font_unifont_t_symbols);
display.setFontMode(1); // activate transparent font mode
switch (status) {
case STATUS_CONNECTING_TO_WIFI:
line = 0;
break;
case STATUS_SENDING_DATA:
line = 1;
break;
case STATUS_SENSOR_READ:
line = 2;
break;
default:
return;
}
if (value)
display.setDrawColor(1);
else
display.setDrawColor(0);
display.drawGlyph(
STATUS_X_OFFS_STATUS_GLYPH,
STATUS_Y_OFFS_STATUS_GLYPH + lh * line,
GLYPH_STATUS_ACTIVE
);
display.sendBuffer();
display.setDrawColor(1);
}
//---------------------------------------------------------------------------------------
void handle_disp() {
unsigned long current_millis = millis();
static unsigned long timestamp_display_page_switch = 0;
static uint8_t display_page_index = 0;
if (current_millis - timestamp_display_page_switch > DISPLAY_PAGE_SWITCH_INTERVAL) {
show_display_page(display_page_index);
display_page_index++;
if (display_page_index >= DISPLAY_PAGE_COUNT)
display_page_index = 0;
timestamp_display_page_switch = current_millis;
}
}
//---------------------------------------------------------------------------------------

22
src/disp.h Normal file
View File

@ -0,0 +1,22 @@
#ifndef DISP_H
#define DISP_H
#include <Arduino.h>
#define DISPLAY_PAGE_SWITCH_INTERVAL 5000
#define DISPLAY_PAGE_COUNT 3
#define STATUS_CONNECTING_TO_WIFI 0
#define STATUS_SENSOR_READ 1
#define STATUS_SENDING_DATA 2
//---------------------------------------------------------------------------------------
void init_disp();
void handle_disp();
void disp_indicate_status(uint8_t status, bool value);
#endif

42
src/hw.cpp Normal file
View File

@ -0,0 +1,42 @@
#include <Arduino.h>
#include "hw.h"
//---------------------------------------------------------------------------------------
void init_hw()
{
pinMode(LED_BLUE, OUTPUT);
pinMode(LED_RED, OUTPUT);
pinMode(LED_YELLOW, OUTPUT);
pinMode(LED_GREEN, OUTPUT);
}
//---------------------------------------------------------------------------------------
void set_led(int led, bool value)
{
bool invert = led == LED_BLUE;
if (!invert)
digitalWrite(led, value ? HIGH : LOW);
else
digitalWrite(led, value ? LOW : HIGH);
}
//---------------------------------------------------------------------------------------
void blink_led(int led, int duration)
{
set_led(led, true);
delay(duration);
set_led(led, false);
}
//---------------------------------------------------------------------------------------

35
src/hw.h Normal file
View File

@ -0,0 +1,35 @@
#ifndef HW_H
#define HW_H
#define D0 0
#define D1 1
#define D2 2
#define D3 3
#define D4 4
#define D5 5
#define D6 6
#define D7 7
#define D8 8
#define D9 9
#define D10 10
#define D11 11
#define D12 12
#define D13 13
#define D14 14
#define D15 15
#define LED_RED D3
#define LED_YELLOW D4
#define LED_GREEN D5
#define LED_BLUE LED_BUILTIN
//---------------------------------------------------------------------------------------
void init_hw();
void set_led(int led, bool value);
void blink_led(int led, int duration);
#endif

286
src/measure.cpp Normal file
View File

@ -0,0 +1,286 @@
#include <OneWire.h>
#include <DallasTemperature.h>
#include "defs.h"
#include "measure.h"
#include "dht_measure.h"
#include "comm.h"
#include "disp.h"
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature ds_sensors(&oneWire);
DallasSensorData sensor_data[MAX_SENSORS_COUNT];
int ds_sensor_count = 0;
int ds_current_sensor = 0;
//---------------------------------------------------------------------------------------
void assign_sensor_value(SensorData* sd, float value)
{
// calculate average
sd->cumulative_value += value;
sd->count++;
sd->value = sd->cumulative_value / sd->count;
sd->timestamp = millis();
sd->sent = false;
sd->valid = true;
}
//---------------------------------------------------------------------------------------
void sensor_value_sent(SensorData* sd)
{
sd->cumulative_value = 0;
sd->count = 0;
sd->sent = true;
}
//---------------------------------------------------------------------------------------
void ds_address_to_str(DeviceAddress address, char *out_buff)
{
char *p = out_buff;
for (uint8_t i = 0; i < 8; i++, p += 2)
sprintf(p, "%02x", address[i]);
}
//---------------------------------------------------------------------------------------
void str_to_ds_address(char* str, DeviceAddress* address)
{
char* p_str = str;
uint8_t* p_out = address[0];
char buff[3];
memset(buff, 0, sizeof(buff));
for (int i = 0; i < 8; i++) {
buff[0] = *p_str++;
buff[1] = *p_str++;
*p_out++ = (uint8_t)strtol(buff, NULL, 16);
}
}
//---------------------------------------------------------------------------------------
float ds_get_sensor_value(DeviceAddress deviceAddress)
{
return ds_sensors.getTempC(deviceAddress);
}
//---------------------------------------------------------------------------------------
void ds_discover_sensors()
{
int i;
char buff[100];
DallasSensorData* sd;
// locate devices on the bus
SERIAL_PRINT("Discovering sensors...");
ds_sensor_count = ds_sensors.getDeviceCount();
SERIAL_PRINT(ds_sensor_count);
SERIAL_PRINTLN(" sensors.");
// report parasite power requirements
SERIAL_PRINT("Parasite power: ");
SERIAL_PRINTLN(ds_sensors.isParasitePowerMode() ? "ON" : "OFF");
for (i = 0; i < ds_sensor_count; i++)
{
sd = &sensor_data[i];
ds_sensors.getAddress(sd->address, i);
ds_sensors.setResolution(sd->address, TEMPERATURE_PRECISION);
ds_address_to_str(sd->address, buff);
SERIAL_PRINTF("Sensor %d, address: %s\n", i, buff);
delay(1000);
}
}
//---------------------------------------------------------------------------------------
void init_measure()
{
ds_sensors.begin();
delay(500);
ds_discover_sensors();
delay(500);
dht_init_measure();
delay(500);
}
//---------------------------------------------------------------------------------------
bool ds_is_value_valid(float value)
{
// value == -127 -> sensor disconnected
// value == 85 -> sensor comm error, probably bad wiring
return !(value == -127 || value == 85);
}
//---------------------------------------------------------------------------------------
void measure_next_sensor()
{
float value;
char buff[100];
DallasSensorData* sd;
if (ds_sensor_count == 0)
return;
set_led(LED_YELLOW, true);
disp_indicate_status(STATUS_SENSOR_READ, true);
if (ds_current_sensor >= ds_sensor_count)
{
// request sensors to measure temperatures only once
ds_sensors.requestTemperatures();
ds_current_sensor = 0;
}
sd = &sensor_data[ds_current_sensor];
ds_address_to_str(sd->address, buff);
SERIAL_PRINTF("Reading sensor %d [address %s]... ", ds_current_sensor, buff);
value = ds_get_sensor_value(sd->address);
SERIAL_PRINTF("Value: %0.2f\r\n", value);
if (ds_is_value_valid(value))
{
assign_sensor_value(&sd->data, value);
} else {
sd->data.valid = false;
blink_led(LED_RED, 200);
}
ds_current_sensor++;
set_led(LED_YELLOW, false);
disp_indicate_status(STATUS_SENSOR_READ, false);
}
//---------------------------------------------------------------------------------------
String format_measurement_data()
{
String res;
char address[MAX_ADDRESS_STR_LENGTH];
char buff[200];
int i;
DallasSensorData *sd;
int cnt = 0;
for (i = 0; i < ds_sensor_count; i++)
{
sd = &sensor_data[i];
if (sd->data.valid && !sd->data.sent)
{
ds_address_to_str(sd->address, address);
sprintf(
buff,
"s%daddr=%s&s%dval=%0.2f&",
cnt,
address,
cnt,
sd->data.value
);
// reset average calc
sensor_value_sent(&sd->data);
res += buff;
cnt++;
}
}
pDHTSensorData dht_data = dht_get_sensors_data();
for (i = 0; i < DHT_SENSOR_COUNT; i++)
{
// format temperature
if (dht_data->temperature.valid) {
sprintf(
buff,
"s%daddr=dht%dtemp&s%dval=%0.2f&",
cnt,
i,
cnt,
dht_data->temperature.value
);
// reset average calc
sensor_value_sent(&dht_data->temperature);
res += buff;
cnt++;
}
// format humidity
if (dht_data->humidity.valid) {
sprintf(
buff,
"s%daddr=dht%dhumidity&s%dval=%0.2f&",
cnt,
i,
cnt,
dht_data->humidity.value
);
// reset average calc
sensor_value_sent(&dht_data->humidity);
res += buff;
cnt++;
}
dht_data++;
}
sprintf(buff, "sensor_count=%d", cnt);
res += buff;
return res;
}
//---------------------------------------------------------------------------------------
pSensorData get_ds_sensor_data(const char* sensor_address) {
DeviceAddress address;
DallasSensorData *sd;
str_to_ds_address((char*)sensor_address, &address);
for (uint8_t i = 0; i < ds_sensor_count; i++) {
sd = &sensor_data[i];
if (memcmp(address, sd->address, sizeof(DeviceAddress)) == 0) {
return &sd->data;
}
}
return NULL;
}
//---------------------------------------------------------------------------------------

63
src/measure.h Normal file
View File

@ -0,0 +1,63 @@
#ifndef MEASURE_H
#define MEASURE_H
#include "hw.h"
#include <DallasTemperature.h>
#include <Adafruit_Sensor.h>
#include <DHT.h>
#define ONE_WIRE_BUS D13
#define TEMPERATURE_PRECISION 11 // can be 9, 10, 11 or 12 bits
#define MAX_SENSORS_COUNT 10
#define MEASUREMENT_TIMEOUT 60000 // ms
#define MAX_ADDRESS_STR_LENGTH 20
#define INVALID_SENSOR_VALUE -127
// Sensor addresses
#define T1_ADDRESS "28ffbb19841704c3"
#define T2_ADDRESS "28ff112ca01705d0"
#define T3_ADDRESS "28ffde61a01705e0"
#define T4_ADDRESS "28ff3c09a017046b"
#define T5_ADDRESS "28ff4459a01704e0"
#define T6_ADDRESS "28ffbb19841704c3"
#define T7_ADDRESS "28ff3e69a0170478"
#define T8_ADDRESS "28ffcb4ba0170525"
typedef struct {
float value = INVALID_SENSOR_VALUE;
float cumulative_value = 0;
int count = 0;
long timestamp = 0;
bool sent = false;
bool valid = false;
} SensorData;
typedef struct {
DeviceAddress address;
SensorData data;
} DallasSensorData;
typedef struct {
DHT* sensor;
SensorData temperature;
SensorData humidity;
} DHTSensorData;
typedef SensorData* pSensorData;
typedef DallasSensorData* pDallasSensorData;
typedef DHTSensorData* pDHTSensorData;
//---------------------------------------------------------------------------------------
void init_measure();
void measure_next_sensor();
String format_measurement_data();
void assign_sensor_value(SensorData* sensor_data, float value);
pSensorData get_ds_sensor_data(const char* sensor_address);
void str_to_ds_address(char* str, DeviceAddress* address);
#endif

84
src/teleinsektarij.ino Normal file
View File

@ -0,0 +1,84 @@
#include "hw.h"
#include "comm.h"
#include "measure.h"
#include "dht_measure.h"
#include "defs.h"
#include "disp.h"
unsigned long timestamp_blink = 0;
unsigned long timestamp_http_request = 0;
unsigned long timestamp_ds_measure = 0;
unsigned long timestamp_dht_measure = 0;
bool first_loop = true;
//---------------------------------------------------------------------------------------
void setup()
{
init_hw();
#ifdef SERIAL_DEBUG
Serial.begin(SERIAL_BAUD_RATE);
Serial.setDebugOutput(true);
#endif
init_comm();
init_measure();
init_disp();
first_loop = true;
}
//---------------------------------------------------------------------------------------
void loop()
{
unsigned long current_millis = millis();
// connect WiFi
connect_comm();
if (first_loop)
{
// don't allow sending data to server in the first loop
timestamp_http_request = current_millis;
first_loop = false;
}
// indicate program run
if (current_millis - timestamp_blink > SIGNAL_LED_BLINK_INTERVAL)
{
blink_led(LED_BLUE, 50);
delay(50);
blink_led(LED_BLUE, 50);
timestamp_blink = current_millis;
}
if (current_millis - timestamp_ds_measure > DS_MEASURE_INTERVAL)
{
measure_next_sensor();
timestamp_ds_measure = current_millis;
}
if (current_millis - timestamp_dht_measure > DHT_MEASURE_INTERVAL)
{
dht_read_next_sensor();
timestamp_dht_measure = current_millis;
}
// post data to server
if (comm_is_connected() && current_millis - timestamp_http_request > SEND_DATA_TO_SERVER_INTERVAL)
{
post_data_to_server();
timestamp_http_request = current_millis;
}
handle_disp();
}
//---------------------------------------------------------------------------------------

48
test/main.cpp Normal file
View File

@ -0,0 +1,48 @@
#include <iostream>
#include <stdio.h>
#include <time.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
using namespace std;
#define T1_ADDRESS "28ffbb19841704c3"
#define T2_ADDRESS "28ff112ca01705d0"
#define T3_ADDRESS "28ffde61a01705e0"
#define T4_ADDRESS "28ff3c09a017046b"
#define T5_ADDRESS "28ff4459a01704e0"
#define T6_ADDRESS "28ffbb19841704c3"
#define T7_ADDRESS "28ff3e69a0170478"
#define T8_ADDRESS "28ffcb4ba0170525"
typedef unsigned char uint8_t;
typedef uint8_t DeviceAddress[8];
int main() {
char str_address[20];
char* p_str = str_address;
DeviceAddress out;
uint8_t* p_out = out;
char buff[3];
strcpy(str_address, T2_ADDRESS);
memset(buff, 0, sizeof(buff));
for (int i = 0; i < 8; i++) {
buff[0] = *p_str++;
buff[1] = *p_str++;
*p_out++ = (uint8_t)strtol(buff, NULL, 16);
}
printf("%s\n", str_address);
for(int i = 0; i < sizeof(out); i++) {
printf("%X ", out[i]);
}
printf("\n");
}

10
test/makefile Normal file
View File

@ -0,0 +1,10 @@
OOUTDIR = o/
output: main.o
g++ -std=c++0x -Wall $(OOUTDIR)main.o -o a.out
main.o: main.cpp
g++ -c main.cpp -o $(OOUTDIR)main.o
clean:
rm $(OOUTDIR)*.o a.out