main/co2meter.c

changeset 17
f9eca4a55911
parent 16
e38ffa806e84
child 18
d969e0fe05dc
equal deleted inserted replaced
16:e38ffa806e84 17:f9eca4a55911
1 /* 1 /**
2 * co2meter project. 2 * @file co2meter.c
3 * @brief co2meter project.
3 */ 4 */
4 5
5 #include "config.h" 6 #include "config.h"
6 7
7 static const char *TAG = "co2meter"; 8 static const char *TAG = "co2meter";
10 #define PIN_SCL (CONFIG_I2C_MASTER_SCL) 11 #define PIN_SCL (CONFIG_I2C_MASTER_SCL)
11 #define ROT_ENC_A_GPIO (CONFIG_ROT_ENC_A_GPIO) 12 #define ROT_ENC_A_GPIO (CONFIG_ROT_ENC_A_GPIO)
12 #define ROT_ENC_B_GPIO (CONFIG_ROT_ENC_B_GPIO) 13 #define ROT_ENC_B_GPIO (CONFIG_ROT_ENC_B_GPIO)
13 #define ROT_ENC_SW_GPIO (CONFIG_ROT_ENC_SW_GPIO) 14 #define ROT_ENC_SW_GPIO (CONFIG_ROT_ENC_SW_GPIO)
14 #define INACTIVITY 480 ///< Time in 250 mSec units. 15 #define INACTIVITY 480 ///< Time in 250 mSec units.
15
16 #define RESET_AT 0 ///< Set to a positive non-zero number to reset the position if this value is exceeded
17 16
18 17
19 int Main_Loop1 = ML1_INIT; ///< Loop 1 init 18 int Main_Loop1 = ML1_INIT; ///< Loop 1 init
20 int Main_Loop2 = -1; ///< Loop 2 invalid 19 int Main_Loop2 = -1; ///< Loop 2 invalid
21 int New_Loop2 = ML2_DONE; ///< Loop 2 new state 20 int New_Loop2 = ML2_DONE; ///< Loop 2 new state
43 static xQueueHandle gpio_evt_queue = NULL; ///< Rotary pushbutton queue 42 static xQueueHandle gpio_evt_queue = NULL; ///< Rotary pushbutton queue
44 static int usertimer = 0; ///< User inactive timeout 43 static int usertimer = 0; ///< User inactive timeout
45 44
46 45
47 46
47 /**
48 * @brief Write a menu line on the display.
49 * @param bright Display the line with a bold or normal font.
50 * @param x The horizontal start position of the line.
51 * @param y The vertical bottom of the line position.
52 * @param format The formatted data to display.
53 */
54 void menu_line(int bright, int x, int y, const char *format, ...)
55 {
56 char buf[65];
57 va_list va_ptr;
58
59 if (bright)
60 u8g2_SetFont(&u8g2, u8g2_font_t0_12b_tr);
61 else
62 u8g2_SetFont(&u8g2, u8g2_font_t0_12_tr);
63
64 va_start(va_ptr, format);
65 vsnprintf(buf, 65, format, va_ptr);
66 va_end(va_ptr);
67
68 u8g2_DrawStr(&u8g2, x, y, buf);
69 }
70
71
72
73 /**
74 * @brief Clear the display and prepare the top of the display.
75 * @format The formatted data to display at the top.
76 */
77 void screen_top(const char *format, ...)
78 {
79 char buf[65];
80 va_list va_ptr;
81
82 va_start(va_ptr, format);
83 vsnprintf(buf, 65, format, va_ptr);
84 va_end(va_ptr);
85
86 u8g2_ClearBuffer(&u8g2);
87 u8g2_DrawHLine(&u8g2, 0, 14, 128);
88
89 u8g2_SetFont(&u8g2, u8g2_font_t0_15_tr);
90 u8g2_uint_t w = u8g2_GetStrWidth(&u8g2, buf);
91 u8g2_DrawStr(&u8g2, (128 - w) / 2,12, buf);
92 }
93
94
95
48 void screen_main() 96 void screen_main()
49 { 97 {
50 char buf[65]; 98 char buf[65];
51 int i; 99 int i;
52 100
53 u8g2_ClearBuffer(&u8g2); 101 screen_top("CO2 meter %s", app_desc->version);
54 u8g2_DrawHLine(&u8g2, 0, 14, 128);
55 u8g2_SetFont(&u8g2, u8g2_font_t0_15_tr);
56 sprintf(buf, "CO2 meter %s", app_desc->version);
57 u8g2_uint_t w = u8g2_GetStrWidth(&u8g2, buf);
58 u8g2_DrawStr(&u8g2, (128 - w) / 2,12, buf);
59 102
60 if (xSemaphoreTake(xSemaphoreUnits, 25) == pdTRUE) { 103 if (xSemaphoreTake(xSemaphoreUnits, 25) == pdTRUE) {
61 104
62 u8g2_SetFont(&u8g2, u8g2_font_t0_22b_tf); 105 u8g2_SetFont(&u8g2, u8g2_font_t0_22b_tf);
63 sprintf(buf, "%.1f °C", units[0].temperature / 1000.0); 106 sprintf(buf, "%.1f °C", units[0].temperature / 1000.0);
64 w = u8g2_GetUTF8Width(&u8g2, buf); 107 u8g2_uint_t w = u8g2_GetUTF8Width(&u8g2, buf);
65 u8g2_DrawUTF8(&u8g2, (128 - w) / 2,40, buf); 108 u8g2_DrawUTF8(&u8g2, (128 - w) / 2,40, buf);
66 u8g2_SetFont(&u8g2, u8g2_font_t0_18b_tf); 109 u8g2_SetFont(&u8g2, u8g2_font_t0_18b_tf);
67 110
68 for (i = 0; i < 3; i++) { 111 for (i = 0; i < 3; i++) {
69 sprintf(buf, "%.1f", units[i].pressure / 1000.0); 112 sprintf(buf, "%.1f", units[i].pressure / 1000.0);
80 123
81 void screen_unit(int no) 124 void screen_unit(int no)
82 { 125 {
83 char buf[65]; 126 char buf[65];
84 127
85 u8g2_ClearBuffer(&u8g2);
86 u8g2_DrawHLine(&u8g2, 0, 14, 128);
87 u8g2_SetFont(&u8g2, u8g2_font_t0_15_tr);
88 sprintf(buf, "Unit %d", no + 1);
89 u8g2_uint_t w = u8g2_GetStrWidth(&u8g2, buf);
90 u8g2_DrawStr(&u8g2, (128 - w) / 2,12, buf);
91
92 if (xSemaphoreTake(xSemaphoreUnits, 25) == pdTRUE) { 128 if (xSemaphoreTake(xSemaphoreUnits, 25) == pdTRUE) {
129
130 screen_top("Unit %d %s", no + 1, units[no].mode ? "On":"Off");
93 131
94 u8g2_SetFont(&u8g2, u8g2_font_t0_22b_tf); 132 u8g2_SetFont(&u8g2, u8g2_font_t0_22b_tf);
95 sprintf(buf, "%.1f °C", units[no].temperature / 1000.0); 133 sprintf(buf, "%.1f °C", units[no].temperature / 1000.0);
96 w = u8g2_GetUTF8Width(&u8g2, buf); 134 u8g2_uint_t w = u8g2_GetUTF8Width(&u8g2, buf);
97 u8g2_DrawUTF8(&u8g2, (128 - w) / 2,40, buf); 135 u8g2_DrawUTF8(&u8g2, (128 - w) / 2,40, buf);
98 // u8g2_SetFont(&u8g2, u8g2_font_t0_18b_tf);
99 136
100 sprintf(buf, "%.2f bar", units[no].pressure / 1000.0); 137 sprintf(buf, "%.2f bar", units[no].pressure / 1000.0);
101 w = u8g2_GetUTF8Width(&u8g2, buf); 138 w = u8g2_GetUTF8Width(&u8g2, buf);
102 u8g2_DrawUTF8(&u8g2, (128 - w) / 2,63, buf); 139 u8g2_DrawUTF8(&u8g2, (128 - w) / 2,63, buf);
103 140
109 146
110 147
111 148
112 void screen_unit_setup(int no, int sub) 149 void screen_unit_setup(int no, int sub)
113 { 150 {
114 char buf[65]; 151 screen_top("Unit %d setup", no + 1);
115 152 menu_line(sub == 0, 2, 25, "Mode %s", units[no].mode ? "ON":"OFF");
116 u8g2_ClearBuffer(&u8g2); 153 menu_line(sub == 1, 2, 37, "Zero mV %d", units[no].pressure_zero);
117 u8g2_DrawHLine(&u8g2, 0, 14, 128); 154 menu_line(sub == 2, 2, 49, "DS18B20 %s", units[no].temperature_rom_code);
118 u8g2_SetFont(&u8g2, u8g2_font_t0_15_tr); 155 menu_line(sub == 3, 2, 61, "Return");
119 sprintf(buf, "Unit %d setup", no + 1);
120 u8g2_uint_t w = u8g2_GetStrWidth(&u8g2, buf);
121 u8g2_DrawStr(&u8g2, (128 - w) / 2,12, buf);
122
123 if (sub == 0)
124 u8g2_SetFont(&u8g2, u8g2_font_t0_15b_tr);
125 else
126 u8g2_SetFont(&u8g2, u8g2_font_t0_15_tr);
127 sprintf(buf, "Mode %s", units[no].mode ? "ON":"OFF");
128 u8g2_DrawStr(&u8g2,2,28, buf);
129
130 if (sub == 1)
131 u8g2_SetFont(&u8g2, u8g2_font_t0_15b_tr);
132 else
133 u8g2_SetFont(&u8g2, u8g2_font_t0_15_tr);
134 sprintf(buf, "Calibrate");
135 u8g2_DrawStr(&u8g2,2,40, buf);
136
137 if (sub == 2)
138 u8g2_SetFont(&u8g2, u8g2_font_t0_15b_tr);
139 else
140 u8g2_SetFont(&u8g2, u8g2_font_t0_15_tr);
141 sprintf(buf, "T.sensor ");
142 u8g2_DrawStr(&u8g2,2,52, buf);
143
144 if (sub == 3)
145 u8g2_SetFont(&u8g2, u8g2_font_t0_15b_tr);
146 else
147 u8g2_SetFont(&u8g2, u8g2_font_t0_15_tr);
148 sprintf(buf, "Return");
149 u8g2_DrawStr(&u8g2,2,64, buf);
150 156
151 u8g2_SendBuffer(&u8g2); 157 u8g2_SendBuffer(&u8g2);
152 u8g2_SetPowerSave(&u8g2, 0); 158 u8g2_SetPowerSave(&u8g2, 0);
153 } 159 }
154 160
522 Main_Loop2 = New_Loop2; 528 Main_Loop2 = New_Loop2;
523 529
524 switch (Main_Loop2) { 530 switch (Main_Loop2) {
525 case ML2_INIT: 531 case ML2_INIT:
526 ESP_LOGI(TAG, "Loop user: Init"); 532 ESP_LOGI(TAG, "Loop user: Init");
527 // u8g2_SetPowerSave(&u8g2, 0); // wake up display
528 // u8g2_ClearBuffer(&u8g2);
529 New_Loop2 = ML2_USER; 533 New_Loop2 = ML2_USER;
530 usertimer = INACTIVITY; 534 usertimer = INACTIVITY;
531 break; 535 break;
532 536
533 case ML2_USER: 537 case ML2_USER:
579 583
580 /* 584 /*
581 * Main user processing. Handle the rotary encoder and pushbutton. 585 * Main user processing. Handle the rotary encoder and pushbutton.
582 */ 586 */
583 if (Main_Loop2 < ML2_INACTIVE) { 587 if (Main_Loop2 < ML2_INACTIVE) {
584 // If wakeup from GPIO -- state machine 2
585 // Init OLED
586 // If not configured, start configure 588 // If not configured, start configure
587 // If configured select first unit 589 // If configured select first unit
588 // New rotate position, set screen, reset waittimer
589 // Handle screen (first is show measured values) 590 // Handle screen (first is show measured values)
590 // Count inactivity 591
591 // flag if inactive and OLED lowpower.
592
593 // Generic display all units at once. Press is xxx? Bold/Italic is selected.
594 // Display per unit. Temp + Pressure + state. Press is setup this sensor. 592 // Display per unit. Temp + Pressure + state. Press is setup this sensor.
595 // Setup menu: Sensors 593 // Setup menu: Sensors
596 // WiFi 594 // WiFi
597 // MQTT 595 // MQTT
598 // System (timers etc) 596 // System (timers etc)
617 case ML2_SETUP_UNIT1: 615 case ML2_SETUP_UNIT1:
618 case ML2_SETUP_UNIT2: 616 case ML2_SETUP_UNIT2:
619 case ML2_SETUP_UNIT3: if (event.state.position > 0) { 617 case ML2_SETUP_UNIT3: if (event.state.position > 0) {
620 if (sub < 3) { 618 if (sub < 3) {
621 sub++; 619 sub++;
622 screen_unit_setup(Main_Loop2 - ML2_SETUP_UNIT1, sub); 620 } else {
621 sub = 0;
623 } 622 }
623 screen_unit_setup(Main_Loop2 - ML2_SETUP_UNIT1, sub);
624 ESP_ERROR_CHECK(rotary_encoder_reset(&rinfo)); 624 ESP_ERROR_CHECK(rotary_encoder_reset(&rinfo));
625 } else if (event.state.position < 0) { 625 } else if (event.state.position < 0) {
626 if (sub > 0) { 626 if (sub > 0) {
627 sub--; 627 sub--;
628 screen_unit_setup(Main_Loop2 - ML2_SETUP_UNIT1, sub); 628 } else {
629 sub = 3;
629 } 630 }
631 screen_unit_setup(Main_Loop2 - ML2_SETUP_UNIT1, sub);
630 ESP_ERROR_CHECK(rotary_encoder_reset(&rinfo)); 632 ESP_ERROR_CHECK(rotary_encoder_reset(&rinfo));
631 } 633 }
632 break; 634 break;
633 default: 635 default:
634 ESP_LOGI(TAG, "Event: position %d, direction %s", event.state.position, 636 ESP_LOGI(TAG, "Event: position %d, direction %s", event.state.position,
635 event.state.direction ? (event.state.direction == ROTARY_ENCODER_DIRECTION_CLOCKWISE ? "CW":"CCW"):"NOT_SET"); 637 event.state.direction ? (event.state.direction == ROTARY_ENCODER_DIRECTION_CLOCKWISE ? "CW":"CCW"):"NOT_SET");
636 } 638 }
637 } else { 639 } else {
638 // Poll current position and direction 640 // Poll current position and direction
639 rotary_encoder_state_t state = { 0 }; 641 // rotary_encoder_state_t state = { 0 };
640 ESP_ERROR_CHECK(rotary_encoder_get_state(&rinfo, &state)); 642 // ESP_ERROR_CHECK(rotary_encoder_get_state(&rinfo, &state));
641 643
642 // ESP_LOGI(TAG, "Poll: position %d, direction %s timer %d", state.position, 644 // ESP_LOGI(TAG, "Poll: position %d, direction %s timer %d", state.position,
643 // state.direction ? (state.direction == ROTARY_ENCODER_DIRECTION_CLOCKWISE ? "CW" : "CCW") : "NOT_SET", usertimer); 645 // state.direction ? (state.direction == ROTARY_ENCODER_DIRECTION_CLOCKWISE ? "CW" : "CCW") : "NOT_SET", usertimer);
644 if (usertimer) 646 if (usertimer) {
645 usertimer--; 647 usertimer--;
646 else 648 if ((usertimer % 240) == 0) { // Each minute
649 ESP_LOGI(TAG, "usertimer %d", usertimer);
650 if (Main_Loop1 == ML1_DONE)
651 Main_Loop1 = ML1_INIT;
652 }
653 } else
647 New_Loop2 = ML2_INACTIVE; 654 New_Loop2 = ML2_INACTIVE;
648
649 // Reset the device
650 // if (RESET_AT && (state.position >= RESET_AT || state.position <= -RESET_AT)) {
651 // ESP_LOGI(TAG, "Reset");
652 // ESP_ERROR_CHECK(rotary_encoder_reset(&rinfo));
653 // }
654 } 655 }
655 } 656 }
656 657
657 switch (Main_Loop2) { 658 switch (Main_Loop2) {
658 // Break if all done and inactive. 659 // Break if all done and inactive.
697 break; 698 break;
698 699
699 vTaskDelay(10 / portTICK_PERIOD_MS); 700 vTaskDelay(10 / portTICK_PERIOD_MS);
700 } 701 }
701 702
702 // u8g2_ClearBuffer(&u8g2); 703 ESP_LOGI(TAG, "Entering deep sleep");
703 // u8g2_SendBuffer(&u8g2);
704 // u8g2_SetPowerSave(&u8g2, 1);
705
706 // printf("Simulate deep sleep\n");
707 // vTaskDelay(1000 * wakeup_time_sec / portTICK_PERIOD_MS);
708
709 printf("Entering deep sleep\n");
710 gettimeofday(&sleep_enter_time, NULL); 704 gettimeofday(&sleep_enter_time, NULL);
711 esp_deep_sleep_start(); 705 esp_deep_sleep_start();
712 706
713 Main_Loop1 = ML1_INIT; 707 Main_Loop1 = ML1_INIT;
714 New_Loop2 = ML2_INIT; 708 New_Loop2 = ML2_INIT;

mercurial