381 lines
10 KiB
C
381 lines
10 KiB
C
/* Hello World Example
|
|
|
|
This example code is in the Public Domain (or CC0 licensed, at your option.)
|
|
|
|
Unless required by applicable law or agreed to in writing, this
|
|
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
|
CONDITIONS OF ANY KIND, either express or implied.
|
|
*/
|
|
|
|
/* ESP libraries */
|
|
#include "esp_log.h"
|
|
#include "esp_sleep.h"
|
|
#include "esp_system.h"
|
|
|
|
/* FreeRTOS */
|
|
#include "freertos/FreeRTOS.h"
|
|
#include "freertos/queue.h"
|
|
#include "freertos/task.h"
|
|
|
|
/* MISC */
|
|
#include "cbor.h"
|
|
#include "nvs_flash.h"
|
|
|
|
/* APP */
|
|
#include "app_bkp_ram.h"
|
|
#include "app_lvgl.h"
|
|
#include "app_mqtt.h"
|
|
#include "app_ui.h"
|
|
#include "app_wifi.h"
|
|
|
|
#define APP_WAKE_INTERVAL_MS (30 * 60 * 1000)
|
|
|
|
#define APP_LOG_TAG "MAIN"
|
|
|
|
RTC_NOINIT_ATTR app_bkp_ram_t g_app_bkp;
|
|
|
|
static CborError app_get_condition_string(CborValue *it, char *key, char **value, uint32_t *size);
|
|
static int app_windlevel_to_font(char *input, char **output);
|
|
static int app_condition_to_font(char *input, char **output);
|
|
static int app_update_ui(void);
|
|
|
|
void app_main(void) {
|
|
esp_err_t ret = nvs_flash_init();
|
|
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
|
|
ESP_ERROR_CHECK(nvs_flash_erase());
|
|
|
|
ret = nvs_flash_init();
|
|
|
|
ESP_LOGW(APP_LOG_TAG, "NVS content corrupted or outdated, cleared.");
|
|
}
|
|
|
|
ESP_ERROR_CHECK(ret);
|
|
|
|
if (app_wifi_init() != 0) {
|
|
goto next_round;
|
|
}
|
|
|
|
if (app_mqtt_init() != 0) {
|
|
goto next_round;
|
|
}
|
|
|
|
if (app_wifi_wait_ready(5000) != 0) {
|
|
ESP_LOGW(APP_LOG_TAG, "Failed to connect to WiFi in time, bail out.");
|
|
goto next_round;
|
|
}
|
|
|
|
if (app_lvgl_init() != 0) {
|
|
goto next_round;
|
|
}
|
|
|
|
if (app_ui_init() != 0) {
|
|
goto next_round;
|
|
}
|
|
|
|
if (app_update_ui() != 0) {
|
|
goto next_round;
|
|
}
|
|
|
|
app_lvgl_start_flush();
|
|
if(app_lvgl_wait_flush(5000) < 0) {
|
|
ESP_LOGW(APP_LOG_TAG, "Did not get flush ready flag in time, bail out.");
|
|
goto next_round;
|
|
}
|
|
|
|
next_round:
|
|
|
|
ESP_LOGI(APP_LOG_TAG, "Enter deep power down mode, interval: %d", APP_WAKE_INTERVAL_MS);
|
|
app_lvgl_deinit();
|
|
esp_deep_sleep(APP_WAKE_INTERVAL_MS * 1000);
|
|
}
|
|
|
|
static int app_update_ui(void) {
|
|
app_mqtt_subscribe("iot/weather/ESP32_Weather/response");
|
|
|
|
CborEncoder root_encoder;
|
|
uint8_t *tx_buf = malloc(100);
|
|
if (tx_buf == NULL) {
|
|
ESP_LOGE(APP_LOG_TAG, "Failed to allocate tx buffer");
|
|
|
|
return -1;
|
|
}
|
|
|
|
cbor_encoder_init(&root_encoder, tx_buf, 100, 0);
|
|
|
|
CborEncoder map_encoder;
|
|
cbor_encoder_create_map(&root_encoder, &map_encoder, 2);
|
|
|
|
cbor_encode_text_stringz(&map_encoder, "type");
|
|
cbor_encode_text_stringz(&map_encoder, "condition");
|
|
|
|
cbor_encode_text_stringz(&map_encoder, "city_id");
|
|
cbor_encode_uint(&map_encoder, 10);
|
|
|
|
cbor_encoder_close_container(&root_encoder, &map_encoder);
|
|
|
|
uint32_t data_size = cbor_encoder_get_buffer_size(&root_encoder, tx_buf);
|
|
|
|
ESP_LOGI(APP_LOG_TAG, "Encoded CBOR message %d bytes.", data_size);
|
|
|
|
app_mqtt_publish("iot/weather/ESP32_Weather/request", tx_buf, data_size);
|
|
|
|
free(tx_buf);
|
|
|
|
char *topic;
|
|
uint8_t *payload;
|
|
uint32_t payload_len;
|
|
|
|
if (app_mqtt_poll(&topic, &payload, &payload_len, 10000) != 0) {
|
|
ESP_LOGE(APP_LOG_TAG, "failed to receive payload in time, bail out");
|
|
|
|
return -2;
|
|
}
|
|
|
|
ESP_LOGI(APP_LOG_TAG, "Received %d bytes from topic %s", payload_len, topic);
|
|
|
|
CborParser root_parser;
|
|
CborValue it;
|
|
CborError cbor_ret;
|
|
|
|
cbor_ret = cbor_parser_init(payload, payload_len, 0, &root_parser, &it);
|
|
if (cbor_ret != CborNoError) {
|
|
ESP_LOGE(APP_LOG_TAG, "CBOR Parse 1");
|
|
return -3;
|
|
}
|
|
|
|
char *str;
|
|
uint32_t size;
|
|
if (app_get_condition_string(&it, "sunRise", &str, &size) == CborNoError) {
|
|
app_ui_update(APP_UI_CMD_SUNRISE, str);
|
|
free(str);
|
|
}
|
|
|
|
if (app_get_condition_string(&it, "sunSet", &str, &size) == CborNoError) {
|
|
app_ui_update(APP_UI_CMD_SUNSET, str);
|
|
free(str);
|
|
}
|
|
|
|
if (app_get_condition_string(&it, "uvi", &str, &size) == CborNoError) {
|
|
app_ui_update(APP_UI_CMD_UVI, str);
|
|
free(str);
|
|
}
|
|
|
|
if (app_get_condition_string(&it, "updatetime", &str, &size) == CborNoError) {
|
|
app_ui_update(APP_UI_CMD_UPDATED, str);
|
|
free(str);
|
|
}
|
|
|
|
if (app_get_condition_string(&it, "temp", &str, &size) == CborNoError) {
|
|
app_ui_update(APP_UI_CMD_TEMP, str);
|
|
free(str);
|
|
}
|
|
|
|
if (app_get_condition_string(&it, "humidity", &str, &size) == CborNoError) {
|
|
app_ui_update(APP_UI_CMD_HUMID, str);
|
|
free(str);
|
|
}
|
|
|
|
if (app_get_condition_string(&it, "realFeel", &str, &size) == CborNoError) {
|
|
app_ui_update(APP_UI_CMD_TEMP_REAL, str);
|
|
free(str);
|
|
}
|
|
|
|
if (app_get_condition_string(&it, "windSpeed", &str, &size) == CborNoError) {
|
|
app_ui_update(APP_UI_CMD_WIND_SPEED, str);
|
|
free(str);
|
|
}
|
|
|
|
if (app_get_condition_string(&it, "windLevel", &str, &size) == CborNoError) {
|
|
app_windlevel_to_font(str, &str);
|
|
app_ui_update(APP_UI_CMD_WIND_LEVEL, str);
|
|
free(str);
|
|
}
|
|
|
|
if (app_get_condition_string(&it, "conditionId", &str, &size) == CborNoError) {
|
|
app_condition_to_font(str, &str);
|
|
app_ui_update(APP_UI_CMD_COND, str);
|
|
free(str);
|
|
}
|
|
|
|
free(topic);
|
|
free(payload);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int app_condition_to_font(char *input, char **output) {
|
|
*output = malloc(4);
|
|
if (*output == NULL) return -1;
|
|
|
|
char *unicode = "\U0000F00D";
|
|
|
|
int wx_id = strtol(input, NULL, 10);
|
|
|
|
if (wx_id <= 5) {
|
|
/* Do nothing */
|
|
} else if (wx_id <= 7) {
|
|
unicode = "\U0000F072";
|
|
} else if (wx_id <= 11) {
|
|
unicode = "\U0000F002";
|
|
} else if (wx_id <= 12) {
|
|
unicode = "\U0000F072";
|
|
} else if (wx_id <= 14) {
|
|
unicode = "\U0000F013";
|
|
} else if (wx_id <= 23) {
|
|
unicode = "\U0000F009";
|
|
} else if (wx_id <= 25) {
|
|
unicode = "\U0000F00A";
|
|
} else if (wx_id <= 28) {
|
|
unicode = "\U0000F003";
|
|
} else if (wx_id <= 33) {
|
|
unicode = "\U0000F082";
|
|
} else if (wx_id <= 35) {
|
|
unicode = "\U0000F0B6";
|
|
} else if (wx_id <= 36) {
|
|
unicode = "\U0000F041";
|
|
} else if (wx_id <= 41) {
|
|
unicode = "\U0000F00E";
|
|
} else if (wx_id <= 43) {
|
|
unicode = "\U0000F005";
|
|
} else if (wx_id <= 45) {
|
|
unicode = "\U0000F068";
|
|
} else if (wx_id <= 48) {
|
|
unicode = "\U0000F004";
|
|
} else if (wx_id <= 50) {
|
|
unicode = "\U0000F006";
|
|
} else if (wx_id <= 57) {
|
|
unicode = "\U0000F008";
|
|
} else if (wx_id <= 63) {
|
|
unicode = "\U0000F00A";
|
|
} else if (wx_id <= 65) {
|
|
unicode = "\U0000F006";
|
|
} else if (wx_id <= 70) {
|
|
unicode = "\U0000F008";
|
|
} else if (wx_id <= 77) {
|
|
unicode = "\U0000F00A";
|
|
} else if (wx_id <= 78) {
|
|
unicode = "\U0000F008";
|
|
} else if (wx_id <= 79) {
|
|
unicode = "\U0000F0B6";
|
|
} else if (wx_id <= 82) {
|
|
unicode = "\U0000F002";
|
|
} else if (wx_id <= 84) {
|
|
unicode = "\U0000F003";
|
|
} else if (wx_id <= 85) {
|
|
unicode = "\U0000F002";
|
|
} else if (wx_id <= 86) {
|
|
unicode = "\U0000F009";
|
|
} else if (wx_id <= 90) {
|
|
unicode = "\U0000F00E";
|
|
} else if (wx_id <= 93) {
|
|
unicode = "\U0000F008";
|
|
} else if (wx_id <= 94) {
|
|
unicode = "\U0000F00A";
|
|
}
|
|
|
|
snprintf(*output, 4, "%s", unicode);
|
|
free(input);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int app_windlevel_to_font(char *input, char **output) {
|
|
*output = malloc(4);
|
|
if (*output == NULL) return -1;
|
|
|
|
char *unicode = "\U0000F0B7";
|
|
|
|
if (input[1] != '\0') {
|
|
switch (input[0]) {
|
|
case '0':
|
|
unicode = "\U0000F0C1";
|
|
break;
|
|
case '1':
|
|
unicode = "\U0000F0C2";
|
|
break;
|
|
case '2':
|
|
unicode = "\U0000F0C3";
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
} else {
|
|
switch (input[0]) {
|
|
case '0':
|
|
unicode = "\U0000F0B7";
|
|
break;
|
|
case '1':
|
|
unicode = "\U0000F0B8";
|
|
break;
|
|
case '2':
|
|
unicode = "\U0000F0B9";
|
|
break;
|
|
case '3':
|
|
unicode = "\U0000F0BA";
|
|
break;
|
|
case '4':
|
|
unicode = "\U0000F0BB";
|
|
break;
|
|
case '5':
|
|
unicode = "\U0000F0BC";
|
|
break;
|
|
case '6':
|
|
unicode = "\U0000F0BD";
|
|
break;
|
|
case '7':
|
|
unicode = "\U0000F0BE";
|
|
break;
|
|
case '8':
|
|
unicode = "\U0000F0BF";
|
|
break;
|
|
case '9':
|
|
unicode = "\U0000F0C0";
|
|
break;
|
|
}
|
|
}
|
|
|
|
snprintf(*output, 4, "%s", unicode);
|
|
free(input);
|
|
return 0;
|
|
}
|
|
|
|
static CborError app_get_condition_string(CborValue *it, char *key, char **value, uint32_t *size) {
|
|
CborError ret;
|
|
|
|
CborValue data;
|
|
ret = cbor_value_map_find_value(it, "data", &data);
|
|
if (ret != CborNoError) {
|
|
ESP_LOGE(APP_LOG_TAG, "CBOR Parse 2");
|
|
return ret;
|
|
}
|
|
|
|
if (cbor_value_get_type(&data) != CborMapType) {
|
|
ESP_LOGE(APP_LOG_TAG, "CBOR Parse 3");
|
|
return ret;
|
|
}
|
|
|
|
CborValue condition;
|
|
ret = cbor_value_map_find_value(&data, "condition", &condition);
|
|
if (ret != CborNoError) {
|
|
ESP_LOGE(APP_LOG_TAG, "CBOR Parse 4");
|
|
return ret;
|
|
}
|
|
|
|
if (cbor_value_get_type(&condition) != CborMapType) {
|
|
ESP_LOGE(APP_LOG_TAG, "CBOR Parse 5");
|
|
return ret;
|
|
}
|
|
|
|
CborValue result;
|
|
ret = cbor_value_map_find_value(&condition, key, &result);
|
|
if (ret != CborNoError) {
|
|
ESP_LOGE(APP_LOG_TAG, "CBOR Parse 6");
|
|
return ret;
|
|
}
|
|
|
|
if (cbor_value_is_text_string(&result)) {
|
|
cbor_value_dup_text_string(&result, value, (size_t *)&size, &result);
|
|
}
|
|
|
|
return ret;
|
|
} |