/**@brief Handle events from m_pwr_mgmt_timer. */ static void nrf_pwr_mgmt_timeout_handler(void * p_context) { #if NRF_PWR_MGMT_CONFIG_CPU_USAGE_MONITOR_ENABLED uint32_t delta; uint32_t ticks; uint8_t cpu_usage; ticks = app_timer_cnt_get(); UNUSED_VARIABLE(app_timer_cnt_diff_compute(ticks, m_ticks_last, &delta)); cpu_usage = 100 * (delta - m_ticks_sleeping) / delta; NRF_LOG_DEBUG("CPU Usage: %d%%\r\n", cpu_usage); if (m_max_cpu_usage < cpu_usage) { m_max_cpu_usage = cpu_usage; } m_ticks_last = ticks; m_ticks_sleeping = 0; #endif // NRF_PWR_MGMT_CONFIG_CPU_USAGE_MONITOR_ENABLED #if NRF_PWR_MGMT_CONFIG_AUTO_SHUTDOWN_RETRY if (m_sysoff_guard) { // Try to continue the shutdown procedure. nrf_pwr_mgmt_shutdown(NRF_PWR_MGMT_SHUTDOWN_CONTINUE); return; } #endif // NRF_PWR_MGMT_CONFIG_AUTO_SHUTDOWN_RETRY #if NRF_PWR_MGMT_CONFIG_STANDBY_TIMEOUT_ENABLED if (m_standby_counter < NRF_PWR_MGMT_CONFIG_STANDBY_TIMEOUT_S) { m_standby_counter++; } else { nrf_pwr_mgmt_shutdown(NRF_PWR_MGMT_SHUTDOWN_GOTO_SYSOFF); } #endif // NRF_PWR_MGMT_CONFIG_STANDBY_TIMEOUT_ENABLED }
void Node::TimerTickHandler(u16 timerMs) { passsedTimeSinceLastTimerHandler = timerMs; appTimerMs += timerMs; currentStateTimeoutMs -= timerMs; //Update our global time //TODO: should take care of PRESCALER register value as well u32 rtc1, passedTime; app_timer_cnt_get(&rtc1); app_timer_cnt_diff_compute(rtc1, globalTimeSetAt, &passedTime); globalTime += passedTime; app_timer_cnt_get(&globalTimeSetAt); //Update the time that the timestamp was last updated //FIXME: Used for debugging invalid state errors, reboot the nodes until an error occurs if (lookingForInvalidStateErrors) { if (appTimerMs > 25000) { NVIC_SystemReset(); } } //logt("TIMER", "Tick, appTimer %d, stateTimeout:%d, lastDecisionTime:%d, state:%d=>%d", appTimerMs, currentStateTimeoutMs, lastDecisionTimeMs, currentDiscoveryState, nextDiscoveryState); //Check if we should switch states because of timeouts if (nextDiscoveryState != INVALID_STATE && currentStateTimeoutMs <= 0) { //Go to the next state ChangeState(nextDiscoveryState); } //FIXME: there should be a handshake timeout if (currentLedMode == LED_MODE_CONNECTIONS) { //Now we test for blinking lights u8 countHandshake = (cm->inConnection->handshakeDone ? 1 : 0) + (cm->outConnections[0]->handshakeDone ? 1 : 0) + (cm->outConnections[1]->handshakeDone ? 1 : 0) + (cm->outConnections[2]->handshakeDone ? 1 : 0); u8 countConnected = (cm->inConnection->isConnected ? 1 : 0) + (cm->outConnections[0]->isConnected ? 1 : 0) + (cm->outConnections[1]->isConnected ? 1 : 0) + (cm->outConnections[2]->isConnected ? 1 : 0); //Check if we want to switch one off if (appTimerMs - LedRed->lastStateChangeMs >= 300) LedRed->Off(); if (appTimerMs - LedGreen->lastStateChangeMs >= 300) LedGreen->Off(); if (appTimerMs - LedBlue->lastStateChangeMs >= 300) LedBlue->Off(); if (appTimerMs % 2000 == 0 || appTimerMs % 2000 == 500 || appTimerMs % 2000 == 1000 || appTimerMs % 2000 == 1500) { u8 id = (appTimerMs % 2000) / 500; if (countHandshake == 0 && countConnected == 0) { LedRed->On(); LedRed->lastStateChangeMs = appTimerMs; } if (countConnected > id) { LedGreen->On(); LedGreen->lastStateChangeMs = appTimerMs; } if (countHandshake > id) { LedBlue->On(); LedBlue->lastStateChangeMs = appTimerMs; } } } }
bool Node::TerminalCommandHandler(string commandName, vector<string> commandArgs) { /************* SYSTEM ***************/ if (commandName == "reset") { sd_nvic_SystemReset(); } else if (commandName == "startterm") { Terminal::promptAndEchoMode = true; } else if (commandName == "stopterm") { Terminal::promptAndEchoMode = false; Logger::getInstance().disableAll(); } /************* NODE ***************/ else if (commandName == "status") { PrintStatus(); } else if (commandName == "bufferstat") { PrintBufferStatus(); } else if (commandName == "stat") { PrintSingleLineStatus(); } else if (commandName == "data") { nodeID receiverId = 0; if(commandArgs.size() > 0){ if(commandArgs[0] == "sink") receiverId = NODE_ID_SHORTEST_SINK; else if(commandArgs[0] == "hop") receiverId = NODE_ID_HOPS_BASE + 1; else receiverId = NODE_ID_BROADCAST; } //Send data over all connections connPacketData1 data; data.header.messageType = MESSAGE_TYPE_DATA_1; data.header.sender = persistentConfig.nodeId; data.header.receiver = receiverId; data.payload.length = 7; data.payload.data[0] = 1; data.payload.data[1] = 3; data.payload.data[2] = 3; bool reliable = (commandArgs.size() == 0) ? false : true; cm->SendMessageToReceiver(NULL, (u8*) &data, SIZEOF_CONN_PACKET_DATA_1, reliable); } else if(commandName == "datal") { //Send some large data that is split over messages const u8 dataLength = 45; u8 _packet[dataLength]; connPacketHeader* packet = (connPacketHeader*)_packet; packet->messageType = MESSAGE_TYPE_DATA_1; packet->receiver = 0; packet->sender = persistentConfig.nodeId; for(u32 i=0; i< dataLength-5; i++){ _packet[i+5] = i+1; } cm->SendMessageToReceiver(NULL, _packet, dataLength, true); } else if (commandName == "loss") { //Simulate connection loss this->persistentConfig.connectionLossCounter++; clusterId = this->GenerateClusterID(); this->UpdateJoinMePacket(NULL); } else if (commandName == "settime") { u64 timeStamp = (atoi(commandArgs[0].c_str()) * (u64)APP_TIMER_CLOCK_FREQ); //Set the time for our node globalTime = timeStamp; app_timer_cnt_get(&globalTimeSetAt); } else if(commandName == "gettime") { u32 rtc1; app_timer_cnt_get(&rtc1); char timestring[50]; Logger::getInstance().convertTimestampToString(globalTime, timestring); trace("Time is currently %s, setAt:%d, rtc1:%u" EOL, timestring, globalTimeSetAt, rtc1); } else if (commandName == "sendtime") { //Generate a timestamp packet and send it to all other nodes connPacketUpdateTimestamp packet; packet.header.messageType = MESSAGE_TYPE_UPDATE_TIMESTAMP; packet.header.sender = persistentConfig.nodeId; packet.header.receiver = 0; //Data must not be filled because it is set in the fillTransmitBuffers method //Because it might still take some time from filling the buffer to sending the packet //We should use the radio event or estimate the sending based on the connetion parameters //It is then received and processed in the Connectionmanager::messageReceivedCallback cm->SendMessageToReceiver(NULL, (u8*)&packet, SIZEOF_CONN_PACKET_UPDATE_TIMESTAMP, true); } else if (commandName == "discovery") { if (commandArgs.size() < 1 || commandArgs[0] == "high") { noNodesFoundCounter = 0; ChangeState(discoveryState::DISCOVERY_HIGH); } else if (commandArgs[0] == "low") { noNodesFoundCounter = 50; ChangeState(discoveryState::DISCOVERY_LOW); } else if (commandArgs[0] == "off") { ChangeState(discoveryState::DISCOVERY_OFF); } } else if (commandName == "discover") { ChangeState(discoveryState::DISCOVERY_HIGH); } else if (commandName == "discoverylow") { noNodesFoundCounter = 50; } //Trigger this to save the current node configuration else if (commandName == "savenode") { persistentConfig.reserved++; Storage::getInstance().QueuedWrite((u8*) &persistentConfig, sizeof(NodeConfiguration), 0, this); } else if (commandName == "stop") { DisableStateMachine(true); } else if (commandName == "start") { DisableStateMachine(false); } else if (commandName == "break") { Config->breakpointToggleActive = !Config->breakpointToggleActive; } else if (commandName == "connect") { for (int i = 0; i <NUM_TEST_DEVICES ; i++) { if (strcmp(commandArgs[0].c_str(), testDevices[i].name) == 0) { trace("Trying to connecting to node %s", testDevices[i].name); cm->ConnectAsMaster(testDevices[i].id, &testDevices[i].addr, 14); } } } else if (commandName == "disconnect") { if (commandArgs.size() > 0) { u8 connectionNumber = atoi(commandArgs[0].c_str()); cm->connections[connectionNumber]->Disconnect(); } } else if (commandName == "heap") { Utility::CheckFreeHeap(); return true; } else if (commandName == "modules") { for(u32 i=0; i<MAX_MODULE_COUNT; i++) { if(activeModules[i] != NULL) log("Module: %s", activeModules[i]->moduleName); } return true; } else if (commandName == "yousink") { this->persistentConfig.deviceType = deviceTypes::DEVICE_TYPE_SINK; return true; } else if (commandName == "set_nodeid") { this->persistentConfig.nodeId = atoi(commandArgs[0].c_str()); return true; } /************* UART COMMANDS ***************/ else if (commandName == "uart_set_campaign") { if (commandArgs.size() > 0){ AdvertisingController::SetScanResponseData(this, commandArgs[0]); } else { uart_error(Logger::ARGUMENTS_WRONG); } } else { return false; } return true; }
/* * Get the elapsed time since startup in milliseconds */ uint64_t get_current_time_millis(void) { return (app_timer_cnt_get() / APP_TIMER_CLOCK_FREQ) + (HEARTBEAT_TIMER_TIMEOUT * heartbeat_timeout_count); }
void ConnectionManager::fillTransmitBuffers(){ u32 err; //Fill with unreliable packets for(int i=0; i<Config->meshMaxConnections; i++) { while( connections[i]->isConnected() && connections[i]->GetPendingPackets() > 0) { bool packetCouldNotBeSent = false; bool reliable = false; u8* data = NULL; u16 dataSize = 0; if(!connections[i]->handshakeDone() && connections[i]->packetSendQueue->_numElements < 1){ //TODO: might want to use a different method then GetPendingPackets if we are not in the handshake break; } //A cluster info update is waiting, this is important to send first (but only if handshake is finished and if no split packet is eing sent) if( connections[i]->handshakeDone() && connections[i]->currentClusterInfoUpdatePacket.header.messageType != 0 && connections[i]->packetSendPosition == 0 ){ //If a clusterUpdate is available we send it immediately reliable = true; data = (u8*)&(connections[i]->currentClusterInfoUpdatePacket); dataSize = SIZEOF_CONN_PACKET_CLUSTER_INFO_UPDATE; logt("CM", "Filling CLUSTER UPDATE for CONN %u", connections[i]->connectionId); } //Pick the next packet from the packet queue else { //Get one packet from the packet queue sizedData packet = connections[i]->packetSendQueue->PeekNext(); if(packet.length > 0){ reliable = packet.data[0]; data = packet.data + 1; dataSize = packet.length - 1; } else { break; } } //Multi-part messages are only supported reliable //Switch packet to reliable if it is a multipart packet if(dataSize > MAX_DATA_SIZE_PER_WRITE) reliable = true; else ((connPacketHeader*) data)->hasMoreParts = 0; //The Next packet should be sent reliably if(reliable){ if(connections[i]->reliableBuffersFree > 0){ //Check if the packet can be transmitted in one MTU //If not, it will be sent with message splitting and reliable if(dataSize > MAX_DATA_SIZE_PER_WRITE){ //We might already have started to transmit the packet if(connections[i]->packetSendPosition != 0){ //we need to modify the data a little and build our split //message header. This does overwrite some of the old data //but that's already been transmitted connPacketSplitHeader* newHeader = (connPacketSplitHeader*)(data + connections[i]->packetSendPosition); newHeader->hasMoreParts = (dataSize - connections[i]->packetSendPosition > MAX_DATA_SIZE_PER_WRITE) ? 1: 0; newHeader->messageType = ((connPacketHeader*) data)->messageType; //We take it from the start of our packet which should still be intact //If the packet has more parts, we send a full packet, otherwise we send the remaining bits if(newHeader->hasMoreParts) dataSize = MAX_DATA_SIZE_PER_WRITE; else dataSize = dataSize - connections[i]->packetSendPosition; //Now we set the data pointer to where we left the last time minus our new header data = (u8*)newHeader; } //Or maybe this is the start of the transmission else { ((connPacketHeader*) data)->hasMoreParts = 1; //Data is alright, but dataSize must be set to its new value dataSize = MAX_DATA_SIZE_PER_WRITE; } } //Update packet timestamp as close as possible before sending it //TODO: This could be done more accurate because we receive an event when the //Packet was sent, so we could calculate the time between sending and getting the event //And send a second packet with the time difference. if(((connPacketHeader*) data)->messageType == MESSAGE_TYPE_UPDATE_TIMESTAMP){ //Add the time that it took from setting the time until it gets send u32 additionalTime; u32 rtc1; app_timer_cnt_get(&rtc1); app_timer_cnt_diff_compute(rtc1, node->globalTimeSetAt, &additionalTime); logt("NODE", "sending time:%u with prevRtc1:%u, rtc1:%u, diff:%u", (u32)node->globalTime, node->globalTimeSetAt, rtc1, additionalTime); ((connPacketUpdateTimestamp*) data)->timestamp = node->globalTime + additionalTime; /*((connPacketUpdateTimestamp*) data)->timestamp = 0; ((connPacketUpdateTimestamp*) data)->milliseconds = 0;*/ } //Finally, send the packet to the SoftDevice err = GATTController::bleWriteCharacteristic(connections[i]->connectionHandle, connections[i]->writeCharacteristicHandle, data, dataSize, true); if(err != NRF_SUCCESS) logt("ERROR", "GATT WRITE ERROR %u", err); if(err == NRF_SUCCESS){ //Consume a buffer because the packet was sent connections[i]->reliableBuffersFree--; memcpy(connections[i]->lastSentPacket, data, dataSize); //A special packet that is not from the packetqueue is emptied as soon as it is in the send buffer //If it can not be sent, the connection will be disconnected because of a timeout, so we must not await //A success event if(((connPacketHeader*)data)->messageType == MESSAGE_TYPE_CLUSTER_INFO_UPDATE){ connections[i]->ClusterUpdateSentHandler(); } else { } } else if(err == NRF_ERROR_DATA_SIZE || err == NRF_ERROR_INVALID_PARAM){ logt("ERROR", "NRF_ERROR sending %u!!!!!!!!!!!!!", err); //Drop the packet if it's faulty if(((connPacketHeader*)data)->messageType == MESSAGE_TYPE_CLUSTER_INFO_UPDATE){ logt("ERROR", "MALFORMED CLUSTER_INFO_UPDATE packet"); } else { logt("ERROR", "MALFORMED DATA PACKET FROM QUEUE"); connections[i]->packetSendQueue->DiscardNext(); } } else { //Will try to send it later packetCouldNotBeSent = true; } } else { packetCouldNotBeSent = true; } } //The next packet is to be sent unreliably if(!reliable){ if(connections[i]->unreliableBuffersFree > 0) { err = GATTController::bleWriteCharacteristic(connections[i]->connectionHandle, connections[i]->writeCharacteristicHandle, data, dataSize, false); if(err != NRF_SUCCESS) logt("ERROR", "GATT WRITE ERROR %u", err); if(err == NRF_SUCCESS){ //Consumes a send Buffer connections[i]->unreliableBuffersFree--; logt("CONN", "packet to conn %u (txfree: %d)", i, connections[i]->unreliableBuffersFree); memcpy(connections[i]->lastSentPacket, data, dataSize); } //In either case (success or faulty packet) we drop the packet if(err == NRF_SUCCESS || err == NRF_ERROR_DATA_SIZE || err == NRF_ERROR_INVALID_PARAM){ connections[i]->packetSendQueue->DiscardNext(); } else { //Will try to send it later packetCouldNotBeSent = true; } } else { packetCouldNotBeSent = true; } } //Go to next connection if a packet (either reliable or unreliable) //could not be sent because the corresponding buffers are full if(packetCouldNotBeSent) break; } } }
void ConnectionManager::messageReceivedCallback(ble_evt_t* bleEvent) { ConnectionManager* cm = ConnectionManager::getInstance(); //Handles BLE_GATTS_EVT_WRITE //FIXME: must check for reassembly buffer size, if it is bigger, a stack overflow will occur Connection* connection = cm->GetConnectionFromHandle(bleEvent->evt.gatts_evt.conn_handle); if (connection != NULL) { //TODO: At this point we should check if the write was a valid operation for the mesh //Invalid actions should cause a disconnect /*if( bleEvent->evt.gatts_evt.params.write.handle != GATTController::getMeshWriteHandle() ){ connection->Disconnect(); logt("ERROR", "Non mesh device was disconnected"); }*/ connPacketHeader* packet = (connPacketHeader*)bleEvent->evt.gatts_evt.params.write.data; //At first, some special treatment for out timestamp packet if(packet->messageType == MESSAGE_TYPE_UPDATE_TIMESTAMP) { //Set our time to the received timestamp and update the time when we've received this packet app_timer_cnt_get(&cm->node->globalTimeSetAt); cm->node->globalTime = ((connPacketUpdateTimestamp*)packet)->timestamp; logt("NODE", "time updated at:%u with timestamp:%u", cm->node->globalTimeSetAt, (u32)cm->node->globalTime); } u8 t = ((connPacketHeader*)bleEvent->evt.gatts_evt.params.write.data)->messageType; if( t != 20 && t != 21 && t != 22 && t != 23 && t != 30 && t != 31 && t != 50 && t != 51 && t != 52 && t != 53 && t != 56 && t != 57 && t != 60 && t != 61 && t != 62 && t != 80 && t != 81){ logt("ERROR", "WAAAAAAAAAAAAAHHHHH, WRONG DATAAAAAAAAAAAAAAAAA!!!!!!!!!"); } //Print packet as hex char stringBuffer[100]; Logger::getInstance().convertBufferToHexString(bleEvent->evt.gatts_evt.params.write.data, bleEvent->evt.gatts_evt.params.write.len, stringBuffer, 100); logt("CONN_DATA", "Received type %d, hasMore %d, length %d, reliable %d:", ((connPacketHeader*)bleEvent->evt.gatts_evt.params.write.data)->messageType, ((connPacketHeader*)bleEvent->evt.gatts_evt.params.write.data)->hasMoreParts, bleEvent->evt.gatts_evt.params.write.len, bleEvent->evt.gatts_evt.params.write.op); logt("CONN_DATA", "%s", stringBuffer); //Check if we need to reassemble the packet if(connection->packetReassemblyPosition == 0 && packet->hasMoreParts == 0) { //Single packet, no more data connectionPacket p; p.connectionHandle = bleEvent->evt.gatts_evt.conn_handle; p.data = bleEvent->evt.gatts_evt.params.write.data; p.dataLength = bleEvent->evt.gatts_evt.params.write.len; p.reliable = bleEvent->evt.gatts_evt.params.write.op == BLE_GATTS_OP_WRITE_CMD ? false : true; connection->ReceivePacketHandler(&p); } //First of a multipart packet, still has more parts else if(connection->packetReassemblyPosition == 0 && packet->hasMoreParts) { //Save at correct position of memcpy( connection->packetReassemblyBuffer, bleEvent->evt.gatts_evt.params.write.data, bleEvent->evt.gatts_evt.params.write.len ); connection->packetReassemblyPosition += bleEvent->evt.gatts_evt.params.write.len; //Do not notify anyone until packet is finished logt("CM", "Received first part of message"); } //Multipart packet, intermediate or last frame else if(connection->packetReassemblyPosition != 0) { memcpy( connection->packetReassemblyBuffer + connection->packetReassemblyPosition, bleEvent->evt.gatts_evt.params.write.data + SIZEOF_CONN_PACKET_SPLIT_HEADER, bleEvent->evt.gatts_evt.params.write.len - SIZEOF_CONN_PACKET_SPLIT_HEADER ); //Intermediate packet if(packet->hasMoreParts){ connection->packetReassemblyPosition += bleEvent->evt.gatts_evt.params.write.len - SIZEOF_CONN_PACKET_SPLIT_HEADER; logt("CM", "Received middle part of message"); //Final packet } else { logt("CM", "Received last part of message"); //Notify connection connectionPacket p; p.connectionHandle = bleEvent->evt.gatts_evt.conn_handle; p.data = connection->packetReassemblyBuffer; p.dataLength = bleEvent->evt.gatts_evt.params.write.len + connection->packetReassemblyPosition - SIZEOF_CONN_PACKET_SPLIT_HEADER; p.reliable = bleEvent->evt.gatts_evt.params.write.op == BLE_GATTS_OP_WRITE_CMD ? false : true; //Reset the assembly buffer connection->packetReassemblyPosition = 0; connection->ReceivePacketHandler(&p); } } } }
void scr_mngr_draw_screen(void) { scr_mngr_draw_ctx draw_ctx; draw_ctx.force_colors = false; if (scr_alert_notification_state != SCR_ALERT_NOTIFICATION_STATE_NONE) { if (scr_alert_notification_state == SCR_ALERT_NOTIFICATION_STATE_INIT) { scr_mngr_handle_event_internal(SCR_ALERT_NOTIFICATION, SCR_EVENT_INIT_SCREEN, scr_alert_notification_address); // draw alert notification screen mlcd_fb_clear(); scr_mngr_handle_event_internal(SCR_ALERT_NOTIFICATION, SCR_EVENT_DRAW_SCREEN, (uint32_t)&draw_ctx); scr_alert_notification_state = SCR_ALERT_NOTIFICATION_STATE_SHOW; } else if (scr_alert_notification_state == SCR_ALERT_NOTIFICATION_STATE_SHOW) { scr_mngr_handle_event_internal(SCR_ALERT_NOTIFICATION, SCR_EVENT_REFRESH_SCREEN, (uint32_t)&draw_ctx); } else if (scr_alert_notification_state == SCR_ALERT_NOTIFICATION_STATE_CLOSE) { scr_mngr_handle_event_internal(SCR_ALERT_NOTIFICATION, SCR_EVENT_DESTROY_SCREEN, NULL); scr_alert_notification_state = SCR_ALERT_NOTIFICATION_STATE_NONE; // draw sceen mlcd_fb_clear(); scr_mngr_handle_event(SCR_EVENT_DRAW_SCREEN, (uint32_t)&draw_ctx); } } else if (scr_notifications_state != SCR_NOTIFICATIONS_STATE_NONE) { if (scr_notifications_state == SCR_NOTIFICATIONS_STATE_INIT) { scr_mngr_handle_event_internal(SCR_NOTIFICATIONS, SCR_EVENT_INIT_SCREEN, NULL); // draw alert notification screen mlcd_fb_clear(); scr_mngr_handle_event_internal(SCR_NOTIFICATIONS, SCR_EVENT_DRAW_SCREEN, (uint32_t)&draw_ctx); scr_notifications_state = SCR_NOTIFICATIONS_STATE_SHOW; } else if (scr_notifications_state == SCR_NOTIFICATIONS_STATE_SHOW) { scr_mngr_handle_event_internal(SCR_NOTIFICATIONS, SCR_EVENT_REFRESH_SCREEN, (uint32_t)&draw_ctx); } else if (scr_notifications_state == SCR_NOTIFICATIONS_STATE_REDRAW) { mlcd_fb_clear(); scr_mngr_handle_event_internal(SCR_NOTIFICATIONS, SCR_EVENT_DRAW_SCREEN, (uint32_t)&draw_ctx); scr_notifications_state = SCR_NOTIFICATIONS_STATE_SHOW; } else if (scr_notifications_state == SCR_NOTIFICATIONS_STATE_CLOSE) { scr_mngr_handle_event_internal(SCR_NOTIFICATIONS, SCR_EVENT_DESTROY_SCREEN, NULL); scr_notifications_state = SCR_NOTIFICATIONS_STATE_NONE; // draw sceen mlcd_fb_clear(); scr_mngr_handle_event(SCR_EVENT_DRAW_SCREEN, (uint32_t)&draw_ctx); } } else { if (switch_to_screen != SCR_NOT_SET) { uint16_t old_screen = current_screen; uint16_t new_screen; // disable events current_screen = SCR_NOT_SET; do { // release memory used by old screen scr_mngr_handle_event_internal(old_screen, SCR_EVENT_DESTROY_SCREEN, NULL); new_screen = switch_to_screen; // initilize new screen scr_mngr_handle_event_internal(switch_to_screen, SCR_EVENT_INIT_SCREEN, switch_to_screen_param); old_screen = new_screen; // handle case when init redirects to other screen } while (new_screen != switch_to_screen); // draw screen mlcd_fb_clear(); #ifdef OSSW_DEBUG uint32_t start_draw_ticks; app_timer_cnt_get(&start_draw_ticks); #endif scr_mngr_handle_event_internal(switch_to_screen, SCR_EVENT_DRAW_SCREEN, (uint32_t)&draw_ctx); #ifdef OSSW_DEBUG uint32_t end_draw_ticks; uint32_t total_diff; app_timer_cnt_get(&end_draw_ticks); app_timer_cnt_diff_compute(end_draw_ticks, start_draw_ticks, &total_diff); printf("DRAW: 0x%08x\r\n", total_diff); #endif // set new screen current_screen = switch_to_screen; switch_to_screen = SCR_NOT_SET; } else { if (redraw == true) { #ifdef OSSW_DEBUG uint32_t start_draw_ticks; app_timer_cnt_get(&start_draw_ticks); #endif scr_mngr_handle_event(SCR_EVENT_REFRESH_SCREEN, (uint32_t)&draw_ctx); #ifdef OSSW_DEBUG uint32_t end_draw_ticks; uint32_t total_diff; app_timer_cnt_get(&end_draw_ticks); app_timer_cnt_diff_compute(end_draw_ticks, start_draw_ticks, &total_diff); printf("REDRAW: 0x%08x\r\n", total_diff); #endif redraw = false; } } } mlcd_fb_flush_with_param(draw_ctx.force_colors); }