1.9 PRIME Service Dual Metering Demo

The PRIME Service Dual Metering Demo application implements a metering application and a PRIME dual Service Node, capable of joining a PRIME network and replying to IEC 61334-4-32 requests. It demonstrates how to send metrology data through the IEC 61334-4-32 connection.

It is based on the metering demo (for more information refer to its documentation (smartenergy_metrology repository) with added support for PRIME.

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-6. PRIME Base Modem projects
NamePath Boards
Service Dual Metering Demo\smartenergy_prime_apps\apps\prime_apps\prime_service_dual_metering_demo\pic32cx_mtsh_db_pl460_rf215.XPIC32CXMTSH-DB+ PL460-EK + REB215-XPRO

Application Example

The PRIME Service Dual Metering Demo application implements a metering application and a PRIME dual Service Node, capable of joining a PRIME network and replying to IEC 61334-4-32 requests. It demonstrates how to send metrology data through the IEC 61334-4-32 connection.

The PRIME Service Dual Metering Demo application implements a console through a serial port, where the user can introduce commands. The settings of the serial port are:
  • Baud-rate 115200 bps
  • 8 data bits
  • no parity
  • 1 stop bit
In order to separate functionalities, there are several application files, each one with its own state machine:
  • Metrology (app_metrology.c): Handles the metrology driver and its configuration.
  • Energy (app_energy.c): Interacts with the metrology application to gather energy consumption. Energy consumption is assigned and grouped into different time zones. This application also manages the RTC peripheral.
  • Events (app_events.c): Interacts with the metrology application to store the event happening and its occurrence time.
  • Console (app_console.c): Interacts with the application using a serial port to send commands to the board and receive data.
  • Datalog (app_datalog.c): Writes/Reads data information to/from the external flash memory.
  • Display (app_display.c): Navigates through the different data shown in the display using the buttons Scroll Down and Scroll up of the demo boards.
  • PRIME Management (app_prime_management.c): Handles the PRIME Stack operation and maintains the PRIME metrology connection functionality.
  • PRIME Metrology (prime_metrology.c): Interacts with the metrology application to gather data and send it to the PRIME Base Node.

Application Functionality

For information about the metering demo refer to its documentation (smartenergy_metrology repository).

The PRIME Management application functionality includes the following:
  • Firmware upgrade handling:
    • Swapping of the PRIME Stack version between v1.3.6 and v1.4 depending on the network traffic.
      static void lAPP_PRIME_MANAGEMENT_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_PRIME_MANAGEMENT_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 metrology application */
          APP_PRIME_METROLOGY_Initialize(); /* Needed to set up callbacks */
      }
    • Swapping of the firmware received in a firmware upgrade process.
      static void lAPP_PRIME_MANAGEMENT_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_PRIME_MANAGEMENT_SwapFirmware(void)
      {
          /* Swap firmware */
          if (SRV_FU_SwapFirmware() == true)
          {
              /* Trigger reset to launch bootloader */
              SRV_RESET_HANDLER_RestartSystem(RESET_HANDLER_FU_RESET);
          }
      }
The PRIME Metrology application is initialized through the PRIME Management. The APP_PRIME_METROLOGY_Initialize function initializes the application’s State Machine and gets the PRIME API pointer:
void APP_PRIME_METROLOGY_Initialize ( void )
{
    /* Place the App state machine in its initial state. */
    app_prime_metrologyState = APP_PRIME_METROLOGY_STATE_INIT;

    (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;
    }

    isDataReceived = false;
    
    /* Set state */
    app_prime_metrologyState = APP_PRIME_METROLOGY_STATE_SERVICE_CONFIGURE;
}
After Initialization, the State Machine has to wait for the PRIME Stack to be ready before setting needed callbacks and requesting data for the IEC 16334-4-32 connection:
case APP_PRIME_METROLOGY_STATE_SERVICE_CONFIGURE:
{
    /* Check if PRIME stack is ready */
    if (gPrimeApi->Status() == SYS_STATUS_READY)
    {
        /* Set callback functions */
        lAPP_PRIME_METROLOGY_SetCallbacks();
        
        /* Reset connection parameters */
        con432Info.isOpen = false;
        con432Info.baseAddr = CL_432_INVALID_ADDRESS;
        con432Info.nodeAddr = CL_432_INVALID_ADDRESS;
        memset(&con432Info.deviceId, 0, sizeof(con432Info.deviceId));
        con432Info.deviceIdLen = 0;
        
        /* Read parameters for serial number */
        if (boardInfo.primeVersion == PRIME_VERSION_1_3)
        {
            gPrimeApi->MlmeGetRequest(PIB_MTP_MAC_EUI_48);
        } 
        else 
        {
            gPrimeApi->MlmeGetRequest(PIB_MAC_EUI_48);
        }

        gPrimeApi->MlmeGetRequest(PIB_MAC_APP_FW_VERSION);
        gPrimeApi->MlmeGetRequest(PIB_MAC_APP_VENDOR_ID);
        gPrimeApi->MlmeGetRequest(PIB_MAC_APP_PRODUCT_ID);

        app_prime_metrologyState = APP_PRIME_METROLOGY_STATE_SERVICE_TASKS;
    }
    
    break;
}
static void lAPP_PRIME_METROLOGY_SetCallbacks(void)
{
    MAC_CALLBACKS macCallbacks;
    CL_432_CALLBACKS cl432_callbacks;
    
    memset(&macCallbacks, 0, sizeof(macCallbacks));
    memset(&cl432_callbacks, 0, sizeof(cl432_callbacks));
    
    macCallbacks.mlme_get_cfm = lAPP_PRIME_METROLOGY_MLME_GetConfirm;
    macCallbacks.mlme_register_ind = lAPP_PRIME_METROLOGY_MLME_RegisterIndication;
    macCallbacks.mlme_unregister_ind = lAPP_PRIME_METROLOGY_MLME_UnregisterIndication;

    gPrimeApi->MacSetCallbacks(&macCallbacks);
    
    cl432_callbacks.cl_432_establish_cfm = lAPP_PRIME_METROLOGY_CL432_EstablishConfirm;
    cl432_callbacks.cl_432_release_cfm = lAPP_PRIME_METROLOGY_CL432_ReleaseConfirm;
    cl432_callbacks.cl_432_dl_data_ind = lAPP_PRIME_METROLOGY_CL432_DlDataIndication;
    cl432_callbacks.cl_432_dl_data_cfm = lAPP_PRIME_METROLOGY_CL432_DlDataConfirm;
  
    gPrimeApi->Cl432SetCallbacks(&cl432_callbacks);    
}

When the Service Node registers, the PRIME Metrology application requests to open an IEC 16334-4-32 connection. If the connection is released, it requests to reopen it.

static void lAPP_PRIME_METROLOGY_MLME_RegisterIndication(uint8_t *sna, uint8_t sid)
{
    if (boardInfo.primeVersion == PRIME_VERSION_1_4)
    {
	/* Get current security profile */
	gPrimeApi->MlmeGetRequest(PIB_MAC_SEC_PROFILE_USED);
    } 
    else 
    {
        ae = 0;
    }

    /* Launch 432 connection */
    gPrimeApi->Cl432EstablishRequest((uint8_t *)meterParams.meterSerial, 
                                     strlen((const char *)meterParams.meterSerial), 
                                     ae);
}
static void lAPP_PRIME_METROLOGY_CL432_ReleaseConfirm(uint16_t dstAddress,
                                                      DL_432_RESULT result)
{
    (void)dstAddress;
    (void)result;

    /* Reset connection parameters */
    con432Info.isOpen = false;
    con432Info.baseAddr = CL_432_INVALID_ADDRESS;
    con432Info.nodeAddr = CL_432_INVALID_ADDRESS;
    memset(&con432Info.deviceId, 0, sizeof(con432Info.deviceId));
    con432Info.deviceIdLen = 0;
    con432Info.dstLsap = 0;
    con432Info.srcLsap = 0;
    con432Info.linkClass = 0;
    
    /* Re-launch 432 connection */
    gPrimeApi->Cl432EstablishRequest((uint8_t *)meterParams.meterSerial, 
                                     strlen((const char *)meterParams.meterSerial), 
                                     ae);
}

And finally, when a message is received, a response is sent with metrology data. Note that the request to send data cannot happen in the indication callback.

static void lAPP_PRIME_METROLOGY_CL432_DlDataIndication(uint8_t dstLsap, 
            uint8_t srcLsap, uint16_t dstAddress, uint16_t srcAddress, 
            uint8_t *data, uint16_t lsduLen, uint8_t linkClass)
{
    (void)dstAddress;
    (void)srcAddress;
    (void)data;
    (void)lsduLen;
    
    con432Info.dstLsap = srcLsap;
    con432Info.srcLsap = dstLsap;
    con432Info.linkClass = linkClass;
    
    isDataReceived = true;
}
case APP_PRIME_METROLOGY_STATE_SERVICE_TASKS:
{
    if (isDataReceived == true)
    {
        /* Send metrology data */
        lAPP_PRIME_METROLOGY_SendData();
        
        isDataReceived = false;
    }
    
    break;
}