Realisiere gerade ESP32 Gewächshaus-Controller, der jetzt 10 Werte zu meinem Portal www.robosolar.de loggt und möchte die Regel-Logik nicht fest codieren. Serverseitige Kontrolle geht auch nicht, da ich nur alle 30 Minuten drei 10-Minuten Logs hochlade. Klar könnte ich jetzt in so 2 Tage eigene "Scriptsprache" entwickeln die z.B. Lüfter anschaltet wenn (T_in > 35°C) und (V_batt>3.5V) Hab dann aber eher keine Freude auch noch das webinterface zu programmieren. Und über ein beta-Stadium würde ich wohl nie hinaus kommen. Also vielleicht kennt jemand schon so etwas. Die 10 Werte liegen im Esp32 praktisch schon als float-array for, so dass ich diese variablen leicht adressieren kann. Ich würde das Endresultat (Lüfter an oder aus) dann zeilenweise berechnen: 0: T_in > 35.0 => result0 = 1 oder 0 1: V_batt > 3.5 => result1 = 1 oder 0 2. result0 + result1 => result2 = 1 oder 2 oder 3 3. result2 == 3 => Lüfter an oder aus Es gäbe also das 10-Werte Array, ein Parameter-Array und ein Result-Array. Dazu die Zeilenweise x?y Berechnung. Wobei "?" ein Operator sein kann wie Wert>Parameter oder Wert>Wert oder Wert>Result oder Result>Result. Jeden Operator + - * / > < == gäbe es also vier mal. Kodier in einem ascii Zeichen hätte ich Platz für (128-32)/4 = 24 mathematische/logische Operatoren. Solch ein "Programm" lässt sich leicht codieren und vom Server zum ESP-Client schicken. Mag aber nicht schon wieder das Rad neu erfinden. Also Ideen immer zu mir. das Roland
:
Bearbeitet durch User
Wenn ich den Anspruch hätte, dass das so allgemein geht, würde ich das einfach selber schreiben. So schwer ist das nicht, ist vermutlich in 2-3 Stunden gemacht, und die Zeit würdest du zum Verstehen, Einbinden und Anpassen irgendeiner komischen Bibliothek auch brauchen. Das Interface würde ich sparen und die Regeln stattdessen in JSON oder irgendeiner einfachen DSL (aka, selbst gebauten Mini-Sprache) kodieren. Das Problem ist einfach zu klein für eine eigene Bibliothek, und zu speziell um Teil einer größeren zu sein. Das mit dem neu erfinden des Rades ist beim Programmieren auch oft überbewertet. Das gilt für die allgemeinen, bekannt komplexen Probleme, für die es etablierte, zuverlässige, getestete und dokumentierte Bibliotheken gibt. Für jedes Obskurum von Github eine seit Jahren nicht gewartete 100-Zeilen-Murksbibliothek zu importieren die so ungefähr das tut was man braucht, davon ist eher abzuraten. Dann lieber selber machen, dann weiß man was man hat.
:
Bearbeitet durch User
Das R. schrieb: > Also vielleicht kennt jemand schon so etwas. Im Bereich Home Automation gibt es diese ganze Scripterei auch mit Datenbank und Visualisierung, z.B. OpenHAB mit InfluxDB/Grafana oder wie sie alle heißen. Das läuft dann allerdings eher auf einem Raspberry Pi.
Wenn es textuell sein soll und es sich lediglich um Logik-Funktionen handeln soll, könntest du dich vielleicht an AWL aus der SPS-Technik orientieren. Einfaches Und / oder und Grenzwertvergleiche sind damit recht einfach zu handhaben und die übersetzund auf dem Automatisierungssystem unter Einschränkung der Funktionalitäten auch kein Hexenwerk. Für dein Lüfterbeispiel sähe das dann so aus:
1 | U( |
2 | L T_in |
3 | L 35.0 |
4 | >R |
5 | ) |
6 | U( |
7 | L VBat |
8 | L 3.5 |
9 | >R |
10 | ) |
11 | = Luefter |
Stefanus F. schrieb: > NodeMCU? Ja an Lua hab ich auch gedacht. Hab aber wohl zu viele I2C Module am Esp32.. Sven B. schrieb: > einfach selber schreiben. So schwer ist das nicht, ist vermutlich in 2-3 > Stunden gemacht, und die Zeit würdest du zum Verstehen, Einbinden und Ja wer das in 2-3 Stunden schafft ist wirklich gut. Hier meine 3 Stunden: **online testen** ---> http://www.robosolar.de/calcy.htm Die Berechnung wie "(v2>46.3)+(v10>3.0)=2=:s1" erfolgt von links nach rechts. v? = (sensor data) g? = (global variables) s? = (system variable like time or outupt-pins) Der "=" Operator ist der Vergleichsoperator Der "=:" Ist der Zuweise-Operator Man kann also Werte für spätere Berechnung in g? speichern. Mit s? kann man so auch die aktuelle Uhrzeit in die Berechnung einbeziehen und zum Beispiel einen Lüfter für 2 Minuten anschalten.. Und nun freue ich mich auf bugs :-)
1 | <html>
|
2 | <body>
|
3 | <div id=demo></div> |
4 | <script>
|
5 | |
6 | var s; // the calculation string |
7 | var aValue; // the array of values |
8 | var aGlobal; // the array of global variables |
9 | var aSystem; // the array of system variables, |
10 | // some readonly like time, some writeonly like output bits
|
11 | |
12 | function GetTypeArray(sType) |
13 | {
|
14 | switch(sType) |
15 | {
|
16 | case "v": return aValue; |
17 | case "g": return aGlobal; |
18 | case "s": return aSystem; |
19 | }
|
20 | return 0; |
21 | }
|
22 | |
23 | function Calc(s) |
24 | {
|
25 | var iLen = s.length; |
26 | var o1 = NextNum(s,0,iLen); |
27 | while(1) |
28 | {
|
29 | var sOp = s.charAt(o1.j); |
30 | var o2 = NextNum(s,o1.j+1,iLen); |
31 | var f = 0; |
32 | switch(sOp) |
33 | {
|
34 | case ">": f = (o1.fRet > o2.fRet) ? 1 : 0; break; |
35 | case "<": f = (o1.fRet < o2.fRet) ? 1 : 0; break; |
36 | case "=": f = (o1.fRet == o2.fRet) ? 1 : 0; break; |
37 | case "+": f = o1.fRet + o2.fRet; break; |
38 | case "-": f = o1.fRet + o2.fRet; break; |
39 | case "~": |
40 | if (!o2.t) // syntax error: can not set o1.f to a constant variable |
41 | return 0; |
42 | var a = GetTypeArray(o2.t); |
43 | a[o2.fRet] = f = o1.fRet; |
44 | break; |
45 | default: // unkown operator |
46 | return 0; |
47 | }
|
48 | if (o2.j >= iLen) |
49 | break; |
50 | o1 = {"t":0,"i":o2.i,"j":o2.j,"fRet":f}; |
51 | }
|
52 | return f; |
53 | }
|
54 | |
55 | function NextNum(s,i,j) |
56 | {
|
57 | //return 0;
|
58 | while(i<j) |
59 | {
|
60 | var c = s.charAt(i++); |
61 | if (c == "(") |
62 | {
|
63 | var iOpen = 1; |
64 | var j2=i; |
65 | while(j2<j) |
66 | {
|
67 | var c = s.charAt(j2++); |
68 | if (c == "(") |
69 | {
|
70 | iOpen++; |
71 | }
|
72 | if (c == ")") |
73 | {
|
74 | iOpen--; |
75 | break; |
76 | }
|
77 | }
|
78 | if (iOpen>0) |
79 | return 0; |
80 | //return NextNum(i,j2);
|
81 | return {"t":0,"i":i,"j":j2,"fRet":Calc(s.substr(i,j2-i-1))}; |
82 | }
|
83 | var cType = 0; // expecting this to be a constant number |
84 | var j2=--i; |
85 | while(j2<j) |
86 | {
|
87 | switch(c) |
88 | {
|
89 | case "+": |
90 | case "-": |
91 | case ".": |
92 | if (cType) // syntax error |
93 | return 0; |
94 | case "0": |
95 | case "1": |
96 | case "2": |
97 | case "3": |
98 | case "4": |
99 | case "5": |
100 | case "6": |
101 | case "7": |
102 | case "8": |
103 | case "9": |
104 | break; |
105 | default: |
106 | if (cType) |
107 | {
|
108 | var iPos = s.substr(i,j2-i); |
109 | var a = GetTypeArray(cType); |
110 | var f=a[iPos]; |
111 | return {"t":cType,"i":i,"j":j2,"fRet":f}; |
112 | }
|
113 | if (j2>i) // we already parsed a number |
114 | {
|
115 | return {"t":0,"i":i,"j":j2,"fRet":s.substr(i,j2-i)}; |
116 | }
|
117 | cType = c; // seems to be something linke "v1" |
118 | i++; |
119 | }
|
120 | var c = s.charAt(++j2); |
121 | }
|
122 | return {"t":cType,"i":i,"j":j2,"fRet":s.substr(i,j2-i)}; |
123 | }
|
124 | }
|
125 | |
126 | aValue = [0,16.7,64.4,95.5,18.3,43.7,95.5,99,6.5,43.6,4]; |
127 | aGlobal = [0,0,0,0,0,0,0,0,0]; |
128 | aSystem = [1556474970,0]; |
129 | |
130 | function Demo(sFormula) |
131 | {
|
132 | sCalc = sFormula.replace("=:","~"); |
133 | var sRes = Calc(sCalc); |
134 | var s = "v? = "+aValue.join(" , ") + " (sensor data)<br>" |
135 | +"g? = "+aGlobal.join(" , ") + " (global variables)<br>" |
136 | +"s? = "+aSystem.join(" , ") + " (system variable like time or outupt-pins)<br>" |
137 | +"calculate " + sFormula + "<br>" |
138 | +"result: " + sRes + "<br>" |
139 | +"next: <form onSubmit='return Demo(this.s.value,this)'><input name=s value='"+sFormula+"' size=50><input type=submit value='calculate'></form><br>"; |
140 | var r = document.getElementById("demo"); |
141 | if (r) |
142 | r.innerHTML = s; |
143 | return false; |
144 | }
|
145 | |
146 | Demo("(v2>46.3)+(v10>3.0)=2=:s1"); |
147 | |
148 | </script>
|
149 | </body>
|
150 | </html>
|
Das R. schrieb: > tefanus F. schrieb: >> NodeMCU? meine empfehlung wäre MicroPython!!! ich finde mit python kann man sehr effektiv/schnell solche aufgaben erledigen und beispiele/projekte/lib's gibt es reichlich zum starten/lernen/anwenden. ich programmiere immer mehr in mpy und bin begeistert wie "einfach" sich viele aufgaben in mpy erledigen lassen. in c muss ich da deutlich mehr "klimmzüge" machen und python kenntnisse kann man auch sonst gut ausserhalb embedded anwendungen gebrauchen. mt
So das portieren auf Arduino Code hat schon fast doppelt so lange gedauert:
1 | #define _DEMO_ 1 |
2 | #include "calcy.h" |
3 | |
4 | // this is the data like you will use in your project |
5 | float aG[9] = {0,0,0,0,0,0,0,0,0}; |
6 | float aS[2] = {1556474970,0}; |
7 | SyncData oData = {1556474000,0,16.7,64.4,95.5,18.3,43.7,95.5,99,6.5,43.6,4}; |
8 | |
9 | |
10 | void setup() { |
11 | Serial.begin(115200); |
12 | |
13 | // now point the calcy data arrays to your true data |
14 | aValue = (float*) &oData; aValue++; |
15 | aGlobal = (float*) &aG; |
16 | aSystem = (float*) &aS; |
17 | |
18 | String sCalc = "(v2>46.3)+(v10>3.0)=2~s1"; |
19 | String sError; |
20 | float fResult = Calc(sCalc,sError); |
21 | |
22 | if (sError) |
23 | Serial.println(sError); |
24 | } |
25 | |
26 | void loop() {} |
und die calcy.h
1 | #define _DEBUG_ 1 |
2 | |
3 | #ifdef _DEBUG_ |
4 | #define DEBUG(txt, val) Serial.print(txt); Serial.print(": "); Serial.print(val); Serial.print("\t"); |
5 | #define DEBUGLN(txt, val) Serial.print(txt); Serial.print(": "); Serial.println(val) |
6 | #endif |
7 | #ifndef _DEBUG_ |
8 | #define DEBUG(txt, val) |
9 | #define DEBUGLN(txt, val) |
10 | #endif |
11 | |
12 | |
13 | float* aValue; // the array of values |
14 | float* aGlobal; // the array of global variables |
15 | float* aSystem; // the array of system variables, |
16 | |
17 | void SetArrayValue(char sType,int i, float f) |
18 | { |
19 | switch(sType) |
20 | { |
21 | case 'v': aValue[i] = f; break; |
22 | case 'g': aGlobal[i] = f; break; |
23 | case 's': aSystem[i] = f; break; |
24 | } |
25 | DEBUGLN("\tSetArrayValue",String(sType)+","+String(i) + "," + String(f)); |
26 | } |
27 | |
28 | float GetArrayValue(char sType,int i) |
29 | { |
30 | switch(sType) |
31 | { |
32 | case 'v': |
33 | DEBUGLN("\tGetArrayValue(v)",String(i)+") = "+String(aValue[i])); |
34 | return aValue[i]; |
35 | case 'g': return aGlobal[i]; |
36 | case 's': return aSystem[i]; |
37 | } |
38 | return 0; |
39 | } |
40 | |
41 | typedef struct __attribute((__packed__)) |
42 | { |
43 | char t; |
44 | int i; |
45 | int j; |
46 | float fRet; |
47 | } Result; |
48 | |
49 | float Calc(String s, String& sError); // recursive programming -> declaration neccessary |
50 | Result NextNum(String s,int i, int j, String& sError) |
51 | { |
52 | while(i<j) |
53 | { |
54 | char c = s.charAt(i++); |
55 | if (c == '(') |
56 | { |
57 | int iOpen = 1; |
58 | int j2=i; |
59 | while(j2<j) |
60 | { |
61 | char c = s.charAt(j2++); |
62 | if (c == '(') |
63 | { |
64 | iOpen++; |
65 | } |
66 | if (c == ')') |
67 | { |
68 | iOpen--; |
69 | break; |
70 | } |
71 | } |
72 | if (iOpen>0) |
73 | { |
74 | sError += s + " : syntax error too many '('\n"; |
75 | return {0,i,j2,0}; |
76 | } |
77 | return {0,i,j2,Calc(s.substring(i,j2-1),sError)}; |
78 | } |
79 | char cType = 0; // expecting this to be a constant number |
80 | int j2=--i; |
81 | while(j2<j) |
82 | { |
83 | switch(c) |
84 | { |
85 | case '+': case '-': case '.': |
86 | if (cType) // syntax error |
87 | { |
88 | sError += s + " : syntax error #2\n"; |
89 | return {0,i,j2,0}; |
90 | } |
91 | case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': |
92 | break; |
93 | default: |
94 | if (cType) |
95 | { |
96 | int iPos = s.substring(i,j2).toInt(); |
97 | return {cType,i,j2,GetArrayValue(cType,iPos)}; |
98 | } |
99 | if (j2>i) // we already parsed a number |
100 | { |
101 | return {0,i,j2,s.substring(i,j2).toFloat()}; |
102 | } |
103 | cType = c; // seems to be something linke "v1" |
104 | i++; |
105 | } |
106 | j2++; |
107 | c = s.charAt(j2); |
108 | } |
109 | return {cType,i,j2,s.substring(i,j2).toFloat()}; |
110 | } |
111 | } |
112 | |
113 | float Calc(String s, String& sError) |
114 | { |
115 | DEBUGLN("Calc()",s); |
116 | int iLen = s.length(); |
117 | Result o1 = NextNum(s,0,iLen,sError); |
118 | float f = 0; |
119 | while(1) |
120 | { |
121 | char sOp = s.charAt(o1.j); |
122 | Result o2 = NextNum(s,o1.j+1,iLen,sError); |
123 | |
124 | switch(sOp) |
125 | { |
126 | case '>': f = (o1.fRet > o2.fRet) ? 1 : 0; break; |
127 | case '<': f = (o1.fRet < o2.fRet) ? 1 : 0; break; |
128 | case '=': f = (o1.fRet == o2.fRet) ? 1 : 0; break; |
129 | case '+': f = o1.fRet + o2.fRet; break; |
130 | case '-': f = o1.fRet + o2.fRet; break; |
131 | case '~': |
132 | { |
133 | if (!o2.t) // syntax error: can not set o1.f to a constant variable |
134 | return 0; |
135 | f = o1.fRet; |
136 | SetArrayValue(o2.t,(int)o2.fRet,f); |
137 | break; |
138 | } |
139 | default: // unkown operator |
140 | return 0; |
141 | } |
142 | if (o2.j >= iLen) |
143 | break; |
144 | //o1 = {"t":0,"i":o2.i,"j":o2.j,"fRet":f}; |
145 | o1.t=0; o1.i=o2.i; o1.j=o2.j; o1.fRet=f; |
146 | } |
147 | DEBUG("Calc()", s + "\treturning:" + String(f)); |
148 | return f; |
149 | } |
150 | |
151 | #ifdef _DEMO_ |
152 | typedef struct __attribute((__packed__)) |
153 | { |
154 | uint32_t iTime; |
155 | uint32_t wData; // bits 0..7 = iAlarms |
156 | float fInT; // temperature |
157 | float fInH; // humidity |
158 | float fInP; // preassure |
159 | float fOutT; |
160 | float fOutH; |
161 | float fOutP; |
162 | float fSoilH; // moisture |
163 | float fSoilA; // acid |
164 | float fSoilL; // light |
165 | float fBatt; // battery voltage |
166 | } SyncData; |
167 | #endif |
das Roland der kleine Physiker :-)
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.