Fork for Get Started Demo

Dependencies:   DebouncedInterrupt dash7-alp mbed-rtos mbed wizzi-utils

Fork of D7A_Demo_full by WizziLab

main.cpp

Committer:
Jeej
Date:
2015-11-20
Revision:
6:897d29f7c89a
Parent:
5:e18c38942326
Child:
7:4226c77951a4

File content as of revision 6:897d29f7c89a:

#include "mbed.h"
#include "rtos.h"
#include "dbg.h"
#include "shield.h"
#include "alp_serial.h"
#include "alp_device.h"
#include "alp_file.h"
#include "alp_report.h"
#include "DebouncedInterrupt.h"
#include "system.h"


/*
These data are user defined and will be used by the dash7board
to identify the device
*/

#define __MANUFACTURER_ID__         0x01BC50C7 // Identify the manufacturer
#define __DEVICE_ID__               0x89ABCDEF // Identify the device type

#define __FW_ID__                   0x01       // Firmware ID
#define __HW_ID__                   0x01520C02 // Hardware ID

// Firmware version
#define __FW_MAJOR__                0x02
#define __FW_MINOR__                0x03
#define __FW_PATCH__                0x0045
#define __FW_HASH__                 0x86605dba


const revision_t revision = {
    // The two first bytes must be present in all reported files
    // They are parsed by the dash7board
    .nw_stat = 0,
    .nw_seq = 255,
    // These data are parsed to identify the device
    .manufacturer_id    = __MANUFACTURER_ID__,
    .device_id          = __DEVICE_ID__,
    .fw_version.fw_id   = __FW_ID__,
    .fw_version.major   = __FW_MAJOR__,
    .fw_version.minor   = __FW_MINOR__,
    .fw_version.patch   = __FW_PATCH__,
    .fw_version.hash    = __FW_HASH__,
    .hw_version         = __HW_ID__,
    .fs_crc             = 0
};

// Alarm data structure
TYPEDEF_STRUCT_PACKED
{
    uint8_t  nw_stat;
    uint8_t  nw_seq;
    uint8_t  status; // Alarm state true/false
} alarm_data_t;

#define ALARM_DATA_FILE_ID          (224)
#define ALARM_DATA_FILE_SIZE        ((uint16_t) sizeof(alarm_data_t))

alarm_data_t alarm_data = {
    .nw_stat = 0,
    .nw_seq = 255,
    .status = 0
};

// Alarm data structure
TYPEDEF_STRUCT_PACKED
{
    uint8_t  cmd; // Alarm state true/false
} alarm_cmd_t;

#define ALARM_CMD_FILE_ID           (211)
#define ALARM_CMD_FILE_SIZE         ((uint16_t) sizeof(alarm_cmd_t))

alarm_cmd_t alarm_cmd = {
    .cmd = 0
};

// Alarm data structure
TYPEDEF_STRUCT_PACKED
{
    uint8_t  nw_stat;
    uint8_t  nw_seq;
    int8_t   value; // Temperature value in °C
} temp_data_t;

#define TEMP_DATA_FILE_ID           (226)
#define TEMP_DATA_FILE_SIZE         ((uint16_t) sizeof(temp_data_t))

temp_data_t temp_data = {
    .nw_stat = 0,
    .nw_seq = 255,
    .value = 0
};

// Checks the status of the report send.
void check_status( AlpReport* report, void* param )
{
    char* message = (char*)param;
    uint8_t status = report->get_status();
    switch (status)
    {
        case ALP_CMD_OK: // message is send and acknowleged
            DPRINT("%s OK\r\n", message);
        break;
        case ALP_CMD_TIMEOUT: // message has not been acknowleged by modem
            DPRINT("%s CMD TIMEOUT\r\n", message);
        break;
        case ALP_CMD_ERROR: // message has been acknowleged by modem but returned an error
            DPRINT("%s CMD ERROR\r\n", message);
        break;
        case ALP_D7_TIMEOUT: // message has not been acknowleged by gateway
            DPRINT("%s D7 TIMEOUT\r\n", message);
        break;
        case ALP_D7_ERROR: // message has been acknowleged by gateway but returned an error
            DPRINT("%s D7 ERROR\r\n", message);
        break;
        default:
            DPRINT("%s UNKNOWN ERROR %d\r\n", message, status);
        break;
    }
}


// Semaphore for notifiying button presses
Semaphore button_user(1);


// Prints the alarm status.
void print_alarm( AlpReport* report, void* param )
{
    alarm_data_t* alarm = (alarm_data_t*)report->get_content();
    DPRINT("SENDING REPORT ALARM %d\r\n", alarm->status);
}

// Updates the temperature value.
void update_temp( AlpReport* report, void* param )
{
    temp_data_t* temp = (temp_data_t*)report->get_content();
    
    temp->value = system_get_temperature();
    
    DPRINT("SENDING REPORT TEMP %d C\r\n", temp->value);
}

// Interrupt Service Routine on button press.
// Inverts alarm status and releases button Semaphore.
void button_push_isr( void )
{
    // Retrieve file content
    alarm_data_t* alarm = (alarm_data_t*)alp_file_get_content(ALARM_DATA_FILE_ID);
    // Invert alarm status
    alarm->status = !alarm->status;
    // Release button Semaphore
    button_user.release();
}

// This Thread monitors the user button
// and reports the alarm status
void alarm_thread( const void* args )
{
    // Get shield device
    AlpDevice* shield = (AlpDevice*)args;
    uint8_t retries;
    
    // Create new report
    AlpReport report_alarm(shield, ALARM_DATA_FILE_ID);
    report_alarm.attach_before(print_alarm, NULL);
    report_alarm.attach_after(check_status, (void*)"REPORT ALARM");
    
    // Enable interrupt on User button
    DebouncedInterrupt button(USER_BUTTON);
    button.attach(&button_push_isr, IRQ_FALL, 200);

    while(true)
    {
        // Wait for button press
        button_user.wait();
        
        // Reset retries
        retries = 0;

        // Notify alarm status
        if (report_alarm.send() != ALP_CMD_OK)
        {
            // Retry policy
            while (retries < 3)
            {
                // retry 1 second later
                Thread::wait(1000);
                retries++;
                DPRINT("RETRY %d: ", retries);
                if (report_alarm.retry() == ALP_CMD_OK) {
                    break;
                }
            }
        }
    }
}

// This Thread reads the chip temperature
// and reports it every 5 minutes
void temp_thread( const void* args )
{
    // Get shield device
    AlpDevice* shield = (AlpDevice*)args;
    uint8_t retries;
    
    // Create new report
    AlpReport report_temp(shield, TEMP_DATA_FILE_ID);
    report_temp.attach_before(update_temp, NULL);
    report_temp.attach_after(check_status, (void*)"REPORT TEMP");

    while(true)
    {
        // Reset retries
        retries = 0;
        // Notify temp value
        if (report_temp.send() == ALP_CMD_OK)
        {
            // Wait 5 minutes
            // The function Thread::wait(...) takes a uin32_t as parameter
            // but the maximum value is uin16_t (65535)
            for (uint8_t i=0 ; i<5 ; i++) { Thread::wait(60000); }
        }
        else
        {
            // Retry policy
            while (retries < 3)
            {
                // retry 1 second later
                Thread::wait(1000);
                retries++;
                DPRINT("RETRY %d: ", retries);
                if (report_temp.retry() == ALP_CMD_OK) {
                    break;
                }
            }
        }
    }
}

void write_file_callback(uint8_t op, void* p)
{
    ASSERT(op == ALP_OP_WRITE_DATA, "Callback called with wrong OP %d.", op);
    
    alp_write_tpl_t* write_cmd = (alp_write_tpl_t*)p;
    if (write_cmd->access_tpl.file_id == ALARM_CMD_FILE_ID)
    {
        alarm_data_t* alarm = (alarm_data_t*)alp_file_get_content(ALARM_DATA_FILE_ID);
        alarm_cmd_t* status = (alarm_cmd_t*)write_cmd->data;
        // Update the report file
        alarm->status = status->cmd;
        // Simulate a button press
        // this will report the new written value
        button_user.release();
    }
}

int main()
{
    // ----- Debug session over USB Serial ----- //
    DBG_OPEN();
        
    // Clear some lines on the terminal
    DPRINT("\r\n\nBOOT\r\n");
    
    // Declare Shield NRST pin
    DigitalOut shield_nrst(PB_0);
    
    // Initialize system functions
    system_open();
    
    // Reset shield
    shield_nrst = 0;
    
    // Open ALP port over serial
    // This is the physical ALP serial port
    AlpSerial serial_shield;

    // Initialize ALP device
    // This creates an AlpDevice object abstracting the shield1001
    // The parameters describe how to access it
    AlpDevice shield(&serial_shield, 0, ACCESS_CLASS_EP, ALP_FILE_BLOCK_ISFB, true);

    // Release reset
    shield_nrst = 1;
    
    // Check the shield boot packets
    shield_check_boot(&shield);
    
    // Wait for Shield to notify its files if there is any
    Thread::wait(1000);
    
    // Add the files to the file system
    alp_file_add(ALARM_CMD_FILE_ID,       ALARM_CMD_FILE_SIZE,       (uint8_t*)&alarm_cmd);
    alp_file_add(TEMP_DATA_FILE_ID,       TEMP_DATA_FILE_SIZE,       (uint8_t*)&temp_data);
    alp_file_add(ALARM_DATA_FILE_ID,      ALARM_DATA_FILE_SIZE,      (uint8_t*)&alarm_data);
    alp_file_add(REVISION_DEVICE_FILE_ID, REVISION_DEVICE_FILE_SIZE, (uint8_t*)&revision);
    
    // Create the Revision report
    AlpReport report_revision(&shield, REVISION_DEVICE_FILE_ID);
    
    // Attach callback after report is send
    report_revision.attach_after(check_status, (void*)"REPORT REVISION");
    
    // Attach callback after a file is written
    // This allows us to be notified when a file is remotely modified
    serial_shield.attach_after(ALP_OP_WRITE_DATA, write_file_callback);
    
    // Send the Revision report
    DPRINT("SENDING REPORT REVISION\r\n");
    ASSERT(report_revision.send() == ALP_CMD_OK, "Failed to send revision!\r\n");
    
    // Init Threads
    Thread t_alarm_thread(alarm_thread, &shield);
    Thread t_temp_thread(temp_thread, &shield);
    
    // Set main task to lowest priority
    osThreadSetPriority(osThreadGetId(), osPriorityIdle);
    while(true)
    {
        // Wait to avoid beeing stuck in loop
        Thread::wait(osWaitForever);
    }
}