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 |
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; |