|
@@ -0,0 +1,189 @@
|
|
|
+#include <utility>
|
|
|
+
|
|
|
+#include <esp_log.h>
|
|
|
+
|
|
|
+#include "tasks/display_manager.h"
|
|
|
+
|
|
|
+using namespace kbf;
|
|
|
+
|
|
|
+using std::atomic;
|
|
|
+using std::string;
|
|
|
+using std::shared_ptr;
|
|
|
+using std::make_shared;
|
|
|
+
|
|
|
+static const int MAX_STATUS_LEN = 14;
|
|
|
+static const int MAX_MESSAGE_LEN = 16;
|
|
|
+
|
|
|
+std::shared_ptr<kbf::driver::LCD> DisplayManager::lcd;
|
|
|
+
|
|
|
+atomic<DisplayManager::WifiState> DisplayManager::wifiState;
|
|
|
+atomic<DisplayManager::DoorState> DisplayManager::doorState;
|
|
|
+string DisplayManager::status;
|
|
|
+string DisplayManager::message;
|
|
|
+
|
|
|
+const int DisplayManager::defaultTimeout = 2000;
|
|
|
+atomic<int> DisplayManager::timeoutMs_ = 0;
|
|
|
+
|
|
|
+shared_ptr<rtos::EventGroup> DisplayManager::eventGroup;
|
|
|
+shared_ptr<task::Task> DisplayManager::messageTimeout;
|
|
|
+
|
|
|
+DisplayManager::DisplayManager(const std::string &name, void *arg, uint32_t stackSize, uint32_t priority) :
|
|
|
+ Task(name, arg, stackSize, priority) {
|
|
|
+ ESP_LOGI(TAG, "%s()", __func__);
|
|
|
+
|
|
|
+ lcd = make_shared<driver::LCD>(32, 33, 25, 26, 27, 13); // TODO use Kconfig
|
|
|
+
|
|
|
+ wifiState = WifiState::OFFLINE;
|
|
|
+ doorState = DoorState::NOT_CALIBRATED;
|
|
|
+
|
|
|
+ eventGroup = make_shared<rtos::EventGroup>();
|
|
|
+ messageTimeout = nullptr;
|
|
|
+}
|
|
|
+
|
|
|
+void DisplayManager::start() {
|
|
|
+ ESP_LOGI(TAG, "%s()", __func__);
|
|
|
+
|
|
|
+ static auto task = task::start<DisplayManager>("dm", nullptr);
|
|
|
+}
|
|
|
+
|
|
|
+[[noreturn]] void DisplayManager::run(void *arg) {
|
|
|
+ ESP_LOGI(TAG, "%s()", __func__);
|
|
|
+
|
|
|
+ while (true) {
|
|
|
+ eventGroup->waitForAny(ALL_EVENTS);
|
|
|
+
|
|
|
+ if (eventGroup->getBit(Events::WIFI)) {
|
|
|
+ auto state = wifiState.load();
|
|
|
+ ESP_LOGI(TAG, "event: WIFI: %s",
|
|
|
+ state == WifiState::AP ? "AP" :
|
|
|
+ state == WifiState::CONNECTING ? "CONNECTING" :
|
|
|
+ state == WifiState::ONLINE ? "ONLINE" :
|
|
|
+ state == WifiState::OFFLINE ? "OFFLINE" :
|
|
|
+ "unknown");
|
|
|
+
|
|
|
+ lcd->move(15, 0);
|
|
|
+ // TODO use custom characters
|
|
|
+ switch (wifiState) {
|
|
|
+ case WifiState::AP:
|
|
|
+ lcd->print("A");
|
|
|
+ break;
|
|
|
+ case WifiState::CONNECTING:
|
|
|
+ lcd->print("C");
|
|
|
+ break;
|
|
|
+ case WifiState::ONLINE:
|
|
|
+ lcd->print("O");
|
|
|
+ break;
|
|
|
+ case WifiState::OFFLINE:
|
|
|
+ lcd->print("F");
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ eventGroup->clearBit(Events::WIFI);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (eventGroup->getBit(Events::DOOR)) {
|
|
|
+ auto state = doorState.load();
|
|
|
+ ESP_LOGI(TAG, "event: DOOR: %s",
|
|
|
+ state == DoorState::NOT_CALIBRATED ? "NOT_CALIBRATED" :
|
|
|
+ state == DoorState::OPEN ? "OPEN" :
|
|
|
+ state == DoorState::CLOSED ? "CLOSED" :
|
|
|
+ "unknown");
|
|
|
+
|
|
|
+ lcd->move(14, 0);
|
|
|
+ // TODO use custom characters
|
|
|
+ switch (doorState) {
|
|
|
+ case DoorState::NOT_CALIBRATED:
|
|
|
+ lcd->print("X");
|
|
|
+ break;
|
|
|
+ case DoorState::OPEN:
|
|
|
+ lcd->print("O");
|
|
|
+ break;
|
|
|
+ case DoorState::CLOSED:
|
|
|
+ lcd->print("C");
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ eventGroup->clearBit(Events::DOOR);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (eventGroup->getBit(Events::STATUS)) {
|
|
|
+ ESP_LOGI(TAG, "event: STATUS: \"%s\"", status.c_str());
|
|
|
+ lcd->move(0, 0);
|
|
|
+
|
|
|
+ string tmp = status.substr(0, MAX_STATUS_LEN);
|
|
|
+ if (status.length() < MAX_STATUS_LEN) {
|
|
|
+ tmp.insert(status.length(), MAX_STATUS_LEN - status.length(), ' ');
|
|
|
+ ESP_LOGD(TAG, " padded: \"%s\"", tmp.c_str());
|
|
|
+ } else if (status.length() != tmp.length()) {
|
|
|
+ ESP_LOGW(TAG, " truncated: \"%s\"", tmp.c_str());
|
|
|
+ }
|
|
|
+
|
|
|
+ lcd->print(tmp);
|
|
|
+ eventGroup->clearBit(Events::STATUS);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (eventGroup->getBit(Events::MESSAGE)) {
|
|
|
+ ESP_LOGI(TAG, "event: MESSAGE: \"%s\"", message.c_str());
|
|
|
+ lcd->move(0, 1);
|
|
|
+
|
|
|
+ string tmp = message.substr(0, MAX_MESSAGE_LEN);
|
|
|
+ if (message.length() < MAX_MESSAGE_LEN) {
|
|
|
+ tmp.insert(message.length(), MAX_MESSAGE_LEN - message.length(), ' ');
|
|
|
+ ESP_LOGD(TAG, " padded: \"%s\"", tmp.c_str());
|
|
|
+ } else if (message.length() != tmp.length()) {
|
|
|
+ ESP_LOGW(TAG, " truncated: \"%s\"", tmp.c_str());
|
|
|
+ }
|
|
|
+
|
|
|
+ lcd->print(tmp);
|
|
|
+ eventGroup->clearBit(Events::MESSAGE);
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void DisplayManager::setWifiState(DisplayManager::WifiState state) {
|
|
|
+ ESP_LOGD(TAG, "%s()", __func__);
|
|
|
+
|
|
|
+ wifiState = state;
|
|
|
+ eventGroup->setBit(Events::WIFI);
|
|
|
+}
|
|
|
+
|
|
|
+void DisplayManager::setDoorState(DisplayManager::DoorState state) {
|
|
|
+ ESP_LOGD(TAG, "%s()", __func__);
|
|
|
+
|
|
|
+ doorState = state;
|
|
|
+ eventGroup->setBit(Events::DOOR);
|
|
|
+}
|
|
|
+
|
|
|
+void DisplayManager::setStatus(std::string text) {
|
|
|
+ ESP_LOGD(TAG, "%s(\"%s\")", __func__, text.c_str());
|
|
|
+
|
|
|
+ // TODO use semaphore?
|
|
|
+ status = std::move(text);
|
|
|
+ eventGroup->setBit(Events::STATUS);
|
|
|
+}
|
|
|
+
|
|
|
+void DisplayManager::setMessage(std::string text, int timeoutMs) {
|
|
|
+ ESP_LOGD(TAG, "%s(\"%s\")", __func__, text.c_str());
|
|
|
+
|
|
|
+ if (messageTimeout && messageTimeout->running()) messageTimeout->stop();
|
|
|
+
|
|
|
+ // TODO use semaphore?
|
|
|
+ message = std::move(text);
|
|
|
+ eventGroup->setBit(Events::MESSAGE);
|
|
|
+
|
|
|
+ if (timeoutMs) {
|
|
|
+ timeoutMs_ = timeoutMs;
|
|
|
+ messageTimeout = task::start<MessageTimeout>("msg_timeout", nullptr);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void DisplayManager::MessageTimeout::run(void *arg) {
|
|
|
+ ESP_LOGI("MessageTimeout", "%s(%d)", __func__, DisplayManager::timeoutMs_.load());
|
|
|
+
|
|
|
+ kbf::sleep(timeoutMs_);
|
|
|
+
|
|
|
+ string tmp;
|
|
|
+ tmp.insert(0, MAX_MESSAGE_LEN, ' ');
|
|
|
+ lcd->move(0, 1);
|
|
|
+ lcd->print(tmp);
|
|
|
+
|
|
|
+ ESP_LOGI("MessageTimeout", "%s() finished", __func__);
|
|
|
+}
|