Release candidate version. The pointer in GAS Pressure display is changed to a triangle.

Dependencies:   UniGraphic mbed vt100

Please note, at 2-Mar-2018 the current version of mbed-lib has a defect in Ticker.
https://os.mbed.com/forum/bugs-suggestions/topic/29287/

So, mbed lib version 157 is intentionally being used.
Please do not update mbed library until the problem in the above URL is fixed.

In this version, format of GAS Pressure Display has been changed.
/media/uploads/Rhyme/low.jpg

/media/uploads/Rhyme/good.jpg

/media/uploads/Rhyme/high.jpg

moto

af_utils/af_attriburtes.cpp

Committer:
Rhyme
Date:
2018-03-02
Revision:
0:774324cbc5a6

File content as of revision 0:774324cbc5a6:

#include "mbed.h"
#include <ctype.h>
#include "af_attributes.h"
#include "edge_time.h"
#include "edge_sensor.h"
#include "edge_accel.h"
#include "edge_color.h"
#include "edge_temp.h"
#include "edge_pressure.h"
#include "edge_mgr.h"
#include "edge_reset_mgr.h"
// #include "SO1602A.h"
#include <ILI9341.h>
#include "pending.h"

// extern SO1602A *display ;
extern ILI9341 *display ;
extern pending_class *pending ;

static const af_attribute_type af_attr[] = {
/*     ID,                     Description,                Type,                Size */
    {  ATTR_SENSE_VAL,         "Sensor Value",             ATTRIBUTE_TYPE_UTF8S, 255 },
    {  ATTR_ACCEL_PRESENT,     "Accel Present",            ATTRIBUTE_TYPE_BOOLEAN, 1 },
    {  ATTR_ACCEL_ENABLE,      "Accel Enable",             ATTRIBUTE_TYPE_BOOLEAN, 1 },
    {  ATTR_ACCEL_INTERVAL,    "Accel Interval",           ATTRIBUTE_TYPE_SINT16,  2 },
    {  ATTR_ACCEL_VALUE,       "Accel Value",              ATTRIBUTE_TYPE_FIXED_15_16, 4},
/* first color sensor (VEML6040) and LED set */    
    {  ATTR_COLOR0_PRESENT,    "Color1 Present",           ATTRIBUTE_TYPE_BOOLEAN, 1 },
    {  ATTR_COLOR0_ENABLE,     "Color1 Enable",            ATTRIBUTE_TYPE_BOOLEAN, 1 },
    {  ATTR_COLOR0_INTERVAL,   "Color1 Interval",          ATTRIBUTE_TYPE_SINT16,  2 },
    {  ATTR_COLOR0_TRIGMODE,   "Color1 Trigger Mode",      ATTRIBUTE_TYPE_SINT8,   1 },
    {  ATTR_COLOR0_ITIME,      "Color1 Integration Time",  ATTRIBUTE_TYPE_SINT8,   1 },    
    {  ATTR_COLOR0_CALIBRATE,  "Color1 Calibrate",         ATTRIBUTE_TYPE_BOOLEAN, 1 },
    {  ATTR_COLOR0_PWM_R,      "Color1 PWM R",             ATTRIBUTE_TYPE_SINT32,  4 },
    {  ATTR_COLOR0_PWM_G,      "Color1 PWM G",             ATTRIBUTE_TYPE_SINT32,  4 },
    {  ATTR_COLOR0_PWM_B,      "Color1 PWM B",             ATTRIBUTE_TYPE_SINT32,  4 },
    {  ATTR_COLOR0_PWM_PERIOD, "Color1 PWM Period",        ATTRIBUTE_TYPE_SINT16,  2 },
    {  ATTR_COLOR0_PWM_TARGET, "Color1 PWM Target",        ATTRIBUTE_TYPE_SINT16,  2 },
    {  ATTR_COLOR0_R_VALUE,    "Color1 R",                 ATTRIBUTE_TYPE_SINT16,  2 },
    {  ATTR_COLOR0_G_VALUE,    "Color1 G",                 ATTRIBUTE_TYPE_SINT16,  2 },
    {  ATTR_COLOR0_B_VALUE,    "Color1 B",                 ATTRIBUTE_TYPE_SINT16,  2 },
/* second color sensor (VEML6040) and LED set */ 
    {  ATTR_COLOR1_PRESENT,    "Color2 Present",           ATTRIBUTE_TYPE_BOOLEAN, 1 },
    {  ATTR_COLOR1_ENABLE,     "Color2 Enable",            ATTRIBUTE_TYPE_BOOLEAN, 1 },
    {  ATTR_COLOR1_INTERVAL,   "Color2 Interval",          ATTRIBUTE_TYPE_SINT16,  2 },
    {  ATTR_COLOR1_TRIGMODE,   "Color2 Trigger Mode",      ATTRIBUTE_TYPE_SINT8,   1 },
    {  ATTR_COLOR1_ITIME,      "Color2 Integration Time",  ATTRIBUTE_TYPE_SINT8,   1 },
    {  ATTR_COLOR1_CALIBRATE,  "Color2 Calibrate",         ATTRIBUTE_TYPE_BOOLEAN, 1 },
    {  ATTR_COLOR1_PWM_R,      "Color2 PWM R",             ATTRIBUTE_TYPE_SINT32,  4 },
    {  ATTR_COLOR1_PWM_G,      "Color2 PWM G",             ATTRIBUTE_TYPE_SINT32,  4 },
    {  ATTR_COLOR1_PWM_B,      "Color2 PWM B",             ATTRIBUTE_TYPE_SINT32,  4 },
    {  ATTR_COLOR1_PWM_PERIOD, "Color2 PWM Period",        ATTRIBUTE_TYPE_SINT16,  2 },
    {  ATTR_COLOR1_PWM_TARGET, "Color2 PWM Target",        ATTRIBUTE_TYPE_SINT16,  2 },
    {  ATTR_COLOR1_R_VALUE,    "Color2 R",                 ATTRIBUTE_TYPE_SINT16,  2 },
    {  ATTR_COLOR1_G_VALUE,    "Color2 G",                 ATTRIBUTE_TYPE_SINT16,  2 },
    {  ATTR_COLOR1_B_VALUE,    "Color2 B",                 ATTRIBUTE_TYPE_SINT16,  2 },
/* first temperature sensor (LM75B) */    
    {  ATTR_TEMP0_PRESENT,    "Temp0 Present",             ATTRIBUTE_TYPE_BOOLEAN, 1 },
    {  ATTR_TEMP0_ENABLE,     "Temp0 Enable",              ATTRIBUTE_TYPE_BOOLEAN, 1 },
    {  ATTR_TEMP0_INTERVAL,   "Temp0 Interval",            ATTRIBUTE_TYPE_SINT16,  2 },
    {  ATTR_TEMP0_VALUE,      "Temp0 Value",               ATTRIBUTE_TYPE_FIXED_15_16, 4},
/* second temperature sensor (SMTC502AT/Before) */    
    {  ATTR_TEMP1_PRESENT,    "Temp1 Present",             ATTRIBUTE_TYPE_BOOLEAN, 1 },
    {  ATTR_TEMP1_ENABLE,     "Temp1 Enable",              ATTRIBUTE_TYPE_BOOLEAN, 1 },
    {  ATTR_TEMP1_INTERVAL,   "Temp1 Interval",            ATTRIBUTE_TYPE_SINT16,  2 },
    {  ATTR_TEMP1_VALUE,      "Temp1 Value",               ATTRIBUTE_TYPE_FIXED_15_16, 4},
/* third temperature sensor (SMTC502AT/After) */    
    {  ATTR_TEMP2_PRESENT,    "Temp2 Present",             ATTRIBUTE_TYPE_BOOLEAN, 1 },
    {  ATTR_TEMP2_ENABLE,     "Temp2 Enable",              ATTRIBUTE_TYPE_BOOLEAN, 1 },
    {  ATTR_TEMP2_INTERVAL,   "Temp2 Interval",            ATTRIBUTE_TYPE_SINT16,  2 },
    {  ATTR_TEMP2_VALUE,      "Temp2 Value",               ATTRIBUTE_TYPE_FIXED_15_16, 4},
/* fouth temperateure sensor (LM75B) */
    {  ATTR_TEMP3_PRESENT,    "Temp3 Present",             ATTRIBUTE_TYPE_BOOLEAN, 1 },
    {  ATTR_TEMP3_ENABLE,     "Temp3 Enable",              ATTRIBUTE_TYPE_BOOLEAN, 1 },
    {  ATTR_TEMP3_INTERVAL,   "Temp3 Interval",            ATTRIBUTE_TYPE_SINT16,  2 },
    {  ATTR_TEMP3_VALUE,      "Temp3 Value",               ATTRIBUTE_TYPE_FIXED_15_16, 4}, 
/* Gas Pressure sensor (PSE530) */    
    {  ATTR_GAS_PRESENT,      "Gas Pressure Present",      ATTRIBUTE_TYPE_BOOLEAN, 1 },
    {  ATTR_GAS_ENABLE,       "Gas Pressure Enable",       ATTRIBUTE_TYPE_BOOLEAN, 1 },
    {  ATTR_GAS_INTERVAL,     "Gas Pressure Interval",     ATTRIBUTE_TYPE_SINT16,  2 },
    {  ATTR_GAS_VALUE,        "Gas Pressure Value",        ATTRIBUTE_TYPE_FIXED_15_16, 4}, 
    {  ATTR_GAS_THR_MODE,     "Gas Press Threshold Mode",  ATTRIBUTE_TYPE_SINT8,   1 },
    {  ATTR_GAS_THR_HIGH,     "Gas Press High Thresh",     ATTRIBUTE_TYPE_SINT16,  2 },
    {  ATTR_GAS_THR_LOW,      "Gas Press Low Thresh",      ATTRIBUTE_TYPE_SINT16,  2 },
/* Software Reset Request */    
    {  ATTR_SOFTWARE_RESET,   "Software Reset",            ATTRIBUTE_TYPE_BOOLEAN, 1 },
    {  ATTR_DISPLAY_MODE,     "Display Mode",              ATTRIBUTE_TYPE_SINT8,   1 },
    {  ATTR_MCU_RESET_REASON, "MCU Reset Reason",          ATTRIBUTE_TYPE_UTF8S,  64 },
                   
    { ATTR_LED,               "LED",                       ATTRIBUTE_TYPE_SINT16,  2 },
    { ATTR_IO0,               "I/O 0",                     ATTRIBUTE_TYPE_SINT64,  8 },
    { ATTR_IO1,               "I/O 1",                     ATTRIBUTE_TYPE_SINT64,  8 },
    { ATTR_IO2,               "I/O 2",                     ATTRIBUTE_TYPE_SINT64,  8 },
    { ATTR_BUTTON,            "BUTTON",                    ATTRIBUTE_TYPE_BOOLEAN, 2 },
    { ATTR_IO3,               "I/O 3",                     ATTRIBUTE_TYPE_SINT64,  8 },
    { ATTR_BOOT_LOADER_VER,   "Bootloader Version",        ATTRIBUTE_TYPE_SINT64,  8 },
    { ATTR_BLE_STACK_VER,     "BLE Stack Version",         ATTRIBUTE_TYPE_SINT64,  8 },
    { ATTR_FW_APP_VER,        "FW Application Version",    ATTRIBUTE_TYPE_SINT64,  8 },
    { ATTR_DEVICE_DESC,       "Device Description",        ATTRIBUTE_TYPE_SINT64,  8 },
    { ATTR_WIFI_VER,          "Wi-Fi chip",                ATTRIBUTE_TYPE_SINT64,  8 },
    { ATTR_OFFLINE_SCHED,     "Offline Schedules enable",  ATTRIBUTE_TYPE_SINT16,  2 }, 
    { ATTR_SECURITY_ENABLED,  "Security Enabled",          ATTRIBUTE_TYPE_SINT8,   1 }, /* ? */
    { ATTR_UTC_OFFSET,        "UTC offset data",           ATTRIBUTE_TYPE_BYTES,   8 },
    { ATTR_CONFIGURES_SSID,   "Configured SSID",           ATTRIBUTE_TYPE_UTF8S,  10 }, /* ? */
    { ATTR_WIFI_BARS,         "Wi-Fi Bars",                ATTRIBUTE_TYPE_SINT8,   1 },
    { ATTR_WIFI_STDY_STATE,   "Wi-Fi Steady State",        ATTRIBUTE_TYPE_SINT8,   1 },
    { ATTR_COMMAND,           "Command",                   ATTRIBUTE_TYPE_BYTES,   8 }, /* ? */   
    { ATTR_ASR_STATE,         "ASR State",                 ATTRIBUTE_TYPE_SINT8,   1 },
    { ATTR_LOW_BATTERY,       "Low Battery Warning",       ATTRIBUTE_TYPE_SINT8,   1 },
    { ATTR_LINKED_TIMESTAMP,  "Linked Timestamp",          ATTRIBUTE_TYPE_SINT32,  4 },
    { ATTR_ATTR_ACK,          "Attribute ACK",             ATTRIBUTE_TYPE_SINT16,  8 },
    { ATTR_REBOOT_REASON,     "Reboot Reason",             ATTRIBUTE_TYPE_UTF8S, 100 },
    { ATTR_BLE_COMMS,         "BLE Comms",                 ATTRIBUTE_TYPE_BYTES,  12 },
    { ATTR_MCU_INTERFACE,     "MCU Interface",             ATTRIBUTE_TYPE_SINT8,   1 },
    { 0,                      0,                           0,                      0 }
} ;

int get_af_attr(uint16_t id) 
{
    int i ;
    for (i = 0 ; af_attr[i].id != 0 ; i++ ) {
        if (id == af_attr[i].id) {
            break ;
        }
    }
    return (i) ;
}

void print_af_error(int resultCode) 
{
    switch(resultCode) {
    case afSUCCESS:
        printf("Operation completed successfully\n") ;
        break ;
    case afERROR_NO_SUCH_ATTRIBUTE:
        printf("Request was made for unknown attribute id\n") ;
        break ;
    case afERROR_BUSY:
        printf("Request already in progress, try again\n") ;
        break ;
    case afERROR_INVALID_COMMAND:
        printf("Command could not be parsed\n") ;
        break ;
    case afERROR_QUEUE_OVERFLOW:
        printf("Queue is full\n") ;
        break ;
    case afERROR_QUEUE_UNDERFLOW:
        printf("Queue is empty\n") ;
        break ;
    case afERROR_INVALID_PARAM:
        printf("Bad input parameter\n") ;
        break ;
    default:
        printf("Unknown error code %d\n", resultCode) ;
        break ;
    }
}

void af_print_values(
    const uint8_t   requestId, 
    const uint16_t  attributeId,
    const uint16_t  valueLen,
    const uint8_t   *value
) 
{
    int i, id ;

    id = get_af_attr(attributeId) ;

    if (af_attr[id].id  != 0) {
        printf(af_attr[id].description) ;
        printf(" : ") ;
        switch(af_attr[id].attribute_type) {
        case ATTRIBUTE_TYPE_BOOLEAN:
        case ATTRIBUTE_TYPE_SINT8: 
            if (valueLen >= 1) {
                printf("%02X\n", value[0]) ;
            }
            break ;
        case ATTRIBUTE_TYPE_SINT16:
            if (valueLen >= 2) {
                printf("%02X%02X\n", value[1], value[0]) ;
            }
            break ; 
        case ATTRIBUTE_TYPE_SINT32:
            if (valueLen >= 4) {
                printf("%02X%02X%02X%02X\n",
                    value[3],value[2],value[1],value[0]) ;
            }
            break ;
        case ATTRIBUTE_TYPE_SINT64:
            if (valueLen >= 8) {
                printf("%02X%02X %02X%02X %02X%02X %02X%02X\n",
                    value[7], value[6], value[5], value[4],
                    value[3], value[2], value[1], value[0]) ;
            }
            break ;
        case ATTRIBUTE_TYPE_UTF8S: 
            if (valueLen > 0) {
                for (i = 0 ; i < valueLen ; i++) {
                    if (isprint(value[i])) {
                        printf("%c", value[i]) ;
                    } else if (value[i] == 0) { /* string terminator NULL */
                        break ;
                    } else {
                        printf("\'%02X\'",value[i]) ;
                    }
                }
                printf("\n") ;
            }
            break ;
        case ATTRIBUTE_TYPE_BYTES:
        default:
            if (valueLen > 0) {
                for (i = 0 ; i < valueLen ; i++ ) {
                    printf("%02X ", value[i]) ;
                }
                printf("\n") ;
            }
            break ;
        }
    } else {
        if (valueLen > 0) {
            for (i = 0 ; i < valueLen ; i++ ) {
                printf("%02X ", value[i]) ;
            }
            printf("\n") ;
        }
    }
//    printf("\n") ;
}

void assignAttribute(
    const uint8_t   requestId, 
    const uint16_t  attributeId,
    const uint16_t  valueLen,
    const uint8_t   *value,
    bool fromRequest
) 
{
    switch(attributeId) {
    case ATTR_LINKED_TIMESTAMP: /* timestamp */  
        set_time(valueLen, value) ; /* 68 us */
        if (fromRequest) afero->setAttributeComplete(requestId, attributeId, valueLen, value) ;
        printf("timestampe = ") ;
        print_date_wd(&current_time) ;
//        print_time(&current_time) ;
        printf("\n") ;
        break ;
    case ATTR_SOFTWARE_RESET: /* software reset requested! */
        if (value[0]) {
            reset_watch_dog() ;
            printf("Software Reset Requested!\n") ;
            if (display != 0) {
                display->cls() ;
                display->locate(0,0) ;
                display->printf("System Rebooting!") ;
            }
            reset_watch_dog() ;
            if (fromRequest) afero->setAttributeComplete(requestId, attributeId, valueLen, value) ;
            wait(0.5) ;
            reset_watch_dog() ;
            reboot_edge() ;
        }
        break ;
    case ATTR_DISPLAY_MODE:
        if (display_mode != value[0]) {
            display_mode = value[0] ;
            if (display) {
                display->BusEnable(true) ;
                display->cls() ;
                display->BusEnable(false) ;
            }
        }
        reset_watch_dog() ;
        switch(value[0]) {
        case DISPLAY_MODE_GAS: /* gas pressure monitor only */
            break ;
        case DISPLAY_MODE_SUMMARY: /* summary */
            break ;
        case DISPLAY_MODE_CHART: /* chart mode */
            if (display) {
                draw_all_chart_frame() ;
            }
            break ;
        case DISPLAY_MODE_OFF: /* display off */
        default:
            display_mode = DISPLAY_MODE_OFF ;
            break ;
        }
        if (fromRequest) afero->setAttributeComplete(requestId, attributeId, valueLen, value) ;
        break ;
    case ATTR_ACCEL_ENABLE: /* accel enable */
        if (sensor[SENSOR_ID_ACCEL]) {
            if (value[0]) {
                sensor[SENSOR_ID_ACCEL]->reset() ;
                sensor[SENSOR_ID_ACCEL]->enable() ;
            } else if (sensor[SENSOR_ID_ACCEL]){
                sensor[SENSOR_ID_ACCEL]->disable() ;
            }
            if (fromRequest) afero->setAttributeComplete(requestId, attributeId, valueLen, value) ;
        }
        break ;
    case ATTR_ACCEL_INTERVAL:
        if (sensor[SENSOR_ID_ACCEL]) {
            sensor[SENSOR_ID_ACCEL]->setInterval((value[1] << 8) | value[0]) ;
            if (fromRequest) afero->setAttributeComplete(requestId, attributeId, valueLen, value) ;
        }
        break ;
    case ATTR_COLOR0_ENABLE: /* color0 enable */
        if (sensor[SENSOR_ID_COLOR1]) {
            if (value[0]) {
                sensor[SENSOR_ID_COLOR1]->reset() ;
                sensor[SENSOR_ID_COLOR1]->enable() ;
            } else {
                sensor[SENSOR_ID_COLOR1]->disable() ;
            }
            if (fromRequest) afero->setAttributeComplete(requestId, attributeId, valueLen, value) ;
        }
        break ;
    case ATTR_COLOR0_INTERVAL:
        if (sensor[SENSOR_ID_COLOR1]) {
            sensor[SENSOR_ID_COLOR1]->setInterval((value[1] << 8) | value[0]) ;
            if (fromRequest) afero->setAttributeComplete(requestId, attributeId, valueLen, value) ;
        }
        break ;    
    case ATTR_COLOR0_TRIGMODE: /* color0 config */
        if (sensor[SENSOR_ID_COLOR1]) {
            uint8_t config = ((edge_color*)sensor[SENSOR_ID_COLOR1])->getConfig() & 0x70 ;
            if (value[0]) {
                config = config | 0x06 ;
            } 
            ((edge_color*)sensor[SENSOR_ID_COLOR1])->setConfig(config) ;
            if (fromRequest) afero->setAttributeComplete(requestId, attributeId, valueLen, value) ;
        }
        break ;
    case ATTR_COLOR0_ITIME: /* color0 config */
        if (sensor[SENSOR_ID_COLOR1]) {
            uint8_t config = ((edge_color*)sensor[SENSOR_ID_COLOR1])->getConfig() & 0x07 ;
            config = (value[0] << 4) | config ;
            ((edge_color*)sensor[SENSOR_ID_COLOR1])->setConfig(config) ;
          if (fromRequest) afero->setAttributeComplete(requestId, attributeId, valueLen, value) ;
        }
        break ;    
    case ATTR_COLOR0_PWM_PERIOD: /* color0 pwm period */
        if (sensor[SENSOR_ID_COLOR1]) {
            ((edge_color*)sensor[SENSOR_ID_COLOR1])->set_pwm_period((value[1] << 8) | value[0]) ;
          if (fromRequest) afero->setAttributeComplete(requestId, attributeId, valueLen, value) ;
        }            
        break ;
    case ATTR_COLOR0_PWM_TARGET: /* color0 pwm calibration target */
        if (sensor[SENSOR_ID_COLOR1]) {
            color0_target[0] = (value[1] << 8) | value[0] ;
            color0_target[1] = color0_target[0] ;
            color0_target[2] = color0_target[1] ;
          if (fromRequest) afero->setAttributeComplete(requestId, attributeId, valueLen, value) ;
        }            
        break ;     
#if 1 /* do not handle calibration twice */   
    case ATTR_COLOR0_CALIBRATE: /* calibrate color0 */
        if (sensor[SENSOR_ID_COLOR1] && value[0] && fromRequest) { /* do calibration */ 
            ((edge_color*)sensor[SENSOR_ID_COLOR1])->request_calibration() ;
        }
        break ;
#endif
    case ATTR_COLOR0_PWM_R:
        if (sensor[SENSOR_ID_COLOR1]) {
            ((edge_color*)sensor[SENSOR_ID_COLOR1])->setPwmR( (value[1] << 8) | value[0] ) ;
//            color0_pwm[0] = (value[1] << 8) | value[0] ;
        }
        if (fromRequest) afero->setAttributeComplete(requestId, attributeId, valueLen, value) ;       
        break ;
    case ATTR_COLOR0_PWM_G:
        if (sensor[SENSOR_ID_COLOR1]) {
            ((edge_color*)sensor[SENSOR_ID_COLOR1])->setPwmG( (value[1] << 8) | value[0] ) ;
//            color0_pwm[1] = (value[1] << 8) | value[0] ;
        }
        if (fromRequest) afero->setAttributeComplete(requestId, attributeId, valueLen, value) ;       
        break ;
    case ATTR_COLOR0_PWM_B:
        if (sensor[SENSOR_ID_COLOR1]) {
            ((edge_color*)sensor[SENSOR_ID_COLOR1])->setPwmB( (value[1] << 8) | value[0] ) ;
//            color0_pwm[2] = (value[1] << 8) | value[0] ;
        }
        if (fromRequest) afero->setAttributeComplete(requestId, attributeId, valueLen, value) ;       
        break ;
    case ATTR_COLOR1_ENABLE: /* color1 enable */
        if (sensor[SENSOR_ID_COLOR2]) {
            if (value[0]) {
                sensor[SENSOR_ID_COLOR2]->reset() ;
                sensor[SENSOR_ID_COLOR2]->enable() ;
            } else {
                sensor[SENSOR_ID_COLOR2]->disable() ;
            }
            if (fromRequest) afero->setAttributeComplete(requestId, attributeId, valueLen, value) ;
        }
        break ;
    case ATTR_COLOR1_INTERVAL:
        if (sensor[SENSOR_ID_COLOR2]) {
            sensor[SENSOR_ID_COLOR2]->setInterval((value[1] << 8) | value[0]) ;
            if (fromRequest) afero->setAttributeComplete(requestId, attributeId, valueLen, value) ;
        }
        break ;    
    case ATTR_COLOR1_TRIGMODE: /* color0 config */
        if (sensor[SENSOR_ID_COLOR2]) {
            uint8_t config = ((edge_color*)sensor[SENSOR_ID_COLOR2])->getConfig() & 0x70 ;
            if (value[0]) {
                config = config | 0x06 ;
            } 
            ((edge_color*)sensor[SENSOR_ID_COLOR2])->setConfig(config) ;
            if (fromRequest) afero->setAttributeComplete(requestId, attributeId, valueLen, value) ;
        }
        break ;
    case ATTR_COLOR1_ITIME: /* color0 config */
        if (sensor[SENSOR_ID_COLOR2]) {
            uint8_t config = ((edge_color*)sensor[SENSOR_ID_COLOR2])->getConfig() & 0x07 ;
            config = (value[0] << 4) | config ;
            ((edge_color*)sensor[SENSOR_ID_COLOR2])->setConfig(config) ;
            if (fromRequest) afero->setAttributeComplete(requestId, attributeId, valueLen, value) ;
        }
        break ;  
    case ATTR_COLOR1_PWM_PERIOD: /* color0 pwm period */
        if (sensor[SENSOR_ID_COLOR2]) {
            ((edge_color*)sensor[SENSOR_ID_COLOR2])->set_pwm_period((value[1] << 8) | value[0]) ;
            if (fromRequest) afero->setAttributeComplete(requestId, attributeId, valueLen, value) ;
        }            
        break ;
    case ATTR_COLOR1_PWM_TARGET: /* color0 pwm calibration target */
        if (sensor[SENSOR_ID_COLOR2]) {
            color1_target[0] = (value[1] << 8) | value[0] ;
            color1_target[1] = color1_target[0] ;
            color1_target[2] = color1_target[1] ;
            if (fromRequest) afero->setAttributeComplete(requestId, attributeId, valueLen, value) ;
        }            
        break ;   
#if 1 /* do not handle calibration twice */
    case ATTR_COLOR1_CALIBRATE: /* calibrate color1 */
        if (sensor[SENSOR_ID_COLOR2] && value[0] && fromRequest) { /* do calibration! */
            ((edge_color*)sensor[SENSOR_ID_COLOR2])->request_calibration() ;
        }
        break ;
#endif
    case ATTR_COLOR1_PWM_R:
        if (sensor[SENSOR_ID_COLOR2]) {
            ((edge_color*)sensor[SENSOR_ID_COLOR2])->setPwmR( (value[1] << 8) | value[0] ) ;
//            color1_pwm[0] = (value[1] << 8) | value[0] ;
        }
        if (fromRequest) afero->setAttributeComplete(requestId, attributeId, valueLen, value) ;       
        break ;
    case ATTR_COLOR1_PWM_G:
        if (sensor[SENSOR_ID_COLOR2]) {
            ((edge_color*)sensor[SENSOR_ID_COLOR2])->setPwmG( (value[1] << 8) | value[0] ) ;
//            color1_pwm[1] = (value[1] << 8) | value[0] ;
        }
        if (fromRequest) afero->setAttributeComplete(requestId, attributeId, valueLen, value) ;       
        break ;
    case ATTR_COLOR1_PWM_B:
        if (sensor[SENSOR_ID_COLOR2]) {
            ((edge_color*)sensor[SENSOR_ID_COLOR2])->setPwmB( (value[1] << 8) | value[0] ) ;
//            color1_pwm[2] = (value[1] << 8) | value[0] ;
        }
        if (fromRequest) afero->setAttributeComplete(requestId, attributeId, valueLen, value) ;       
        break ;
    case ATTR_TEMP0_ENABLE: /* temp0 is used to control temp-sensors */
        if (sensor[SENSOR_ID_TEMP]) {
            if (value[0]) {
                sensor[SENSOR_ID_TEMP]->reset() ;
                sensor[SENSOR_ID_TEMP]->enable() ;
            } else {
                sensor[SENSOR_ID_TEMP]->disable() ;
            }
            if (fromRequest) afero->setAttributeComplete(requestId, attributeId, valueLen, value) ;
        } 
        break ;
    case ATTR_TEMP0_INTERVAL:
        if (sensor[SENSOR_ID_TEMP]) {
            sensor[SENSOR_ID_TEMP]->setInterval((value[1] << 8) | value[0]) ;
            if (fromRequest) afero->setAttributeComplete(requestId, attributeId, valueLen, value) ;
        }
        break ; 
    case ATTR_TEMP3_ENABLE: /* temp3 enable */
        break ;
    case ATTR_GAS_ENABLE: /* pressure enable */
        if (sensor[SENSOR_ID_PRESS]) {
            if (value[0]) {
                sensor[SENSOR_ID_PRESS]->reset() ;
                sensor[SENSOR_ID_PRESS]->enable() ;
            } else {
                sensor[SENSOR_ID_PRESS]->disable() ;
            }
            if (fromRequest) afero->setAttributeComplete(requestId, attributeId, valueLen, value) ;
        }   
        break ;
    case ATTR_GAS_INTERVAL:
        if (sensor[SENSOR_ID_PRESS]) {
            sensor[SENSOR_ID_PRESS]->setInterval((value[1] << 8) | value[0]) ;
            if (fromRequest) afero->setAttributeComplete(requestId, attributeId, valueLen, value) ;
        }
        break ; 
    case ATTR_GAS_THR_MODE:
        if (sensor[SENSOR_ID_PRESS]) {
            ((edge_pressure*)sensor[SENSOR_ID_PRESS])->set_thr_mode(value[0]) ;
            if (fromRequest) afero->setAttributeComplete(requestId, attributeId, valueLen, value) ;
        }
        break ;
    case ATTR_GAS_THR_HIGH:
        if (sensor[SENSOR_ID_PRESS]) {
            ((edge_pressure*)sensor[SENSOR_ID_PRESS])->set_thr_high((value[1] << 8) | value[0]) ;
            if (fromRequest) afero->setAttributeComplete(requestId, attributeId, valueLen, value) ;
        }
        break ;
    case ATTR_GAS_THR_LOW:
        if (sensor[SENSOR_ID_PRESS]) {
            ((edge_pressure*)sensor[SENSOR_ID_PRESS])->set_thr_low((value[1] << 8) | value[0]) ;
            if (fromRequest) afero->setAttributeComplete(requestId, attributeId, valueLen, value) ;
        }
        break ;               
    default:
        break ;    
    }
}

/*
 * Callback that allows ASR to request an MCU attribute be changed. 
 * You should define this function in your MCU firmware to perform application-specific actions 
 * your code must take (e.g., updating the state of the hardware), 
 * in light of the attribute value change.
*/
void attributeChangeRequest(
    const uint8_t   requestId, 
    const uint16_t  attributeId,
    const uint16_t  valueLen,
    const uint8_t   *value
) 
{ 
    uint32_t timestamp = edge_time ;
    if ((pending != 0)&&(pending->request->requestId == requestId)) {
        pending->replied_time = timestamp ;

    }
    ts2time(timestamp, &current_time) ; /* 12 us */
    if (verbos) {
        print_time(&current_time) ;
        printf(" %5d ASR requested [%d] : ", attributeId, requestId) ;
        af_print_values(requestId, attributeId, valueLen, value) ;
    }

    assignAttribute(requestId, attributeId, valueLen, value, true) ;

//    af_print_values(requestId, attributeId, valueLen, value) ;
    if ((pending != 0)&&(pending->request->requestId == requestId)) {
        printf("Request [%d] replied in %d sec!\n", requestId, pending->replied_time - pending->submit_time) ;
        delete pending ;
        pending = 0 ;
    }
}

/*
 * Application callback that allows afLib to notify that an attribute has changed. 
 * This method will be called in response to a getAttribute call from the application 
 * and whenever a ASR module attribute changes.
 */
void attributeUpdatedReport(
    const uint8_t   requestId,
    const uint16_t  attributeId,
    const uint16_t  valueLen,
    const uint8_t   *value
) 
{
    uint32_t timestamp = edge_time ;
    int result ;
    if ((pending != 0)&&(pending->request->requestId == requestId)) {
        pending->replied_time = timestamp ;
    }   
    ts2time(timestamp, &current_time) ; /* 12us */
    if (verbos) {
        print_time(&current_time) ;
        printf(" %5d ASR reported [%d]: ", attributeId, requestId) ;
        af_print_values(requestId, attributeId, valueLen, value) ;
    }

    switch(attributeId) {
    case ATTR_LINKED_TIMESTAMP:
        set_time(valueLen, value) ; /* 68 us */
        printf("timestampe = ") ;
        print_date_wd(&current_time) ;
//        print_time(&current_time) ;
        printf("\n") ;
        break ;         
    case ATTR_WIFI_STDY_STATE:
        gConnected = false ;
        printf("WiFi Steady State: ") ;
        switch(value[0]) {
        case 0: printf("Not Connected\n")      ; break ;
        case 1: printf("Pending\n") ;            break ;
        case 2: 
            printf("Connected\n") ;       
            gConnected = true ; // the only case Connected state is OK   
            break ;
        case 3: printf("Unknown Failure\n") ;    break ;
        case 4: printf("Association Failed\n") ; break ;
        case 5: printf("Handshake Failed\n") ;   break ;
        case 6: printf("Echo Failed\n") ;        break ;
        case 7: printf("SSID Not Found\n") ;     break ;
        case 8: printf("NTP Failed\n") ;         break ;
        default: printf("Unknown [%d]\n", value[0]) ; break ;
        }
        break ;
    case ATTR_REBOOT_REASON:
        printf("Reboot Reason: ") ;
        switch(value[0]) {
        case 1: printf("Reset pin asserted\n") ; break ;
        case 2: printf("Watchdog reset\n") ;     break ;
        case 4: printf("Software reset\n") ;     break ;
        case 8: printf("CPU Lock up\n") ;        break ;
        default: printf("Unknown [%d]\n", value[0]) ;     break ;
        }
        if (reboot_requested) {
            printf("Unexpected ASR Reboot. Rebooting MCU.\n") ;
            wait_ms(100) ;
            reboot_edge() ;
        }
        break ; 
    case ATTR_MCU_INTERFACE:
        printf("MCU Interface: ") ;
        switch(value[0]) {
        case 0:  printf("No MCU\n") ;    break ;
        case 1:  printf("SPI Slave\n") ; break ;
        case 2:  printf("UART\n") ;      break ;
        default: printf("Unknown\n") ;   break ; 
        }
        break ;
    case AF_SYSTEM_ASR_STATE:
        printf("ASR state: ") ;
        switch(value[0]) {
        case MODULE_STATE_REBOOTED:
            gLinked = false ;
            printf("Rebooted\n") ;
//            wait_ms(100) ; /* */
            if (edge_mgr_status == EDGE_MGR_RUNNING) {
                result = afero->getAttribute(ATTR_REBOOT_REASON) ;
                reboot_requested = true ;
//                reboot_edge() ;
            }
            break ;
        case MODULE_STATE_LINKED:
            if (gLinked == false) { /* new link established */
                result = afero->getAttribute(ATTR_LINKED_TIMESTAMP) ;
                if (result != afSUCCESS) {
                    printf("getAttriute for ATTR_LINKED_TIMESTAMP failed\n") ;
                }
            }
            gLinked = true ;
            printf("Linked\n") ;
            break ;
        case MODULE_STATE_UPDATING:
            gLinked = true ; 
            printf("Updating\n") ;
            if (display) {
                display->cls() ;
                display->locate(5, 5) ;
                display->printf("FW Updating...") ;
            }
            break ;
        case MOUDLE_STATE_UPDATE_READY:
            gLinked = false ;
            printf("Update ready - rebooting\n") ;
            if (display) {
                display->cls() ;
                display->locate(5, 5) ;
                display->printf("Rebooting...") ;
            }
            while(afero->setAttribute32(AF_SYSTEM_COMMAND, MODULE_COMMAND_REBOOT) != afSUCCESS) {
                afero->loop() ;
                wait_us(100) ;
            }
            reboot_edge() ;
            break ;
        default:
            break ;
        }
        break ;
    default:
        assignAttribute(requestId, attributeId, valueLen, value, false) ;
        break ;
    }      
    if ((pending != 0)&&(pending->request->requestId == requestId)) {
        printf("Request [%d] replied in %d sec!\n", requestId, pending->replied_time - pending->submit_time) ;
        delete pending ;
        pending = 0 ;
    }      
}