app_odiin.cpp
1 #include "app_odiin.h" 2 3 #include "app_error.h" 4 #include "app_timer.h" 5 #include "nrf_drv_clock.h" 6 #include "nrf_log_ctrl.h" 7 #include "nrf_log_default_backends.h" 8 9 #include "app_settings.h" 10 #include "crypto/crypto_shared.h" 11 #include "global/global_data.h" 12 #include "fsm/app_odiin_fsm.h" 13 #include "platform/platform_battery.h" 14 #include "platform/platform_watchdog.h" 15 #include "timer/timer.h" 16 #include "usb/usb.h" 17 18 #include "app_log_module.ii" 19 20 APP_TIMER_DEF(app_odiin_1sec); 21 22 namespace app 23 { 24 namespace 25 { 26 platform_power_driver_t& power = platform_power_nrf52; 27 platform_battery_driver_t& battery = platform_battery_makerdiary; 28 platform_watchdog_driver_t& watchdog = platform_watchdog_nrf52; 29 30 // #todo: This code should be in a HAL. 31 // magic values from UF2 bootloader (see bootloader's main.c) 32 // GPREGRET and GPREGRET2 are general 33 // purpose retention registers. They are 34 // 1 byte each, and their value persists 35 // through wakeups from SYSOFF. The bootloader 36 // checks for these magic values. 37 enum DfuMagic { 38 OtaReset = 0xA8, 39 SerialOnlyReset = 0x4E, 40 Uf2Reset = 0x57, 41 Skip = 0x6d, 42 }; 43 } 44 45 ////////////////////////////////////////////////////////////////////////// 46 // Public interface 47 48 Odiin* Odiin::GetInstance() 49 { 50 static Odiin instance; 51 return &instance; 52 } 53 54 files::Fat32* Odiin::GetSdCard() 55 { 56 return GetInstance()->sdCard; 57 } 58 59 Odiin::~Odiin() 60 { 61 62 } 63 64 void Odiin::Update() 65 { 66 uint32_t ticksCurrent = timer_get_ticks(); 67 uint32_t ticksDelta = ticksCurrent - ticksPrevious; 68 float delta = timer_ticks_to_seconds(ticksDelta); 69 70 NRF_LOG_FLUSH(); 71 72 usb::device::Update(); 73 screen->Update(delta); 74 statusLed->Update(delta); 75 76 UpdateBattery(); 77 78 ticksPrevious = ticksCurrent; 79 80 watchdog.feed(); 81 82 // power update will handle our WFE/SEV 83 power.update(); 84 } 85 86 void Odiin::SetNfcTagPayload(const char* filename) 87 { 88 if (nfcTagEmulator->IsEnabled()) 89 { 90 NRF_LOG_ERROR("Cannot set payload while NFCT is enabled!"); 91 return; 92 } 93 94 files::Fat32File file; 95 if (sdCard->FileOpen(file, filename, FA_READ) == false) 96 { 97 nfcTagEmulator->SetPayload(nullptr); 98 memset(activeNfctFilename, 0, FF_MAX_LFN); 99 NRF_LOG_WARNING("Failed to open `%s` to set payload data!", NRF_LOG_PUSH((char*)filename)); 100 // todo: dispatch an error event 101 } 102 103 size_t amountRead = 0; 104 if (sdCard->FileRead(file, payload.GetRawTagMemory(), payload.TagMemoryCapacity, &amountRead)) 105 { 106 // read successful. load it up and away we go! 107 nfcTagEmulator->SetPayload(&payload); 108 snprintf(activeNfctFilename, FF_MAX_LFN, "%s", filename); 109 NRF_LOG_INFO("NFCT payload set to `%s`", NRF_LOG_PUSH(activeNfctFilename)); 110 SendEvent(app::fsm::NfctActivateEvent()); 111 } 112 else 113 { 114 nfcTagEmulator->SetPayload(nullptr); 115 memset(activeNfctFilename, 0, FF_MAX_LFN); 116 NRF_LOG_WARNING("Failed to read `%s` to set payload data!", NRF_LOG_PUSH((char*)filename)); 117 // todo: dispatch an error event 118 } 119 } 120 121 void Odiin::SetNfcTagEnabled(bool enabled) 122 { 123 if (enabled) 124 { 125 nfcTagEmulator->EnableTag(); 126 } 127 else 128 { 129 nfcTagEmulator->DisableTag(); 130 } 131 } 132 133 const char* Odiin::GetActiveNfcTagPayloadFilename() 134 { 135 return activeNfctFilename; 136 } 137 138 ////////////////////////////////////////////////////////////////////////// 139 // Settings 140 141 void Odiin::SetBacklightBrightness(float level) 142 { 143 if (level < 0.01f) 144 { 145 level = 0.01f; 146 } 147 148 SETTINGS.backlight_brightness = (uint8_t)(UINT8_MAX * level); 149 screen->SetBacklightBrightness(level); 150 } 151 152 void Odiin::LoadSettings() 153 { 154 if (app::settings::Load(*flash)) 155 { 156 NRF_LOG_INFO("Settings loaded!"); 157 } 158 else 159 { 160 NRF_LOG_ERROR("Failed to load settings. Will try to save default settings."); 161 SaveSettings(); 162 } 163 } 164 165 void Odiin::SaveSettings() 166 { 167 if (app::settings::Save(*flash)) 168 { 169 NRF_LOG_INFO("Settings saved!"); 170 } 171 else 172 { 173 NRF_LOG_ERROR("Failed to save settings. Is the flash device working?"); 174 // we probably should panic, but lets try to continue... 175 } 176 } 177 178 ////////////////////////////////////////////////////////////////////////// 179 // USB Listener interface 180 void Odiin::UsbWillEnable(app_usbd_event_type_t event) 181 { 182 app::fsm::UsbConnectionEvent uce = { 183 .IsConnected = true 184 }; 185 StateMachine::dispatch(uce); 186 } 187 188 void Odiin::UsbDidDisable(app_usbd_event_type_t event) 189 { 190 app::fsm::UsbConnectionEvent uce = { 191 .IsConnected = false 192 }; 193 StateMachine::dispatch(uce); 194 } 195 196 ////////////////////////////////////////////////////////////////////////// 197 // Private interface 198 199 ////////////////////////////////////////////////////////////////////////// 200 // Application component initilization 201 202 Odiin::Odiin() 203 { 204 NRF_LOG_RAW_INFO("=====================================\n"); 205 NRF_LOG_INFO("Odiin initializing..."); 206 207 InitializeLogs(); 208 InitializeClocks(); 209 InitializeTimers(); 210 InitializeBsp(); 211 InitializePower(); 212 InitializeFlash(); 213 LoadSettings(); 214 InitializeCrypto(); 215 InitializeUsbDevice(); 216 InitializeSdCard(); 217 InitializeInput(); 218 InitializeScreen(); 219 InitializeLeds(); 220 InitializeNfcTag(); 221 222 NRF_LOG_INFO("Odiin initialization complete " NRF_LOG_FLOAT_MARKER " seconds. Starting application.", NRF_LOG_FLOAT(timer_get_elapsed_seconds())); 223 224 StartApplication(); 225 226 NRF_LOG_RAW_INFO("=====================================\n"); 227 228 // prevent a giant time delta on the first frame, 229 // by initializing the timer to the current amount of ticks. 230 ticksPrevious = timer_get_ticks(); 231 } 232 233 void Odiin::InitializeLogs() 234 { 235 ret_code_t err_code = NRF_LOG_INIT(app_usbd_sof_timestamp_get); 236 APP_ERROR_CHECK(err_code); 237 238 NRF_LOG_DEFAULT_BACKENDS_INIT(); 239 } 240 241 void Odiin::InitializeClocks() 242 { 243 // initialize nrf clock module. (calls nrfx_clock_init inside.) 244 ret_code_t ret = nrf_drv_clock_init(); 245 APP_ERROR_CHECK(ret); 246 } 247 248 void Odiin::InitializeTimers() 249 { 250 ret_code_t err_code = app_timer_init(); 251 APP_ERROR_CHECK(err_code); 252 253 // initialize the frame timer based on the rtc 254 timer_initialize(); 255 timer_start(); 256 257 // initialize the 1-sec tick timer 258 err_code = app_timer_create(&app_odiin_1sec, 259 APP_TIMER_MODE_REPEATED, 260 [](void* ctx){ ((app::Odiin*)ctx)->TimerTick1Sec(); }); 261 262 APP_ERROR_CHECK(err_code); 263 constexpr uint32_t ticks1SecMs = 1000; 264 constexpr uint32_t ticks1Sec = APP_TIMER_TICKS(ticks1SecMs); 265 app_timer_start(app_odiin_1sec, ticks1Sec, this); 266 267 // initialize the watchdog timer 268 platform_watchdog_driver_config_t wdt_config; 269 watchdog.initialize(&wdt_config); 270 watchdog.enable(); 271 } 272 273 void Odiin::InitializeBsp() 274 { 275 uint32_t err_code; 276 277 const uint32_t bspFlags = BSP_INIT_LEDS | BSP_INIT_BUTTONS; 278 279 err_code = bsp_init(bspFlags, Odiin::BspEventHandler); 280 APP_ERROR_CHECK(err_code); 281 } 282 283 void Odiin::InitializePower() 284 { 285 power.event_handler = Odiin::ShutdownHandler; 286 power.initialize(); 287 288 battery.initialize(); 289 // battery is initialized, we want to update at first chance 290 forceUpdateBattery = true; 291 } 292 293 void Odiin::InitializeFlash() 294 { 295 static files::Littlefs fs; 296 flash = &fs; 297 if (flash->Mount() == false) 298 { 299 NRF_LOG_WARNING("Flash failed to mount!"); 300 } 301 } 302 303 void Odiin::InitializeCrypto() // crypto means cryptography 304 { 305 crypto::Initialize(); 306 } 307 308 void Odiin::InitializeUsbDevice() 309 { 310 usb::device::Initialize(); 311 static usb::MassStorageClass msc; 312 usbMassStorageClass = &msc; 313 usbMassStorageClass->RegisterClass(); 314 usb::device::Enable(); 315 316 // We'd like to hear about USB events. 317 usb::device::RegisterListener(this); 318 } 319 320 void Odiin::InitializeSdCard() 321 { 322 static files::Fat32 sdc; 323 sdCard = &sdc; 324 325 // we will go ahead and start mounted 326 sdCard->Mount(); 327 328 // SDCARD will want to listen for USB events. 329 usb::device::RegisterListener(sdCard); 330 } 331 332 void Odiin::InitializeInput() 333 { 334 static input::Keypad kp; 335 keypad = &kp; 336 } 337 338 void Odiin::InitializeScreen() 339 { 340 float backlightBrightness = (SETTINGS.backlight_brightness / (float)UINT8_MAX); 341 static display::Screen scr(keypad, backlightBrightness); 342 screen = &scr; 343 } 344 345 void Odiin::InitializeLeds() 346 { 347 static StatusLed led; 348 statusLed = &led; 349 statusLed->SetModeBoot(); 350 } 351 352 void Odiin::InitializeNfcTag() 353 { 354 static NfcTagEmulator tagEmu; 355 nfcTagEmulator = &tagEmu; 356 357 nfcTagEmulator->Initialize(); 358 nfcTagEmulator->SetPasswordAuthenticationAckResponse( 359 SETTINGS.ntag_default_password_authentication_acknowledgement); 360 } 361 362 void Odiin::StartApplication() 363 { 364 // and then... 365 // let's get down to business! 366 // to defeat 367 // the punssss. 368 StateMachine::SetOdiin(this); 369 StateMachine::start(); 370 } 371 372 ////////////////////////////////////////////////////////////////////////// 373 // Timer Handlers 374 void Odiin::TimerTick1Sec() 375 { 376 updateBattery = true; 377 } 378 379 ////////////////////////////////////////////////////////////////////////// 380 // Private Updaters 381 382 void Odiin::UpdateBattery() 383 { 384 constexpr size_t SAMPLE_COUNT = 10; 385 static uint32_t sample = 0; 386 static uint8_t numSamples = 0; 387 388 if (updateBattery || forceUpdateBattery) 389 { 390 updateBattery = false; 391 392 battery.update(); 393 uint8_t percent = battery.get_battery_state_of_charge(); 394 sample += percent; 395 numSamples++; 396 397 platform_battery_state_t batteryState = battery.get_battery_state(); 398 bool newIsCharging = batteryState == PLATFORM_BATTERY_STATE_CHARGING; 399 400 // did our charging status change? if so, force update! 401 if (batteryIsCharging != newIsCharging) 402 { 403 forceUpdateBattery = true; 404 } 405 406 batteryIsCharging = newIsCharging; 407 408 if (numSamples >= SAMPLE_COUNT || forceUpdateBattery) 409 { 410 forceUpdateBattery = false; 411 412 // calculate the average by the number of samples we've taken. 413 batteryStateOfCharge = (sample / numSamples); 414 415 #if defined(ODIIN_VERBOSE_LOGGING) && ODIIN_VERBOSE_LOGGING == 1 416 uint16_t currentMilliVolts = battery.get_battery_voltage(); 417 const char* chargingText = ""; 418 419 switch (batteryState) 420 { 421 case PLATFORM_BATTERY_STATE_CHARGING: 422 chargingText = "charging"; 423 break; 424 case PLATFORM_BATTERY_CAN_CHARGE: 425 chargingText = "charge possible"; 426 break; 427 case PLATFORM_BATTERY_CAN_NOT_CHARGE: 428 chargingText = "can't charge"; 429 break; 430 case PLATFORM_BATTERY_CAN_CHARGE_UNKNOWN: 431 chargingText = "unknown state"; 432 break; 433 } 434 435 NRF_LOG_VERBOSE("[Bat]: %d%% [%s] (%d mV)", average, chargingText, currentMilliVolts); 436 #endif // ODIIN_VERBOSE_LOGGING 437 438 // reset samples 439 numSamples = 0; 440 sample = 0; 441 442 // update ui 443 screen->SetBatteryStatus(batteryStateOfCharge, batteryIsCharging); 444 } 445 } 446 } 447 448 ////////////////////////////////////////////////////////////////////////// 449 // Power Event Triggers 450 451 void Odiin::Sleep() 452 { 453 power.shutdown(PLATFORM_POWER_SLEEP); 454 } 455 456 void Odiin::PowerOff() 457 { 458 power.shutdown(PLATFORM_POWER_OFF); 459 } 460 461 void Odiin::RebootToDfu() 462 { 463 power.shutdown(PLATFORM_POWER_DFU); 464 } 465 466 void Odiin::Reboot() 467 { 468 power.shutdown(PLATFORM_POWER_RESET); 469 } 470 471 472 ////////////////////////////////////////////////////////////////////////// 473 // Power Event Handlers 474 475 bool Odiin::OnSleep() 476 { 477 NRF_LOG_WARNING("Device going to sleep."); 478 screen->BacklightOffImmediate(); 479 screen->DisplaySleep(); 480 statusLed->SetModeShutdown(); 481 482 // tell the bootloader that we want to come right back to the app. 483 NRF_POWER->GPREGRET = DfuMagic::Skip; 484 485 return true; 486 } 487 488 bool Odiin::OnPowerOff() 489 { 490 NRF_LOG_WARNING("Device will power OFF!"); 491 return true; 492 } 493 494 bool Odiin::OnRebootToDfu() 495 { 496 NRF_LOG_WARNING("Device rebooting to DFU!"); 497 498 // tell the bootloader to boot into uf2 dfu mode 499 NRF_POWER->GPREGRET = DfuMagic::Uf2Reset; 500 501 return true; 502 } 503 504 bool Odiin::OnReboot() 505 { 506 NRF_LOG_WARNING("Device rebooting!"); 507 508 // tell the bootloader that we want to come right back to the app. 509 NRF_POWER->GPREGRET = DfuMagic::Skip; 510 return true; 511 } 512 513 void Odiin::BspEventHandler(bsp_event_t event) 514 { 515 switch (event) 516 { 517 // any key events (button presses) from 518 // our regular buttons, we will feed 519 // the power management. this resets 520 // the sleep timer, and keeps the device active. 521 case BSP_EVENT_KEY_0: 522 case BSP_EVENT_KEY_1: 523 case BSP_EVENT_KEY_2: 524 case BSP_EVENT_KEY_3: 525 case BSP_EVENT_KEY_4: 526 case BSP_EVENT_KEY_5: 527 case BSP_EVENT_KEY_6: 528 power.feed(); 529 break; 530 default: 531 NRF_LOG_DEBUG("[BSP] Unhandled event: %d", event); 532 break; 533 } 534 } 535 536 bool Odiin::ShutdownHandler(platform_power_event_t event) 537 { 538 switch (event) 539 { 540 case PLATFORM_POWER_EVENT_PREPARE_WAKEUP: 541 return Odiin::GetInstance()->OnSleep(); 542 case PLATFORM_POWER_EVENT_PREPARE_OFF: 543 return Odiin::GetInstance()->OnPowerOff(); 544 case PLATFORM_POWER_EVENT_PREPARE_DFU: 545 return Odiin::GetInstance()->OnRebootToDfu(); 546 case PLATFORM_POWER_EVENT_PREPARE_RESET: 547 return Odiin::GetInstance()->OnReboot(); 548 default: 549 NRF_LOG_WARNING("Unhandled shutdown event: %d", event); 550 return true; 551 } 552 } 553 } // namespace app