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.
| Name | Path | Boards | 
|---|---|---|
| Service Dual Metering Demo | \smartenergy_prime_apps\apps\prime_apps\prime_service_dual_metering_demo\pic32cx_mtsh_db_pl460_rf215.X | PIC32CXMTSH-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.
- Baud-rate 115200 bps
 - 8 data bits
 - no parity
 - 1 stop bit
 
- 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).
- 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); } } 
 - Swapping of the PRIME Stack version between v1.3.6 and v1.4
                                depending on the network
                                traffic.
 
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;
}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;
}
        