Browse Source

add firehazard.c

Ondřej Hruška 1 year ago
parent
commit
d0d82ee8fb
Signed by: Ondřej Hruška <ondra@ondrovo.com> GPG key ID: 2C5FD5035250423D
14 changed files with 533 additions and 49 deletions
  1. 1 0
      .gitignore
  2. 11 5
      components/fileserver/src/token_subs.c
  3. 2 0
      main/CMakeLists.txt
  4. 39 16
      main/analog.c
  5. 3 1
      main/analog.h
  6. 32 1
      main/app_main.c
  7. 106 0
      main/arduinopid.c
  8. 52 0
      main/arduinopid.h
  9. 44 15
      main/files/embed/index.html
  10. 121 0
      main/firehazard.c
  11. 25 0
      main/firehazard.h
  12. 5 0
      main/utils.h
  13. 92 11
      main/web/websrv.c
  14. BIN
      pt100_lookup.ods

+ 1 - 0
.gitignore View File

@@ -3,3 +3,4 @@ cmake-build-debug
3 3
 build/
4 4
 .idea/
5 5
 sdkconfig.old
6
+.~lock*

+ 11 - 5
components/fileserver/src/token_subs.c View File

@@ -166,11 +166,17 @@ esp_err_t tpl_kv_replacer(httpd_req_t *r, void *context, const char *token, tpl_
166 166
     struct tpl_kv_list *head = context;
167 167
     SLIST_FOREACH(entry, head, link) {
168 168
         if (0==strcmp(entry->key, token)) {
169
-            return httpd_resp_send_chunk_escaped(r,
170
-                entry->subst_heap ?
171
-                    entry->subst_heap :
172
-                    entry->subst,
173
-                -1, escape);
169
+            if (entry->subst_heap) {
170
+                if (entry->subst_heap[0]) {
171
+                    return httpd_resp_send_chunk_escaped(r, entry->subst_heap, -1, escape);
172
+                }
173
+            } else {
174
+                if (entry->subst[0]) {
175
+                    return httpd_resp_send_chunk_escaped(r, entry->subst, -1, escape);
176
+                }
177
+                return ESP_OK;
178
+            }
179
+            return ESP_OK;
174 180
         }
175 181
     }
176 182
 

+ 2 - 0
main/CMakeLists.txt View File

@@ -19,6 +19,8 @@ set(COMPONENT_SRCS
19 19
         "web/websrv.c"
20 20
         "files/files_enum.c"
21 21
         "utils.c"
22
+        "arduinopid.c"
23
+        "firehazard.c"
22 24
 )
23 25
 set(COMPONENT_ADD_INCLUDEDIRS "." "liquid" "graphics")
24 26
 

+ 39 - 16
main/analog.c View File

@@ -6,6 +6,7 @@
6 6
 #include "driver/gpio.h"
7 7
 #include "driver/adc.h"
8 8
 #include "esp_adc_cal.h"
9
+#include "firehazard.h"
9 10
 
10 11
 static esp_adc_cal_characteristics_t *adc_chars;
11 12
 
@@ -20,15 +21,12 @@ float reg_meas_history[REG_HISTORY_LEN] = {};
20 21
 float reg_tset_history[REG_HISTORY_LEN] = {};
21 22
 uint32_t history_counter = 0;
22 23
 
23
-// TODO move to regulator module (make extern)
24
-float reg_setpoint = 125;
25
-
26 24
 static void analog_service(void *arg);
27 25
 
28 26
 static TaskHandle_t hAnalog;
29 27
 
30
-#define DEFAULT_VREF 1100
31
-#define NO_OF_SAMPLES 64
28
+#define DEFAULT_VREF 1100 // TODO try to find the exact value
29
+#define NO_OF_SAMPLES 128
32 30
 
33 31
 void analog_init() {
34 32
     printf("Analog init\n");
@@ -42,6 +40,32 @@ void analog_init() {
42 40
     assert (rv == pdPASS);
43 41
 }
44 42
 
43
+#define TSENSE_LOOKUP_LEN 81
44
+#define TSENSE_T_STEP 5
45
+#define TSENSE_T_MIN 0
46
+#define TSENSE_T_MAX 400
47
+static const float TSENSE_LOOKUP[TSENSE_LOOKUP_LEN] = {
48
+    // 4k7
49
+    //0.067859346082665f, 0.069156572911158f, 0.070450833857595f, 0.07174213479836f, 0.073030481589859f, 0.074315880068592f, 0.075598336051229f, 0.076877855334685f, 0.078154443696192f, 0.079428106893372f, 0.080698850664312f, 0.081966680727637f, 0.083231602782579f, 0.084493622509052f, 0.085752745567722f, 0.087008977600079f, 0.088262324228509f, 0.089512791056363f, 0.090760383668026f, 0.092005107628991f, 0.093246968485926f, 0.094485971766743f, 0.095722122980667f, 0.096955427618307f, 0.098185891151722f, 0.099413519034488f, 0.10063831670177f, 0.101860289570385f, 0.10307944303887f, 0.10429578248755f, 0.105509313278605f, 0.106720040756132f, 0.107927970246218f, 0.109133107056997f, 0.110335456478721f, 0.111535023783824f, 0.112731814226983f, 0.113925833045189f, 0.115117085457804f, 0.116305576666627f, 0.117491311855962f, 0.118674296192672f, 0.119854534826251f, 0.12103203288888f, 0.122206795495492f, 0.123378827743833f, 0.124548134714525f, 0.125714721471126f, 0.12687859306019f, 0.128039754511331f, 0.129198210837281f, 0.13035396703395f, 0.131507028080486f, 0.132657398939339f, 0.133805084556313f, 0.134950089860629f, 0.136092419764986f, 0.137232079165615f, 0.138369072942339f, 0.139503405958634f, 0.14063508306168f, 0.141764109082427f, 0.142890488835645f, 0.144014227119983f, 0.145135328718029f, 0.146253798396361f, 0.147369640905609f, 0.148482860980504f, 0.14959346333994f, 0.150701452687026f, 0.151806833709142f, 0.152909611077994f, 0.154009789449667f, 0.155107373464683f, 0.156202367748052f, 0.157294776909324f, 0.15838460554265f, 0.159471858226827f, 0.160556539525357f, 0.161638653986497f, 0.162718206143312f
50
+    // 2k7
51
+    //0.118961788031723f,0.121199278149888f,0.123430305551104f,0.125654890818048f,0.12787305443543f,0.130084816790549f,0.132290198173841f,0.134489218779432f,0.13668189870568f,0.138868257955715f,0.141048316437981f,0.143222093966762f,0.145389610262718f,0.147550884953408f,0.149705937573814f,0.151854787566858f,0.153997454283918f,0.156133956985341f,0.158264314840951f,0.160388546930552f,0.162506672244433f,0.164618709683859f,0.166724678061576f,0.168824596102292f,0.170918482443171f,0.173006355634316f,0.17508823413925f,0.177164136335394f,0.179234080514542f,0.181298084883333f,0.183356167563718f,0.185408346593427f,0.187454639926429f,0.189495065433395f,0.191529640902147f,0.19355838403812f,0.195581312464802f,0.197598443724189f,0.199609795277224f,0.20161538450424f,0.203615228705396f,0.205609345101115f,0.207597750832512f,0.209580462961826f,0.211557498472848f,0.213528874271338f,0.215494607185455f,0.217454713966165f,0.219409211287665f,0.221358115747788f,0.223301443868418f,0.225239212095895f,0.227171436801418f,0.229098134281449f,0.231019320758111f,0.232935012379582f,0.234845225220494f,0.236749975282318f,0.238649278493758f,0.240543150711133f,0.242431607718764f,0.24431466522935f,0.246192338884353f,0.248064644254368f,0.249931596839502f,0.25179321206974f,0.253649505305317f,0.255500491837083f,0.257346186886869f,0.259186605607846f,0.261021763084885f,0.262851674334914f,0.264676354307276f,0.266495817884073f,0.268310079880525f,0.270119155045314f,0.271923058060928f,0.273721803544007f,0.275515406045682f,0.277303880051916f,0.27908723998384f
52
+    0.118709444844989f,0.120942188771995f,0.123168483690844f,0.125388350140555f,0.127601808562385f,0.129808879300387f,0.132009582601957f,0.134203938618385f,0.136391967405395f,0.138573688923688f,0.140749123039476f,0.142918289525014f,0.145081208059131f,0.14723789822775f,0.149388379524415f,0.151532671350807f,0.153670793017255f,0.155802763743251f,0.157928602657955f,0.1600483288007f,0.16216196112149f,0.1642695184815f,0.166371019653566f,0.168466483322681f,0.170555928086473f,0.172639372455698f,0.174716834854712f,0.176788333621955f,0.178853887010421f,0.180913513188126f,0.182967230238583f,0.185015056161259f,0.18705700887204f,0.189093106203687f,0.191123365906294f,0.193147805647736f,0.195166443014119f,0.197179295510229f,0.19918638055997f,0.201187715506807f,0.203183317614203f,0.205173204066052f,0.207157391967109f,0.209135898343422f,0.211108740142754f,0.213075934235005f,0.215037497412637f,0.216993446391085f,0.218943797809176f,0.220888568229535f,0.222827774139f,0.224761431949025f,0.226689557996082f,0.228612168542064f,0.230529279774684f,0.232440907807868f,0.234347068682147f,0.236247778365053f,0.238143052751499f,0.24003290766417f,0.241917358853906f,0.243796422000076f,0.245670112710962f,0.247538446524132f,0.249401438906812f,0.251259105256259f,0.253111460900123f,0.254958521096822f,0.256800301035897f,0.258636815838374f,0.260468080557129f,0.262294110177234f,0.264114919616321f,0.265930523724925f,0.267740937286839f,0.269546175019461f,0.271346251574132f,0.273141181536489f,0.274930979426797f,0.276715659700291f,0.278495236747511f
53
+};
54
+
55
+static float v_to_c(float v){
56
+    // TODO use binary search.. lol
57
+    for (int i = 1; i < TSENSE_LOOKUP_LEN; i++) {
58
+        float cur = TSENSE_LOOKUP[i];
59
+        if (cur >= v) {
60
+            float prev = TSENSE_LOOKUP[i-1];
61
+
62
+            float ratio = (v - prev) / (cur - prev);
63
+            return TSENSE_T_MIN + ((float) i + ratio) * TSENSE_T_STEP;
64
+        }
65
+    }
66
+
67
+    return TSENSE_T_MAX;
68
+}
45 69
 
46 70
 static void __attribute__((noreturn)) analog_service(void *arg) {
47 71
     while (1) {
@@ -53,20 +77,17 @@ static void __attribute__((noreturn)) analog_service(void *arg) {
53 77
         adc_reading /= NO_OF_SAMPLES;
54 78
 
55 79
         //Convert adc_reading to voltage in mV
56
-        uint32_t voltage = esp_adc_cal_raw_to_voltage(adc_reading, adc_chars);
80
+        uint32_t mv = esp_adc_cal_raw_to_voltage(adc_reading, adc_chars);
57 81
 
58 82
 #define CORRECT -10;
59
-        voltage += CORRECT;
83
+        mv += CORRECT;
60 84
 
61
-//        printf("Raw: %d ... Voltage: %dmV ...", adc_reading, voltage);
85
+        printf("Raw: %d ... Voltage: %dmV ...", adc_reading, mv);
62 86
 
63
-        float volts = voltage * 0.001f;
87
+        float volts = mv * 0.001f;
64 88
 
65
-#define R1 4750
66
-#define V1 3.3f
67
-        float r_pt100 = (volts * R1)/(V1 - volts);
68
-        float celsius = (r_pt100/100.0f - 1.0f) / 3.9083E-3f;
69
-//        printf("Rpt %.3f, Celsius: %.1f\n", r_pt100, celsius);
89
+        float celsius = v_to_c(volts);
90
+        printf("Celsius: %.1f°C\n", celsius);
70 91
 
71 92
         measurement_celsius = celsius;
72 93
 
@@ -75,10 +96,12 @@ static void __attribute__((noreturn)) analog_service(void *arg) {
75 96
             reg_tset_history[i] = reg_tset_history[i+1];
76 97
         }
77 98
         reg_meas_history[REG_HISTORY_LEN-1] = celsius;
78
-        reg_tset_history[REG_HISTORY_LEN-1] = reg_setpoint;
99
+        reg_tset_history[REG_HISTORY_LEN-1] = fire_get_setpoint(true);
79 100
         history_counter = (history_counter + 1) % 20;
80 101
 
81
-        vTaskDelay(pdMS_TO_TICKS(500));
102
+        fire_regulate(celsius);
103
+
104
+        vTaskDelay(pdMS_TO_TICKS(ANALOG_SAMPLE_TIME_MS));
82 105
     }
83 106
 }
84 107
 

+ 3 - 1
main/analog.h View File

@@ -7,7 +7,9 @@
7 7
 #ifndef REFLOWER_ANALOG_H
8 8
 #define REFLOWER_ANALOG_H
9 9
 
10
-#define REG_HISTORY_LEN 121
10
+#define ANALOG_SAMPLE_TIME_MS 1000
11
+
12
+#define REG_HISTORY_LEN 241
11 13
 extern float reg_meas_history[REG_HISTORY_LEN];
12 14
 extern float reg_tset_history[REG_HISTORY_LEN];
13 15
 extern uint32_t history_counter;

+ 32 - 1
main/app_main.c View File

@@ -9,6 +9,7 @@
9 9
 #include <stdio.h>
10 10
 #include <web/websrv.h>
11 11
 #include <esp_wifi.h>
12
+#include <esp_log.h>
12 13
 #include "freertos/FreeRTOS.h"
13 14
 #include "freertos/task.h"
14 15
 #include "esp_system.h"
@@ -20,6 +21,7 @@
20 21
 #include "utils.h"
21 22
 #include "esp_event_loop.h"
22 23
 #include "nvs_flash.h"
24
+#include "firehazard.h"
23 25
 
24 26
 /**
25 27
  * Application event handler
@@ -75,7 +77,17 @@ static void initialise_wifi(void)
75 77
 
76 78
 void __attribute__((noreturn)) app_main()
77 79
 {
78
-    ESP_ERROR_CHECK(nvs_flash_init());
80
+    // Initialize NVS
81
+    esp_err_t err = nvs_flash_init();
82
+    if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) {
83
+        // NVS partition was truncated and needs to be erased
84
+        // Retry nvs_flash_init
85
+        ESP_ERROR_CHECK(nvs_flash_erase());
86
+        err = nvs_flash_init();
87
+    }
88
+    ESP_ERROR_CHECK( err );
89
+
90
+
79 91
     //ESP_ERROR_CHECK(esp_register_shutdown_handler(cspemu_run_shutdown_handlers));
80 92
 
81 93
     ESP_ERROR_CHECK(esp_event_loop_init(event_handler, NULL));
@@ -108,6 +120,25 @@ void __attribute__((noreturn)) app_main()
108 120
 
109 121
     try_reconn_if_have_wifi_creds();
110 122
     websrv_init();
123
+    fire_init();
124
+
125
+    nvs_handle nvs;
126
+
127
+    ESP_ERROR_CHECK(nvs_open("config", NVS_READWRITE, &nvs)); // must use RW to allow opening nonexistent
128
+
129
+    union uf32 kp, ki, kd;
130
+
131
+    // TODO set good defaults
132
+    kp.f = 0.3f;
133
+    ki.f = 0.5f;
134
+    kd.f = 0.0f;
135
+
136
+    nvs_get_u32(nvs, "kp", &kp.u);
137
+    nvs_get_u32(nvs, "ki", &ki.u);
138
+    nvs_get_u32(nvs, "kd", &kd.u);
139
+
140
+    fire_set_tuning(kp.f, ki.f, kd.f);
141
+    nvs_close(nvs);
111 142
 
112 143
     bool level = 0;
113 144
     while (1) {

+ 106 - 0
main/arduinopid.c View File

@@ -0,0 +1,106 @@
1
+#include "arduinopid.h"
2
+#include <stdbool.h>
3
+#include <stdint.h>
4
+#include <freertos/FreeRTOS.h>
5
+#include <freertos/task.h>
6
+
7
+static void clampOutput(struct PID *self) {
8
+    if (self->Output > self->outMax) self->Output = self->outMax;
9
+    else if (self->Output < self->outMin) self->Output = self->outMin;
10
+}
11
+
12
+static void clampIterm(struct PID *self) {
13
+    if (self->ITerm > self->outMax) self->ITerm = self->outMax;
14
+    else if (self->ITerm < self->outMin) self->ITerm = self->outMin;
15
+}
16
+
17
+void PID_Compute(struct PID *self, float Input)
18
+{
19
+    if (!self->ctlMode) return;
20
+    self->Input = Input;
21
+
22
+    uint32_t now = xTaskGetTickCount();
23
+    int32_t timeChange = (now - self->lastTime);
24
+    if (timeChange >= self->SampleTime) {
25
+        /*Compute all the working error variables*/
26
+        float error = self->Setpoint - Input;
27
+        self->ITerm += (self->ki * error);
28
+
29
+        clampIterm(self);
30
+
31
+        float dInput = (Input - self->lastInput);
32
+
33
+        /*Compute PID Output*/
34
+        self->Output = self->kp * error + self->ITerm - self->kd * dInput;
35
+
36
+        clampOutput(self);
37
+
38
+        /*Remember some variables for next time*/
39
+        self->lastInput = Input;
40
+        self->lastTime = now;
41
+    }
42
+}
43
+
44
+void PID_SetSetpoint(struct PID *self, float Setpoint)
45
+{
46
+    self->Setpoint = Setpoint;
47
+}
48
+
49
+void PID_SetTunings(struct PID *self, float Kp, float Ki, float Kd)
50
+{
51
+    if (Kp < 0 || Ki < 0 || Kd < 0) return;
52
+
53
+    float SampleTimeInSec = ((float) self->SampleTime) / 1000;
54
+    self->kp = Kp;
55
+    self->ki = Ki * SampleTimeInSec;
56
+    self->kd = Kd / SampleTimeInSec;
57
+
58
+    if (self->controllerDirection == PID_REVERSE) {
59
+        self->kp = -self->kp;
60
+        self->ki = -self->ki;
61
+        self->kd = -self->kd;
62
+    }
63
+}
64
+
65
+void PID_SetSampleTime(struct PID *self, uint32_t NewSampleTime)
66
+{
67
+    if (NewSampleTime > 0) {
68
+        float ratio = (float) NewSampleTime
69
+                      / (float) self->SampleTime;
70
+        self->ki *= ratio;
71
+        self->kd /= ratio;
72
+        self->SampleTime = (uint32_t) NewSampleTime;
73
+    }
74
+}
75
+
76
+void PID_SetOutputLimits(struct PID *self, float Min, float Max)
77
+{
78
+    if (Min > Max) return;
79
+    self->outMin = Min;
80
+    self->outMax = Max;
81
+
82
+    clampOutput(self);
83
+    clampIterm(self);
84
+}
85
+
86
+void PID_SetCtlMode(struct PID *self, enum PIDCtlMode Mode)
87
+{
88
+    bool newAuto = (Mode == PID_AUTOMATIC);
89
+    if (newAuto == !self->ctlMode) {  /*we just went from manual to auto*/
90
+        PID_Initialize(self);
91
+    }
92
+    self->ctlMode = newAuto;
93
+}
94
+
95
+void PID_Initialize(struct PID *self)
96
+{
97
+    self->lastInput = self->Input;
98
+    self->ITerm = self->Output;
99
+
100
+    clampIterm(self);
101
+}
102
+
103
+void PID_SetControllerDirection(struct PID *self, enum PIDDirection Direction)
104
+{
105
+    self->controllerDirection = Direction;
106
+}

+ 52 - 0
main/arduinopid.h View File

@@ -0,0 +1,52 @@
1
+/**
2
+ * adapted from the Arduino PID library
3
+ * 
4
+ * Created on 2020/01/08.
5
+ */
6
+
7
+#ifndef ARDUINOPID_H
8
+#define ARDUINOPID_H
9
+
10
+#include <stdint.h>
11
+
12
+enum PIDCtlMode {
13
+    PID_MANUAL = 0,
14
+    PID_AUTOMATIC = 1,
15
+};
16
+
17
+enum PIDDirection {
18
+    PID_DIRECT = 0,
19
+    PID_REVERSE = 1,
20
+};
21
+
22
+struct PID {
23
+    /*working variables*/
24
+    uint32_t lastTime;
25
+    float Input, Output, Setpoint;
26
+    float ITerm, lastInput;
27
+    float kp, ki, kd;
28
+    uint32_t SampleTime; //1 sec  = 1000
29
+    float outMin, outMax;
30
+    enum PIDCtlMode ctlMode; // false
31
+    enum PIDDirection controllerDirection;
32
+};
33
+
34
+#define PID_DEFAULT() { .SampleTime = 1000, .ctlMode=PID_MANUAL, .controllerDirection=PID_DIRECT }
35
+
36
+void PID_Compute(struct PID *self, float Input);
37
+
38
+void PID_SetTunings(struct PID *self, float Kp, float Ki, float Kd);
39
+
40
+void PID_SetSampleTime(struct PID *self, uint32_t NewSampleTime);
41
+
42
+void PID_SetOutputLimits(struct PID *self, float Min, float Max);
43
+
44
+void PID_SetCtlMode(struct PID *self, enum PIDCtlMode Mode);
45
+
46
+void PID_Initialize(struct PID *self);
47
+
48
+void PID_SetSetpoint(struct PID *self, float Setpoint);
49
+
50
+void PID_SetControllerDirection(struct PID *self, enum PIDDirection Direction);
51
+
52
+#endif //ARDUINOPID_H

+ 44 - 15
main/files/embed/index.html View File

@@ -42,13 +42,9 @@
42 42
         stroke: black;
43 43
     }
44 44
     
45
-    path.major {
46
-        stroke-width: 2px;
47
-    }
48
-    
49 45
     .ylabels text {
50 46
         font-size: 10px;
51
-        text-anchor: end;
47
+        text-anchor: start;
52 48
         font-family: Droid Sans, sans-serif;
53 49
         vertical-align: middle;
54 50
     }
@@ -63,13 +59,13 @@
63 59
         fill: none;
64 60
     }
65 61
     </style>
66
-    <g transform="translate(50,15)">
62
+    <g transform="translate(10,15)">
67 63
         <path d="M100,0 v400m100,-400 v400m100,-400 v400m100,-400 v400m100,-400 v400m100,-400 v400"
68 64
             class="grid" transform="translate(0,0)" id="grid-v" />
69 65
         <path d="M0,100 h600m-600,100 h600m-600,100 h600m-600,100"
70 66
             class="grid" stroke-dashoffset="0" id="grid-h" />
71
-        <path d="M-10,0 h10m-10,100 h10m-10,100 h10m-10,100 h10m-10,100 h10" class="ticks" />
72
-        <path d="M-5,10
67
+        <path d="M600,0 h10m-10,100 h10m-10,100 h10m-10,100 h10m-10,100 h10" class="ticks" />
68
+        <path d="M600,10
73 69
                 h5m-5,10h5m-5,10h5m-5,10h5m-5,10h5m-5,10h5m-5,10h5m-5,10h5m-5,10h5m-5,20
74 70
                 h5m-5,10h5m-5,10h5m-5,10h5m-5,10h5m-5,10h5m-5,10h5m-5,10h5m-5,10h5m-5,20
75 71
                 h5m-5,10h5m-5,10h5m-5,10h5m-5,10h5m-5,10h5m-5,10h5m-5,10h5m-5,10h5m-5,20
@@ -79,7 +75,7 @@
79 75
             <path d="{ser-act}" stroke="red" id="ser-act" /><!--M0,0L300,100L600,400-->
80 76
         </g>
81 77
         <path d="M0,0h600v400h-600Z" class="frame" />
82
-        <g class="ylabels" transform="translate(0,3)">
78
+        <g class="ylabels" transform="translate(630,3)">
83 79
             <text x="-15" y="0">400 °C</text>
84 80
             <text x="-15" y="100">300 °C</text>
85 81
             <text x="-15" y="200">200 °C</text>
@@ -90,13 +86,42 @@
90 86
 </svg>
91 87
 </td>
92 88
 <td id="td-side">
93
-Sidebar
89
+    t<sub>sens</sub> = <span id="temp">{temp}</span>°C<br>
90
+
91
+    <form action="/set" method="POST">
92
+        <h3>Oven Control</h3>
93
+        <table>
94
+            <tr>
95
+                <th>Heater:</th>
96
+                <td><select name="fire">
97
+                    <option value="0" {fire_dis_ck}>Disable</option>
98
+                    <option value="1" {fire_en_ck}>Enable</option>
99
+                </select></td>
100
+            </tr>
101
+            <tr>
102
+                <th>t<sub>set</sub> =</th>
103
+                <td><input type="number" step="1" name="tset" value="{tset}"></td>
104
+            </tr>
105
+            <tr><td></td><td><input type="submit" value="Set"></td></tr>
106
+        </table>
107
+    </form>
108
+
109
+    <form action="/set" method="POST">
110
+        <h3>PID tuning</h3>
111
+        <table>
112
+            <tr><th>Kp = </th><td><input type="number" step="any" name="kp" value="{kp}"></td></tr>
113
+            <tr><th>Ki = </th><td><input type="number" step="any" name="ki" value="{ki}"></td></tr>
114
+            <tr><th>Kd = </th><td><input type="number" step="any" name="kd" value="{kd}"></td></tr>
115
+            <tr><td></td><td><input type="submit" value="Set"></td></tr>
116
+        </table>
117
+    </form>
94 118
 </td>
95 119
 </tr>
96 120
 </table>
97 121
 
98 122
 <script>
99
-var Qi = function (x) { return document.getElementById(x) };
123
+let Qi = function (x) { return document.getElementById(x) };
124
+const REFR_TIME = 1000;
100 125
 function update(data) {
101 126
 	if (data) {
102 127
 		let rows = data.split('\x1e');
@@ -113,25 +138,29 @@ function update(data) {
113 138
                     Qi('grid-v').setAttribute('transform', 'translate(-'+(va*5)+',0)');
114 139
                     Qi('grid-h').setAttribute('stroke-dashoffset', -va * 5);
115 140
                     break;
141
+			    case 'temp':
142
+			        Qi('temp').innerHTML = va;
143
+			        break;
144
+			        // form fields are not live updated.
116 145
             }
117 146
 		});
118 147
 	} else {
119
-		var xhr=new XMLHttpRequest();
148
+		let xhr=new XMLHttpRequest();
120 149
 		xhr.onreadystatechange = function () {
121 150
 			if (xhr.readyState===4){
122 151
 				if (xhr.status===200) {
123 152
 					update(xhr.responseText);
124 153
 				}
125
-				setTimeout(update, 500);
154
+				setTimeout(update, REFR_TIME);
126 155
 			}
127 156
 		};
128 157
 		xhr.onerror = function () {
129
-			setTimeout(update, 500);
158
+			setTimeout(update, REFR_TIME);
130 159
 		};
131 160
 		xhr.open('GET', '/data');
132 161
 		xhr.send();
133 162
 	}
134 163
 }
135 164
 
136
-setTimeout(update, 500);
165
+setTimeout(update, REFR_TIME);
137 166
 </script>

+ 121 - 0
main/firehazard.c View File

@@ -0,0 +1,121 @@
1
+#include "firehazard.h"
2
+#include "arduinopid.h"
3
+#include <stdio.h>
4
+#include <stdbool.h>
5
+
6
+#include <math.h>
7
+#include <esp_log.h>
8
+#include <nvs.h>
9
+#include "driver/ledc.h"
10
+#include "esp_err.h"
11
+
12
+static const char *TAG = "fire";
13
+
14
+static struct PID pid = PID_DEFAULT();
15
+
16
+static void pwm_init(void);
17
+static void pwm_set(float duty);
18
+
19
+void fire_get_tuning(float *kp, float *ki, float *kd) {
20
+    *kp = pid.kp;
21
+    *ki = pid.ki;
22
+    *kd = pid.kd;
23
+}
24
+
25
+void fire_init() {
26
+    printf("Regulator init");
27
+
28
+    PID_Initialize(&pid);
29
+    PID_SetOutputLimits(&pid, 0, 1);
30
+    PID_SetCtlMode(&pid, PID_MANUAL);
31
+
32
+    PID_SetTunings(&pid, 0.3, 0.01, 0.1); // TODO load from nvs
33
+    pwm_init();
34
+
35
+    fire_setlevel(20);
36
+//    fire_enable(true);
37
+}
38
+
39
+void fire_set_tuning(float kp, float ki, float kd) {
40
+    ESP_LOGI(TAG, "PID set tuning Kp=%.3f, Ki=%.3f, Kd=%.3f", kp, ki, kd);
41
+    PID_SetTunings(&pid, kp, ki, kd);
42
+}
43
+
44
+float fire_get_setpoint(bool off_is_zero) {
45
+    if (off_is_zero) {
46
+        return pid.ctlMode == PID_MANUAL ? 0 : pid.Setpoint;
47
+    } else {
48
+        return pid.Setpoint;
49
+    }
50
+}
51
+
52
+void fire_setlevel(float cels) {
53
+    ESP_LOGI(TAG, "PID set target %.3f°C", cels);
54
+
55
+    if (cels < 0) cels = 0;
56
+    if (cels > MAX_SETPOINT) cels = MAX_SETPOINT;
57
+    PID_SetSetpoint(&pid, cels);
58
+}
59
+
60
+void fire_enable(bool enable) {
61
+    ESP_LOGI(TAG, "Heater %s", enable ? "enable" : "disable");
62
+
63
+    PID_SetCtlMode(&pid, enable ? PID_AUTOMATIC : PID_MANUAL);
64
+}
65
+
66
+bool fire_enabled() {
67
+    return pid.ctlMode == PID_AUTOMATIC;
68
+}
69
+
70
+void fire_regulate(float cels) {
71
+    PID_Compute(&pid, cels);
72
+
73
+    if (cels > MAX_TSENSE || cels < MIN_TSENSE) {
74
+        ESP_LOGE(TAG, "Tsense out of bounds! Stopping.");
75
+        fire_enable(false);
76
+    }
77
+
78
+    if (pid.ctlMode == PID_MANUAL) {
79
+        pwm_set(0);
80
+    } else {
81
+        printf("PID in %.2f°C, out %.3f, I %.3f\n", cels, pid.Output, pid.ITerm);
82
+        pwm_set(pid.Output);
83
+    }
84
+}
85
+
86
+
87
+#define PWM_CHANNEL LEDC_CHANNEL_1
88
+#define PWM_TIMER LEDC_TIMER_1
89
+#define PWM_BIT_NUM LEDC_TIMER_12_BIT
90
+
91
+#define PWM_PIN GPIO_NUM_14
92
+
93
+static void pwm_init(void)
94
+{
95
+    ledc_channel_config_t ledc_channel_left = {0};
96
+    ledc_channel_left.gpio_num = PWM_PIN;
97
+    ledc_channel_left.speed_mode = LEDC_HIGH_SPEED_MODE;
98
+    ledc_channel_left.channel = PWM_CHANNEL;
99
+    ledc_channel_left.intr_type = LEDC_INTR_DISABLE;
100
+    ledc_channel_left.timer_sel = PWM_TIMER;
101
+    ledc_channel_left.duty = 0;
102
+
103
+    ledc_timer_config_t ledc_timer = {0};
104
+    ledc_timer.speed_mode = LEDC_HIGH_SPEED_MODE;
105
+    ledc_timer.duty_resolution = PWM_BIT_NUM;
106
+    ledc_timer.timer_num = PWM_TIMER;
107
+    ledc_timer.freq_hz = 1; // TODO ??
108
+
109
+    ESP_ERROR_CHECK( ledc_channel_config(&ledc_channel_left) );
110
+    ESP_ERROR_CHECK( ledc_timer_config(&ledc_timer) );
111
+}
112
+
113
+static void pwm_set(float duty)
114
+{
115
+    uint32_t max_duty = (1 << PWM_BIT_NUM);// - 1
116
+    uint32_t dutycycle = lroundf((duty) * (float)max_duty);
117
+    printf("Dutycycle %d\n", dutycycle);
118
+
119
+    ESP_ERROR_CHECK( ledc_set_duty(LEDC_HIGH_SPEED_MODE, PWM_CHANNEL, dutycycle) );
120
+    ESP_ERROR_CHECK( ledc_update_duty(LEDC_HIGH_SPEED_MODE, PWM_CHANNEL) );
121
+}

+ 25 - 0
main/firehazard.h View File

@@ -0,0 +1,25 @@
1
+/**
2
+ * TODO file description
3
+ * 
4
+ * Created on 2020/01/08.
5
+ */
6
+
7
+#ifndef REFLOWER_FIREHAZARD_H
8
+#define REFLOWER_FIREHAZARD_H
9
+#include <stdbool.h>
10
+
11
+#define MAX_SETPOINT 350
12
+#define MAX_TSENSE 400
13
+#define MIN_TSENSE 0
14
+
15
+void fire_regulate(float cels);
16
+void fire_init();
17
+void fire_setlevel(float cels);
18
+void fire_enable(bool enable);
19
+
20
+float fire_get_setpoint(bool off_is_zero);
21
+void fire_get_tuning(float *kp, float *ki, float *kd);
22
+bool fire_enabled();
23
+void fire_set_tuning(float kp, float ki, float kd);
24
+
25
+#endif //REFLOWER_FIREHAZARD_H

+ 5 - 0
main/utils.h View File

@@ -73,4 +73,9 @@ void feed_all_dogs(void);
73 73
 /** Get embedded file size (must be declared with efile() first) */
74 74
 #define efsize(varname) (varname##_end - varname)
75 75
 
76
+union uf32 {
77
+    uint32_t u;
78
+    float f;
79
+};
80
+
76 81
 #endif //CSPEMU_UTILS_H

+ 92 - 11
main/web/websrv.c View File

@@ -3,7 +3,9 @@
3 3
 
4 4
 #include <fileserver/token_subs.h>
5 5
 #include <httpd_utils/captive.h>
6
+#include <errno.h>
6 7
 
8
+#include "firehazard.h"
7 9
 #include "websrv.h"
8 10
 #include "esp_http_server.h"
9 11
 #include "utils.h"
@@ -40,14 +42,14 @@ static struct tpl_kv_list build_index_replacements_kv(void)
40 42
     bool last_empty = true; // first is move
41 43
     bool suc;
42 44
     for (int i = 0; i < REG_HISTORY_LEN; i++) {
43
-        int x = i*5;
45
+        float x = i*2.5f;
44 46
         if (reg_meas_history[i] == 0) {
45
-            snprintf(scratch1, SCRATCH_SIZE, "M%d,0", x);
46
-            snprintf(scratch2, SCRATCH_SIZE, "M%d,0", x);
47
+            snprintf(scratch1, SCRATCH_SIZE, "M%.1f,0", x);
48
+            snprintf(scratch2, SCRATCH_SIZE, "M%.1f,0", x);
47 49
             last_empty = true;
48 50
         } else {
49
-            snprintf(scratch1, SCRATCH_SIZE, "%c%d,%d", last_empty ? 'M' : 'L', x, (int)(400 - reg_meas_history[i]));
50
-            snprintf(scratch2, SCRATCH_SIZE, "%c%d,%d", last_empty ? 'M' : 'L', x, (int)(400 - reg_tset_history[i]));
51
+            snprintf(scratch1, SCRATCH_SIZE, "%c%.1f,%d", last_empty ? 'M' : 'L', x, (int)(400 - reg_meas_history[i]));
52
+            snprintf(scratch2, SCRATCH_SIZE, "%c%.1f,%d", last_empty ? 'M' : 'L', x, (int)(400 - reg_tset_history[i]));
51 53
             last_empty = false;
52 54
         }
53 55
 
@@ -60,8 +62,30 @@ static struct tpl_kv_list build_index_replacements_kv(void)
60 62
     tpl_kv_add_heapstr(&kv, "ser-act", path1);
61 63
     tpl_kv_add_heapstr(&kv, "ser-set", path2);
62 64
 
65
+    bool ena = fire_enabled();
66
+    tpl_kv_add(&kv, "fire_dis_ck", ena ? "" : "selected");
67
+    tpl_kv_add(&kv, "fire_en_ck", ena ? "selected" : "");
68
+
63 69
     tpl_kv_add_int(&kv, "timeshift", history_counter);
64 70
 
71
+    snprintf(scratch1, SCRATCH_SIZE, "%.2f", analog_read());
72
+    tpl_kv_add(&kv, "temp", scratch1);
73
+
74
+    snprintf(scratch1, SCRATCH_SIZE, "%.0f",  fire_get_setpoint(false));
75
+    tpl_kv_add(&kv, "tset", scratch1);
76
+
77
+    float kp, ki, kd;
78
+    fire_get_tuning(&kp, &ki, &kd);
79
+
80
+    snprintf(scratch1, SCRATCH_SIZE, "%.3f",  kp);
81
+    tpl_kv_add(&kv, "kp", scratch1);
82
+
83
+    snprintf(scratch1, SCRATCH_SIZE, "%.3f",  ki);
84
+    tpl_kv_add(&kv, "ki", scratch1);
85
+
86
+    snprintf(scratch1, SCRATCH_SIZE, "%.3f",  kd);
87
+    tpl_kv_add(&kv, "kd", scratch1);
88
+
65 89
 #undef SCRATCH_SIZE
66 90
 
67 91
     return kv;
@@ -90,7 +114,7 @@ static esp_err_t handler_update(httpd_req_t *req)
90 114
 /* Set a param */
91 115
 static esp_err_t handler_set(httpd_req_t *req)
92 116
 {
93
-    char buf[64];
117
+    char buf[200];
94 118
     int n = httpd_req_recv(req, buf, 63);
95 119
     if (n < 0) {
96 120
         ESP_LOGW(TAG, "rx er");
@@ -98,13 +122,70 @@ static esp_err_t handler_set(httpd_req_t *req)
98 122
     }
99 123
     buf[n]=0;
100 124
 
101
-    char keybuf[20];
102
-    char valbuf[20];
103
-    if (ESP_OK != httpd_query_key_value(buf, "key", keybuf, 20)) goto err;
104
-    if (ESP_OK != httpd_query_key_value(buf, "value", valbuf, 20)) goto err;
125
+    char val[20];
105 126
 
106
-    // TODO handle
127
+    // select box 0/1
128
+    esp_err_t rv = httpd_query_key_value(buf, "fire", val, 20);
129
+    if (rv == ESP_OK) {
130
+        fire_enable(val[0] == '1');
131
+    }
132
+
133
+    rv = httpd_query_key_value(buf, "tset", val, 20);
134
+    if (rv == ESP_OK) {
135
+        errno = 0;
136
+        float f = atoff(val);
137
+        if (!errno) {
138
+            fire_setlevel(f);
139
+        }
140
+    }
141
+
142
+    float kp, ki, kd;
143
+
144
+    // sorry
145
+
146
+    // Kp
147
+    rv = httpd_query_key_value(buf, "kp", val, 20);
148
+    if (rv == ESP_OK) {
149
+        errno = 0;
150
+        kp = atoff(val);
151
+        if (!errno) {
152
+            // Ki
153
+            rv = httpd_query_key_value(buf, "ki", val, 20);
154
+            if (rv == ESP_OK) {
155
+                errno = 0;
156
+                ki = atoff(val);
157
+                if (!errno) {
158
+                    // Kd
159
+                    rv = httpd_query_key_value(buf, "kd", val, 20);
160
+                    if (rv == ESP_OK) {
161
+                        errno = 0;
162
+                        kd = atoff(val);
163
+                        if (!errno) {
164
+                            fire_set_tuning(kp, ki, kd);
165
+
166
+                            // save to NVS
167
+                            nvs_handle nvs;
168
+                            ESP_ERROR_CHECK(nvs_open("config", NVS_READWRITE, &nvs));
169
+                            union uf32 ukp, uki, ukd;
170
+                            ukp.f = kp;
171
+                            uki.f = ki;
172
+                            ukd.f = kd;
173
+                            nvs_set_u32(nvs, "kp", ukp.u);
174
+                            nvs_set_u32(nvs, "ki", uki.u);
175
+                            nvs_set_u32(nvs, "kd", ukd.u);
176
+                            nvs_commit(nvs);
177
+                            nvs_close(nvs);
178
+                        }
179
+                    }
180
+
181
+                }
182
+            }
183
+
184
+        }
185
+    }
107 186
 
187
+    httpd_resp_set_hdr(req, "Location", "/");
188
+    httpd_resp_set_status(req, "302 Found");
108 189
     return httpd_resp_send(req, NULL, 0);
109 190
 
110 191
 err:

BIN
pt100_lookup.ods View File