1.6 PRIME Service Dual Modem

The PRIME Service Dual Modem application implements the interface between the PRIME Library and an external device. This interface allows accessing the PRIME API through serial interface.

Serial messages are sent and received using the USI service. The modem can be managed by an external device such as an MCU, MPU or PC, which must implement the USI protocol and the protocol used to serialize the PRIME API. Microchip offers the PRIME Serialized Python library, which allows to develop customized Python scripts to perform different operations. To obtain Python libraries and example scripts contact to the Microchip Smart Energy support team.

The provided project is PLC-RF hybrid, but it can be PLC-only or RF-only if only one expansion board is connected. This project is without operative system (Bare-Metal). The following table shows the available PRIME Service Dual Modem projects.
Table 1-2. PRIME Base Modem projects
NamePath Boards
Service Dual Modem\smartenergy_prime_apps\apps\prime_apps\prime_service_dual_modem\pic32cxmtg_pl460_rf215.XPIC32CXMTG-EK + PL460-EK + REB215-XPRO

Application Example

The PRIME Service Dual Modem application implements the interface between the PRIME Library and an external device. This interface allows accessing the PRIME API through serial interface.

The PRIME Service Dual Modem application interchanges data with the external device through a serial port, whose settings are:
In the example application, the app.c file contains:
  • Firmware upgrade handling:
    • Swapping of the PRIME Stack version between v1.3.6 and v1.4 depending on the network traffic.
      static void lAPP_PrimeVersionSwapRequest(SRV_FU_TRAFFIC_VERSION traffic)
      {
          /* Compare current PRIME pointer with detected traffic */
          if (traffic == SRV_FU_TRAFFIC_VER_PRIME_1_4) 
          {
              newPrimeApi = (PRIME_API *)PRIME_SN_FWSTACK14_ADDRESS;
              versionSwapEn = APP_VERSION_ENABLE_SWAP;
      
          } 
          else if (traffic == SRV_FU_TRAFFIC_VER_PRIME_1_3) 
          {
              newPrimeApi = (PRIME_API *)PRIME_SN_FWSTACK13_ADDRESS;
              versionSwapEn = APP_VERSION_ENABLE_SWAP;
          }
          else
          {
              // Do nothing
          }
      }
      
      static void lAPP_SwapStackVersion(void)
      {
          /* Initialize PRIME stack with the new pointer */
          if (newPrimeApi == (PRIME_API *)PRIME_SN_FWSTACK14_ADDRESS)
          {
              PRIME_Restart((uint32_t *)newPrimeApi, PRIME_VERSION_1_4);
          }
          else
          {
              PRIME_Restart((uint32_t *)newPrimeApi, PRIME_VERSION_1_3);
          }
       
          /* Initialize Modem application */
          APP_Modem_Initialize(); /* Needed to set up callbacks */
      }
    • Swapping of the firmware received in a firmware upgrade process.
      static void lAPP_PrimeFuResultHandler(SRV_FU_RESULT fuResult)
      {
          switch (fuResult) 
          {
              case SRV_FU_RESULT_SUCCESS:
                  /* Update FU pointer */
                  fuSwapEn = APP_FU_ENABLE_SWAP;
                  break;
      
              case SRV_FU_RESULT_CRC_ERROR:
                  /* Nothing to do - FU will restart automatically */
                  break;
      
              case SRV_FU_RESULT_CANCEL:
                  /* Nothing to do */
                  break;
      
              case SRV_FU_RESULT_FW_CONFIRM:
                  /* Nothing to do */
                  break;
      
              case SRV_FU_RESULT_FW_REVERT:
                  /* Revert FU pointer */
                  fuSwapEn = APP_FU_ENABLE_SWAP;
                  break;
      
              case SRV_FU_RESULT_ERROR:
                  /* Nothing to do */
                  break;
      
              case SRV_FU_RESULT_SIGNATURE_ERROR:
                  /* Nothing to do */
                  break;
      
              case SRV_FU_RESULT_IMAGE_ERROR:
                  /* Nothing to do */
                  break;
      
              default:
                  break;
          }
      }
      
      static void lAPP_SwapFirmware(void)
      {
          /* Swap firmware */
          if (SRV_FU_SwapFirmware() == true)
          {
              /* Trigger reset to launch bootloader */
              SRV_RESET_HANDLER_RestartSystem(RESET_HANDLER_FU_RESET);
          }
      }

Application Functionality

The PRIME Service Dual Modem application functionality is very simple as it just redirects requests and callbacks between the PRIME Stack and the USI.

The APP_MODEM_Initialize function initializes the application’s State Machine and gets the PRIME API pointer:
void APP_Modem_Initialize(void)
{
    SRV_STORAGE_PRIME_MODE_INFO_CONFIG boardInfo;

    /* Initialize the reception queue */
    inputMsgRecvIndex = 0;
    outputMsgRecvIndex = 0;

    (void) memset(sAppModemMsgRecv, 0, sizeof(sAppModemMsgRecv));

    /* Initialize TxRx data indicators */
    sRxdataIndication = false;
    sTxdataIndication = false;

    /* Reset node state */
    sAppNodeState = APP_MODEM_NODE_UNREGISTERED;

    (void) memset(&boardInfo, 0, sizeof(boardInfo));

    /* Get the PRIME version */
    SRV_STORAGE_GetConfigInfo(SRV_STORAGE_TYPE_MODE_PRIME, (uint8_t)sizeof(boardInfo),
                              (void *)&boardInfo);

    /* Get PRIME API pointer */
    switch (boardInfo.primeVersion)
    {
        case PRIME_VERSION_1_3:
            PRIME_API_GetPrime13API(&gPrimeApi);
            break;

        case PRIME_VERSION_1_4:
        default:
            PRIME_API_GetPrime14API(&gPrimeApi);
            break;
    }

    /* Set state */
    modemState = APP_MODEM_STATE_CONFIGURE;
}
After Initialization, the State Machine has to wait for the PRIME Stack to be ready before setting the corresponding callbacks:
case APP_MODEM_STATE_CONFIGURE:
    /* Check if PRIME stack is ready */
    if (gPrimeApi->Status() == SYS_STATUS_READY)
    {
        /* Set callback functions */
        APP_Modem_SetCallbacks();

        /* Open USI */
        gUsiHandle = SRV_USI_Open(SRV_USI_INDEX_0);

        /* Configure USI protocol handler */
        SRV_USI_CallbackRegister(gUsiHandle, SRV_USI_PROT_ID_PRIME_API,
                             APP_Modem_USI_PRIME_ApiHandler);

        modemState = APP_MODEM_STATE_TASKS;
    }

    break;
static void APP_Modem_SetCallbacks(void)
{
    MAC_CALLBACKS macCallbacks;
    CL_432_CALLBACKS cl432_callbacks;

    macCallbacks.mac_data_cfm = APP_Modem_DataConfirm;
    macCallbacks.mac_data_ind = APP_Modem_DataIndication;
    macCallbacks.mac_establish_cfm = APP_Modem_EstablishConfirm;
    macCallbacks.mac_establish_ind = APP_Modem_EstablishIndication;
    macCallbacks.mac_join_cfm = APP_Modem_JoinConfirm;
    macCallbacks.mac_join_ind = APP_Modem_JoinIndication;
    macCallbacks.mac_leave_cfm = APP_Modem_LeaveConfirm;
    macCallbacks.mac_leave_ind = APP_Modem_LeaveIndication;
    macCallbacks.mac_release_cfm = APP_Modem_ReleaseConfirm;
    macCallbacks.mac_release_ind = APP_Modem_ReleaseIndication;

    macCallbacks.mlme_demote_cfm = APP_Modem_MLME_DemoteConfirm;
    macCallbacks.mlme_demote_ind = APP_Modem_MLME_DemoteIndication;
    macCallbacks.mlme_mp_demote_cfm = APP_Modem_MLME_MP_DemoteConfirm;
    macCallbacks.mlme_mp_demote_ind = APP_Modem_MLME_MP_DemoteIndication;
    macCallbacks.mlme_get_cfm = APP_Modem_MLME_GetConfirm;
    macCallbacks.mlme_list_get_cfm = APP_Modem_MLME_ListGetConfirm;
    macCallbacks.mlme_promote_cfm = APP_Modem_MLME_PromoteConfirm;
    macCallbacks.mlme_promote_ind = APP_Modem_MLME_PromoteIndication;
    macCallbacks.mlme_mp_promote_cfm = APP_Modem_MLME_MP_PromoteConfirm;
    macCallbacks.mlme_mp_promote_ind = APP_Modem_MLME_MP_PromoteIndication;
    macCallbacks.mlme_register_cfm = APP_Modem_MLME_RegisterConfirm;
    macCallbacks.mlme_register_ind = APP_Modem_MLME_RegisterIndication;
    macCallbacks.mlme_reset_cfm = APP_Modem_MLME_ResetConfirm;
    macCallbacks.mlme_set_cfm = APP_Modem_MLME_SetConfirm;
    macCallbacks.mlme_unregister_cfm = APP_Modem_MLME_UnregisterConfirm;
    macCallbacks.mlme_unregister_ind = APP_Modem_MLME_UnregisterIndication;

    macCallbacks.plme_get_cfm = APP_Modem_PLME_GetConfirm;
    macCallbacks.plme_reset_cfm = APP_Modem_PLME_ResetConfirm;
    macCallbacks.plme_resume_cfm = APP_Modem_PLME_ResumeConfirm;
    macCallbacks.plme_set_cfm = APP_Modem_PLME_SetConfirm;
    macCallbacks.plme_sleep_cfm = APP_Modem_PLME_SleepConfirm;
    macCallbacks.plme_testmode_cfm = NULL;

    gPrimeApi->MacSetCallbacks(&macCallbacks);

    cl432_callbacks.cl_432_dl_data_cfm = APP_Modem_CL432_DlDataConfirm;
    cl432_callbacks.cl_432_dl_data_ind = APP_Modem_CL432_DlDataIndication;
    cl432_callbacks.cl_432_establish_cfm = APP_Modem_CL432_EstablishConfirm;
    cl432_callbacks.cl_432_release_cfm = APP_Modem_CL432_ReleaseConfirm;

    gPrimeApi->Cl432SetCallbacks(&cl432_callbacks);
}

And finally the application just waits for callbacks from the PRIME Stack or request commands from the USI.