1.5.3 Using the AL Module

The following code shows a basic example on how to use Meters And Mode AL:


// *****************************************************************************
// *****************************************************************************
// Section: Global Data Definitions
// *****************************************************************************
// *****************************************************************************

static const uint8_t appDataTxDataBuffer[9] = {1, 2, 3, 4, 5, 6, 7, 8, 9};

APP_DATA appData;

// *****************************************************************************
// *****************************************************************************
// Section: Application Callback Functions
// *****************************************************************************
// *****************************************************************************

static void APP_AL_DataIndication_callback(AL_DATA_IND_PARAMS *indParams)
{
    AL_IB_VALUE ibValue;

    SYS_DEBUG_PRINT(SYS_ERROR_DEBUG, "AL_DATA_INDICATION: DSAP=0x%02X, ECC=0x%02X, SrcAddr=0x%02X%02X%02X%02X%02X%02X, ATTR=%hhu, APDU=0x",
            indParams->dsap, indParams->ecc,
            indParams->srcAddress.address[0], indParams->srcAddress.address[1], indParams->srcAddress.address[2],
            indParams->srcAddress.address[3], indParams->srcAddress.address[4], indParams->srcAddress.address[5],
            indParams->attr);

    for (uint8_t i = 0; i < indParams->apduLen; i++)
    {
        SYS_DEBUG_PRINT(SYS_ERROR_DEBUG, "%02X", indParams->apdu[i]);
    }

    SYS_DEBUG_PRINT(SYS_ERROR_DEBUG, "\r\n");
    
    if (indParams->dsap == DLL_DSAP_APPLICATION_FRAME)
    {
        if (indParams->rxStatus == AL_RX_STATUS_ERROR_BAD_TMAC)
        {
            SYS_DEBUG_PRINT(SYS_ERROR_DEBUG, "TMAC Error\r\n");
        }
        else if (indParams->rxStatus == AL_RX_STATUS_ERROR_AES_NO_KEY)
        {
            SYS_DEBUG_PRINT(SYS_ERROR_DEBUG, "Error No Decryption Key\r\n");
        }
        else if (indParams->rxStatus == AL_RX_STATUS_ERROR_AES_DECRYPT)
        {
            SYS_DEBUG_PRINT(SYS_ERROR_DEBUG, "Decryption Error\r\n");
        }
        else if (indParams->rxStatus == AL_RX_STATUS_ERROR_AES_CMAC)
        {
            SYS_DEBUG_PRINT(SYS_ERROR_DEBUG, "Authentication Error\r\n");
        }
        else if (indParams->rxStatus == AL_RX_STATUS_ERROR_BAD_LEN)
        {
            SYS_DEBUG_PRINT(SYS_ERROR_DEBUG, "Error Bad Length\r\n");
        }
        else
        {
            /* Update LMON to received value */
            appData.drParams.lmon = indParams->lmon;
            if ((indParams->attr == AL_MSG_READ_RESP) || (indParams->attr == AL_MSG_READ_RESP_AUTH))
            {
                SYS_DEBUG_PRINT(SYS_ERROR_DEBUG, "Read Response Frame received\r\n");
            }
            else
            {
                SYS_DEBUG_PRINT(SYS_ERROR_DEBUG, "Application Frame command not recognized\r\n");
            }
        }
    }
    else if (indParams->dsap == DLL_DSAP_NETWORK_MANAGEMENT)
    {
        SYS_DEBUG_PRINT(SYS_ERROR_DEBUG, "Network Management Frame: ");
        if (indParams->attr == AL_MSG_ADDRESS_RESP)
        {
            SYS_DEBUG_PRINT(SYS_ERROR_DEBUG, "ADDRESS RESPONSE\r\n");
        }
        else if (indParams->attr == AL_MSG_REQADDR_RESP)
        {
            SYS_DEBUG_PRINT(SYS_ERROR_DEBUG, "REQ ADDRESS RESPONSE\r\n");
        }
        else
        {
            SYS_DEBUG_PRINT(SYS_ERROR_DEBUG, "Command not recognized\r\n");
        }
    }
    else
    {
        SYS_DEBUG_PRINT(SYS_ERROR_DEBUG, "DSAP not recognized\r\n");
    }
}

static void APP_AL_DataConfirm_callback(AL_DATA_CONFIRM_PARAMS *cfmParams)
{
    SYS_DEBUG_PRINT(SYS_ERROR_DEBUG, "AL_DATA_CONFIRM: Result=%hhu DSAP=0x%02X, ECC=0x%02X, DestAddr=0x%02X%02X%02X%02X%02X%02X\r\n",
            cfmParams->txStatus, cfmParams->dsap, cfmParams->ecc,
            cfmParams->dstAddress.address[0], cfmParams->dstAddress.address[1], cfmParams->dstAddress.address[2],
            cfmParams->dstAddress.address[3], cfmParams->dstAddress.address[4], cfmParams->dstAddress.address[5]);
}

static void APP_AL_EventIndication_callback(AL_EVENT_IND_PARAMS *indParams)
{
    SYS_DEBUG_PRINT(SYS_ERROR_DEBUG, "AL_EVENT_INDICATION: Id=0x%02X, Value=0x", indParams->eventId);

    for (uint8_t i = 0; i < indParams->eventValue.length; i++)
    {
        SYS_DEBUG_PRINT(SYS_ERROR_DEBUG, "%02X", indParams->eventValue.value[i]);
    }

    SYS_DEBUG_PRINT(SYS_ERROR_DEBUG, "\r\n");
}

// *****************************************************************************
// *****************************************************************************
// Section: Application Initialization and State Machine Functions
// *****************************************************************************
// *****************************************************************************

void APP_Initialize ( void )
{
    /* IDLE state is used to signal when application is started */
    appData.state = APP_STATE_INIT;

    /* Init Timer handler */
    appData.tmr1Handle = SYS_TIME_HANDLE_INVALID;
    appData.tmr2Handle = SYS_TIME_HANDLE_INVALID;
    appData.tmr1Expired = false;
    appData.tmr2Expired = false;

    /* Configure DLL Data Request parameters */
    appData.drParams.ecc = DLL_ECC_AES_CTR_READ_KEY;
    appData.drParams.lmon = 0ULL;
    appData.drParams.apdu = (uint8_t *)appDataTxDataBuffer;
    appData.drParams.apduLen = sizeof(appDataTxDataBuffer);

    appData.drParams.dsap = DLL_DSAP_APPLICATION_FRAME;
    appData.drParams.serviceClass = SERVICE_CLASS_S;
    appData.drParams.attr = AL_MSG_COMMAND_AUTH;

    appData.drParams.dstAddress.routeSize = 1;

    /* First Address in the route */
    appData.drParams.dstAddress.macAddress[0].address[0] = 0x11;
    appData.drParams.dstAddress.macAddress[0].address[1] = 0x22;
    appData.drParams.dstAddress.macAddress[0].address[2] = 0x33;
    appData.drParams.dstAddress.macAddress[0].address[3] = 0x44;
    appData.drParams.dstAddress.macAddress[0].address[4] = 0x55;
    appData.drParams.dstAddress.macAddress[0].address[5] = 0x66;
}

void APP_Tasks ( void )
{
    /* Refresh WDG */
    CLEAR_WATCHDOG();

    /* Check the application's current state. */
    switch ( appData.state )
    {
        case APP_STATE_INIT:
        {

            AL_DataIndicationCallbackRegister(APP_AL_DataIndication_callback);
            AL_DataConfirmCallbackRegister(APP_AL_DataConfirm_callback);
            AL_EventIndicationCallbackRegister(APP_AL_EventIndication_callback);

            /* Set MAC address */
            const AL_IB_VALUE value = {
                .length = 6,
                .value = {0x01, 0x00, 0x00, 0x00, 0x00, 0x00}
            };

            AL_SetRequest(AL_MAC_ACA_ADDRESS_IB, 0, &value);

#if defined(TEST_USE_ENCRYPTION)
            /* Set Keys */
            const AL_IB_VALUE valueK1 = {
                .length = 16,
                .value = {0x64, 0x25, 0x77, 0xB3, 0x98, 0x2E, 0x43, 0xAA, 0xF5, 0x35, 0x33, 0x59, 0x74, 0x4D, 0x06, 0x1D}
            };
            AL_SetRequest(AL_AUTH_WRITE_KEY_K1_IB, 0, &valueK1);
            const AL_IB_VALUE valueK2 = {
                .length = 16,
                .value = {0xA0, 0xEE, 0x4C, 0xED, 0x9A, 0x01, 0x59, 0x3C, 0x5C, 0xA7, 0x78, 0xA2, 0xDD, 0x13, 0x34, 0xD2}
            };
            AL_SetRequest(AL_AUTH_READ_KEY_K2_IB, 0, &valueK2);
#endif

            appData.state = APP_STATE_WAIT_AL_READY;
            break;
        }

        case APP_STATE_WAIT_AL_READY:
        {
            if (AL_GetStatus() == SYS_STATUS_READY)
            {
                appData.state = APP_STATE_TX_FRAME;
                appData.txTimeCount = SYS_TIME_Counter64Get() + SYS_TIME_USToCount(APP_TX_DELAY_US);
            }

            break;
        }

        case APP_STATE_TX_FRAME:
        {
            if (SYS_TIME_Counter64Get() >= appData.txTimeCount)
            {
                AL_IB_VALUE valueDestACA = {
                    .length = 6,
                    .value = {0x00}
                };
                /* Reverse ACA for Auth IB */
                valueDestACA.value[0] = appData.drParams.dstAddress.macAddress[0].address[5];
                valueDestACA.value[1] = appData.drParams.dstAddress.macAddress[0].address[4];
                valueDestACA.value[2] = appData.drParams.dstAddress.macAddress[0].address[3];
                valueDestACA.value[3] = appData.drParams.dstAddress.macAddress[0].address[2];
                valueDestACA.value[4] = appData.drParams.dstAddress.macAddress[0].address[1];
                valueDestACA.value[5] = appData.drParams.dstAddress.macAddress[0].address[0];
                AL_SetRequest(AL_AUTH_DESTINATION_NODE_ACA_IB, 0, &valueDestACA);

                SYS_DEBUG_PRINT(SYS_ERROR_DEBUG, "AL_DATA_REQUEST ATTR=%hhu, APDU=0x", appData.drParams.attr);
                for (uint8_t i = 0; i < appData.drParams.apduLen; i++)
                {
                    SYS_DEBUG_PRINT(SYS_ERROR_DEBUG, "%02X", appData.drParams.apdu[i]);
                }
                SYS_DEBUG_PRINT(SYS_ERROR_DEBUG, "\r\n");
                AL_DataRequest(&appData.drParams);

                appData.state = APP_STATE_WAITING;
            }

            break;
        }

        case APP_STATE_WAITING:
        {
            break;
        }

        /* The default state should never be executed. */
        default:
        {
            break;
        }
    }
}