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 additional information refer to the corresponding documentation (smartenergy_metrology repository), with added support for PRIME.

The provided project is PLC-RF hybrid. However, 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 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.
  • PRIME Modem (app_modem.c): Implements the interface between the PRIME Library and an external device. This interface allows accessing the PRIME API through serial interface.

Application Functionality

For additional 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);
}

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