/* * ServiceEink.cpp * * Created on: 22 maj 2019 * Author: robert */ // FreeRTOS extern "C" { #include "FreeRTOS.h" #include "task.h" #include "queue.h" #include "timers.h" #include "semphr.h" } // module-utils #include "log/log.hpp" // module-vfs #include "vfs.hpp" #include "EinkIncludes.hpp" // eink messages #include "messages/EinkMessage.hpp" #include "messages/ImageMessage.hpp" #include "service-gui/messages/GUIMessage.hpp" #include "SystemManager/SystemManager.hpp" #include "MessageType.hpp" #include "ServiceEink.hpp" enum class EinkWorkerCommands { Initialize, Initialized, Destroy, CopyImage, CopyCompleteCallback, CopyComplete }; ServiceEink::ServiceEink(const std::string &name, std::string parent) : sys::Service(name, parent, 4096 + 1024), timerID{0}, selfRefereshTriggerCount{0}, temperatureMeasurementTriggerCount{0}, powerOffTriggerCount{0} { // initialize initial eink parameters memset(&waveformSettings, 0, sizeof(EinkWaveFormSettings_t)); waveformSettings.mode = EinkWaveformGC16; waveformSettings.temperature = -1000; timerPowerOffID = CreateTimer(3000, false); } ServiceEink::~ServiceEink() { // LOG_INFO("[ServiceEink] Cleaning resources"); // release data from settings if (waveformSettings.LUTCData != nullptr) delete[] waveformSettings.LUTCData; if (waveformSettings.LUTDData != nullptr) delete[] waveformSettings.LUTDData; // set sure that any new temperature will cause to load data from file. waveformSettings.temperature = -1000; } // Invoked upon receiving data message sys::Message_t ServiceEink::DataReceivedHandler(sys::DataMessage *msgl, sys::ResponseMessage *resp) { seink::EinkMessage *msg = static_cast(msgl); switch (msg->messageType) { case MessageType::EinkImageData: { stopTimer(timerPowerOffID); auto dmsg = static_cast(msgl); // LOG_INFO("[%s] EinkImageData", GetName().c_str()); memcpy(einkRenderBuffer, dmsg->getData(), dmsg->getSize()); deepRefresh = dmsg->getDeepRefresh(); shutdownInProgress = dmsg->getShutdown(); if (shutdownInProgress) LOG_DEBUG("Shutdown In Progress"); suspendInProgress = dmsg->getSuspend(); if (suspendInProgress) LOG_DEBUG("Suspend In Progress"); auto msg = std::make_shared(MessageType::EinkDMATransfer); sys::Bus::SendUnicast(msg, this->GetName(), this); } break; case MessageType::EinkDMATransfer: { // LOG_INFO("[%s] EinkDMATransfer", GetName().c_str()); // uint32_t start_tick = xTaskGetTickCount(); if (suspended) { if (suspendInProgress) { LOG_ERROR("drawing before suspend failed"); suspendInProgress = false; } LOG_INFO("[ServiceEink] Received image while suspended, ignoring"); } else { EinkPowerOn(); int32_t temperature = EinkGetTemperatureInternal(); // LOG_INFO("temperature: %d", temperature ); EinkStatus_e ret; if (deepRefresh) { changeWaveform(EinkWaveforms_e::EinkWaveformGC16, temperature); EinkDitherDisplay(); } else { changeWaveform(EinkWaveforms_e::EinkWaveformDU2, temperature); } ret = EinkUpdateFrame(0, 0, 480, 600, einkRenderBuffer, Eink4Bpp, EinkDisplayColorModeStandard); if (ret != EinkOK) LOG_FATAL("Failed to update frame"); if (deepRefresh) { // LOG_INFO("EinkDisplayTimingsDeepCleanMode"); ret = EinkRefreshImage(0, 0, 480, 600, EinkDisplayTimingsDeepCleanMode); } else { // LOG_INFO("EinkDisplayTimingsFastRefreshMode"); ret = EinkRefreshImage(0, 0, 480, 600, EinkDisplayTimingsFastRefreshMode); } if (ret != EinkOK) LOG_FATAL("Failed to refresh frame"); // uint32_t end_tick = xTaskGetTickCount(); ReloadTimer(timerPowerOffID); auto msg = std::make_shared(MessageType::GUIDisplayReady, suspendInProgress, shutdownInProgress); suspendInProgress = false; shutdownInProgress = false; sys::Bus::SendUnicast(msg, "ServiceGUI", this); } } break; case MessageType::EinkTemperatureUpdate: { // LOG_INFO("[%s] EinkTemperatureUpdate", GetName().c_str()); } break; case MessageType::EinkStateRequest: { // LOG_INFO("[%s] EinkStateRequest", GetName().c_str()); auto msg = std::make_shared(MessageType::GUIDisplayReady); sys::Bus::SendUnicast(msg, "ServiceGUI", this); } break; default: break; }; return std::make_shared(); } // Invoked when timer ticked void ServiceEink::TickHandler(uint32_t id) { auto findID = std::find_if(timersList.begin(), timersList.end(), [id](auto &timer) { return (timer->GetId() == id); }); if (findID != timersList.end()) { LOG_INFO("[ServiceEink] Power down eink after 3 seconds"); EinkPowerOff(); } } // Invoked during initialization sys::ReturnCodes ServiceEink::InitHandler() { LOG_INFO("[ServiceEink] Initializing"); EinkStatus_e einkStatus = EinkResetAndInitialize(); if (einkStatus != EinkOK) { LOG_FATAL("Error: Could not initialize Eink display!\n"); } // TODO remove screen clearing code below. EinkPowerOn(); // uint8_t s_einkAmbientTemperature = EinkGetTemperatureInternal(); // LOG_INFO("EInk measured temperature: %d\u00B0C", s_einkAmbientTemperature); // // // Make the saturation to the lower limit // if (s_einkAmbientTemperature < 0) // s_einkAmbientTemperature = 0; // // // Make the saturation to the higher limit // if (s_einkAmbientTemperature > 49) // s_einkAmbientTemperature = 49; // // // Clear the temperature timer count // deepClearScreen( s_einkAmbientTemperature ); // EinkPowerOff(); // // //TODO remove and add timer or turning off eink // EinkPowerOn(); auto msg = std::make_shared(MessageType::GUIDisplayReady); sys::Bus::SendUnicast(msg, "ServiceGUI", this); return sys::ReturnCodes::Success; } sys::ReturnCodes ServiceEink::DeinitHandler() { EinkPowerDown(); return sys::ReturnCodes::Success; } sys::ReturnCodes ServiceEink::SwitchPowerModeHandler(const sys::ServicePowerMode mode) { LOG_FATAL("[ServiceEink] PowerModeHandler: %s", c_str(mode)); switch (mode) { case sys::ServicePowerMode ::Active: { suspended = false; EinkStatus_e einkStatus = EinkResetAndInitialize(); if (einkStatus != EinkOK) { LOG_FATAL("Error: Could not initialize Eink display!\n"); } // TODO remove screen clearing code below. EinkPowerOn(); // int32_t temperature = EinkGetTemperatureInternal(); // // LOG_INFO("temperature: %d", temperature ); // // changeWaveform(EinkWaveforms_e::EinkWaveformGC16, temperature); // EinkDitherDisplay(); // // EinkRefreshImage (0, 0, 480, 600, EinkDisplayTimingsDeepCleanMode ); EinkPowerOff(); // // uint8_t s_einkAmbientTemperature = EinkGetTemperatureInternal(); // // Make the saturation to the lower limit // if (s_einkAmbientTemperature < 0) // s_einkAmbientTemperature = 0; // // // Make the saturation to the higher limit // if (s_einkAmbientTemperature > 49) // s_einkAmbientTemperature = 49; // // // Clear the temperature timer count // deepClearScreen( s_einkAmbientTemperature ); } break; case sys::ServicePowerMode ::SuspendToRAM: case sys::ServicePowerMode ::SuspendToNVM: suspended = true; stopTimer(timerPowerOffID); EinkPowerDown(); break; } return sys::ReturnCodes::Success; } bool ServiceEink::changeWaveform(EinkWaveforms_e mode, const int32_t temperature) { // If neither the temperature nor the waveform has changed - do nothing if ((temperature == waveformSettings.temperature) && (mode == waveformSettings.mode)) { return EinkOK; } const uint32_t LUTD_SIZE = 16385; const uint32_t LUTC_SIZE = 64; const uint32_t LUTR_SIZE = 256; ///< Needed due to \ref EINK_LUTS_FILE_PATH structure const uint32_t LUTS_TOTAL_SIZE = LUTD_SIZE + LUTC_SIZE + LUTR_SIZE; waveformSettings.temperature = temperature; waveformSettings.mode = mode; unsigned int segment = 0; if (temperature < 38) { segment = temperature / 3; } else { if (temperature < 43) { segment = 12; } else { segment = 13; } } uint32_t offset = 0; switch (mode) { case EinkWaveformINIT: offset = LUTS_TOTAL_SIZE * segment; break; case EinkWaveformA2: offset = LUTS_TOTAL_SIZE * (14 + segment); break; case EinkWaveformDU2: offset = LUTS_TOTAL_SIZE * (28 + segment); break; case EinkWaveformGLD16: offset = LUTS_TOTAL_SIZE * (42 + segment); break; case EinkWaveformGC16: default: offset = LUTS_TOTAL_SIZE * (56 + segment); break; } // Open file auto file = vfs.fopen("sys/Luts.bin", "rb"); if (file == nullptr) { LOG_FATAL("Could not find the LUTS.bin file. Returning"); return false; } // Allocate memory for the LUTD data. +1 for the EinkLUTD command for SPI transfer if (waveformSettings.LUTDData != nullptr) delete[] waveformSettings.LUTDData; waveformSettings.LUTDSize = 0; waveformSettings.LUTDData = new uint8_t[LUTD_SIZE + 1]; if (waveformSettings.LUTDData == nullptr) { LOG_ERROR("Could not allocate memory for the LUTD array"); vfs.fclose(file); return false; } // Allocate memory for the LUTC data. +1 for the EinkLUTC command for SPI transfer if (waveformSettings.LUTCData != nullptr) delete[] waveformSettings.LUTCData; waveformSettings.LUTCSize = LUTC_SIZE; waveformSettings.LUTCData = new uint8_t[LUTC_SIZE + 1]; if (waveformSettings.LUTCData == nullptr) { LOG_ERROR("Could not allocate memory for the LUTC array"); vfs.fclose(file); return false; } waveformSettings.LUTDData[0] = EinkLUTD; waveformSettings.LUTCData[0] = EinkLUTC; /// LUTD vfs.fseek(file, offset, SEEK_SET); vfs.fread(&waveformSettings.LUTDData[1], 1, LUTD_SIZE, file); uint8_t frameCount = waveformSettings.LUTDData[1] + 1; // 0x00 - 1 frame, ... , 0x0F - 16 frames waveformSettings.LUTDSize = frameCount * 64 + 1 + 1; // (frameCount * 64) - size of actual LUT; (+1) - the byte containing frameCount; (+1) - EinkLUTD command // LUTC offset += LUTD_SIZE; vfs.fseek(file, offset, SEEK_SET); vfs.fread(&waveformSettings.LUTCData[1], 1, LUTC_SIZE, file); vfs.fclose(file); EinkUpdateWaveform(&waveformSettings); return true; } bool ServiceEink::deepClearScreen(int8_t temperature) { EinkWaveforms_e wv = waveformSettings.mode; EinkPowerOn(); changeWaveform(EinkWaveforms_e::EinkWaveformA2, temperature); EinkStatus_e ret; memset(einkRenderBuffer, 15, 480 * 600); ret = EinkUpdateFrame(0, 0, 480, 600, einkRenderBuffer, Eink4Bpp, EinkDisplayColorModeStandard); if (ret != EinkOK) LOG_FATAL("Failed to update frame"); ret = EinkRefreshImage(0, 0, 480, 600, EinkDisplayTimingsFastRefreshMode); if (ret != EinkOK) LOG_FATAL("Failed to refresh frame"); for (uint32_t i = 0; i < 2; i++) { memset(einkRenderBuffer, 0, 480 * 600); ret = EinkUpdateFrame(0, 0, 480, 600, einkRenderBuffer, Eink4Bpp, EinkDisplayColorModeStandard); if (ret != EinkOK) LOG_FATAL("Failed to update frame"); ret = EinkRefreshImage(0, 0, 480, 600, EinkDisplayTimingsFastRefreshMode); if (ret != EinkOK) LOG_FATAL("Failed to refresh frame"); memset(einkRenderBuffer, 15, 480 * 600); ret = EinkUpdateFrame(0, 0, 480, 600, einkRenderBuffer, Eink4Bpp, EinkDisplayColorModeStandard); if (ret != EinkOK) LOG_FATAL("Failed to update frame"); ret = EinkRefreshImage(0, 0, 480, 600, EinkDisplayTimingsFastRefreshMode); if (ret != EinkOK) LOG_FATAL("Failed to refresh frame"); } changeWaveform(wv, temperature); EinkPowerOff(); return true; }