demo new haven display

Dependencies:   LCD Menu ButtonCtrl TimeManagement EventLog AddressMap emic2

ESCM 2000 Control and Display application provides interface for the LPC1768 processor boards with the ECSM 2000 system.

This application implements SW interface : - RX 485 Receive from physical system - RX 485 Interface to send toECOM / ESCM board - CAN Interface to send to ECOM / ESCM board - 4x40 LCD with menu controls - RTC configuration -EMIC2 Sound Card - GPIO Extender to push buttons etc

ESCMControlApp.cpp

Committer:
foxbrianr
Date:
2020-03-17
Revision:
10:f221dd1ef77b
Parent:
8:f15a5358883c

File content as of revision 10:f221dd1ef77b:

/**************************************************************************
 * @file     ESCMControlApp.cpp
 * @brief    Main class for wrapping the interface with ESCM Control 
 *           Application
 * @version: V1.0
 * @date:    9/17/2019

 *
 * @note
 * Copyright (C) 2019 E3 Design. All rights reserved.
 *
 * @par
 * E3 Designers LLC is supplying this software for use with Cortex-M3 LPC1768
 * processor based microcontroller for the ESCM 2000 Monitor and Display.  
 *  *
 * @par
 * THIS SOFTWARE IS PROVIDED "AS IS".  NO WARRANTIES, WHETHER EXPRESS, IMPLIED
 * OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE.
 * ARM SHALL NOT, IN ANY CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR
 * CONSEQUENTIAL DAMAGES, FOR ANY REASON WHATSOEVER.
 *
 ******************************************************************************/
#include "mbed.h"
#include "ESCMControlApp.h"
#include "SPI_MX25R.h"

// ---------------------------------------------------------------------
// External IO for the ESCM Io Board
// ---------------------------------------------------------------------
Serial escmRs485_Input(p9, p10, 9600); //tx,rx,baud
DigitalOut escmRs485_Mode (p11); //Transmit = 1, Receive = 0

// ---------------------------------------------------------------------
// External IO for the MicroComm ESCM Control Board
// ---------------------------------------------------------------------
Serial microCommRs485_Tx(p13, p14, 9600); //tx,rx,baud
DigitalOut microCommRs485_Mode (p12); //Transmit = 1, Receive = 0

CAN microCommCanItf (p30, p29, 50000);       // rx,tx
DigitalOut microCommCan_Mode (p25); //Silent Mode = 1, Normal = 0

// ---------------------------------------------------------------------
// External IO for the Speaker / EMIC2 board
// ---------------------------------------------------------------------
emic2 speaker(p28, p27); //serial RX,TX pins to emic
Mutex  sound_mutex;
// ---------------------------------------------------------------------
// External MEMORY for the FAULT AND ADDRESS MAPS (not used)
// ---------------------------------------------------------------------
SPI_MX25R spi_memory (p5, p6, p7, p8);

// ---------------------------------------------------------------------
ESCM_EventLog escmEventLog;

// ---------------------------------------------------------------------
AddressMap  addressMap;

// ---------------------------------------------------------------------
RealTimeClock rtc;

// ---------------------------------------------------------------------
// queue for button presses 
// ---------------------------------------------------------------------
CircularBuffer<event_t, 64> event_queue;

// ---------------------------------------------------------------------
// queue for playing sound 
// ---------------------------------------------------------------------
CircularBuffer<playbackMessage_t, 10> playback_queue;


// ---------------------------------------------------------------------
void setCurrentTime (char* timeBuf)
{
    
    time_t rawtime;
    struct tm * timeinfo;
    
    time ( &rawtime );
    timeinfo = localtime ( &rawtime );
                
    int hours = timeinfo->tm_hour;
    int mins  = timeinfo->tm_min;
    int secs  = timeinfo->tm_sec;
    
    int years   = timeinfo->tm_year + 1900;
    int months  = timeinfo->tm_mon + 1 ;
    int days    = timeinfo->tm_mday;
    
    sprintf(timeBuf,"%0d/%0d/%04d %02d:%02d:%02d", 
        months,days,years,hours,mins,secs );
        
    printf("NEW %s\n", timeBuf);
    
}

// ---------------------------------------------------------------------
void ESCMControlApp::init()
{
    // set on power, should not change
    escmRs485_Mode      = 0; // Receive
    microCommRs485_Mode = 1; // Transmit
    microCommCan_Mode   = 0; // Normal mode (turn on CNA transeiver)
    
    
    escmEventLog.init();
    
    addressMap.init();
    
    cur_address = 0;
    
    if (escmEventLog.size()>0)
    {
        cur_address = escmEventLog.index(0)->address;
    }
}


// ---------------------------------------------------------------------
void ESCMControlApp::update(void)
{
    static int counter     =  0;
    
    char value;
    int msgCount       = 0;
    int nChar          = 0;
    int new_address    = 0;
    bool update_needed = 0;
    
    char dataRxBuffer[4];
    
    if(escmRs485_Input.readable() ) {
        //rx485Message();
        while (msgCount < 4 && (value = escmRs485_Input.getc())) {
            //printf("%02x ",value);
            dataRxBuffer[nChar++]=value;
            if(nChar==4) { // read 4 characters
            
                printf("%02x ",dataRxBuffer[0]);
                printf("%02x ",dataRxBuffer[1]);
                printf("%02x ",dataRxBuffer[2]);
                printf("%02x ",dataRxBuffer[3]);
                
                if (dataRxBuffer[2] == 0xd && dataRxBuffer[3] == 0xa) {
                    new_address = 10*(dataRxBuffer[0] -0x30) + (dataRxBuffer[1] -0x30);
                    printf("RX: %d\n\n",new_address);
                    update_needed = 1; // receive at least 1
                    nChar       = 0;
                    dataRxCnt++;
                    msgCount++;
                    break;
                }
            }
            
        }
    }
    
    
    if (update_needed)
    {
        if (new_address >= 0 && new_address < 100  )
        {
            cur_address = new_address;
            //printf("New Addr=%d\n",cur_address);
            postEvent(cur_address,0);
            counter     = 0;
            new_address = -1;
        } else {
            
            for (int i=0;i<4;i++)
                printf( "%0x ", dataRxBuffer[i]);
            
            printf("ERROR: INVALID_ADDR=[%d] [%d]\n",
                cur_address,
                new_address);
                
        }
    }

    if (counter == 0 ) {
        relayMessage (cur_address);
        counter = 5; //send .5 sec
        dataTxCnt++;
    }
    counter--;
}

/*************************************************************************/
void ESCMControlApp::processSoundQueue ()
{
    playbackMessage_t e;
    
    if (playback_queue.empty())
        return;
        
    // drain the queue
    while (!playback_queue.empty()) {
        playback_queue.pop(e);
        ThisThread::sleep_for(500);
    }
    
    // emic is very slow so play last message
    speaker.speakf("S");  //Speak command starts with "S"
    speaker.speakf(e.message);  // Send the desired string to convert to speech
    speaker.speakf("\r"); //marks end of speak command
    speaker.ready();      //ready waits for speech to finish from last command with a ":" response
    
}
/*************************************************************************/
void ESCMControlApp::info (char *format, ...)
{
    char buffer[128];
    va_list args;
    va_start(args, format);
    vsprintf(buffer,format,args);
    printf("\nINFO:%s",buffer);
    va_end(args);
}
/*************************************************************************/
void ESCMControlApp::warning (char *format, ...)
{
    
    char buffer[128];
    va_list args;
    va_start(args, format);
    vsprintf(buffer,format,args);
    printf("\nWARNING:%s",buffer);
    va_end(args);
}
/*************************************************************************/
void ESCMControlApp::error (char *format, ...)
{
    char buffer[128];
    va_list args;
    va_start(args, format);
    vsprintf(buffer,format,args);
    
    printf("\nERROR:%s",buffer);
    va_end(args);
}

/*************************************************************************/
void ESCMControlApp::say (int address)
{
    char * desc = addressMap.getDescription(address);
    if (strlen(desc) > 0) {
        escmController.say("%s is open", desc );
    } else {
        escmController.say("Unit %d is open", address );
    }
}
/*************************************************************************/
void ESCMControlApp::say (char *format, ...)
{
    
    playbackMessage_t e;
    
    char buffer[128];
    va_list args;
    va_start(args, format);
    vsprintf(e.message,format,args);
    // note: immediately going to the emic blocks calling thread 
#if 0
    speaker.speakf("S");//Speak command starts with "S"
    speaker.speakf(e.message);  
    speaker.speakf("\r"); 
    speaker.ready(); 
#else

    if (!playback_queue.full()) {
        playback_queue.push(e);
    } else {
        printf("Sound Queue Full\n");
    }
#endif
    
    
    va_end(args);
}

/*************************************************************************
 * The following function is called with a new event is received to
 * update the related parts
 *************************************************************************/
void ESCMControlApp::postEvent (uint16_t address, uint16_t source)
{
    if ( address >= 0 && address < 100 )
    {
        escmEventLog.add(address, source); 
        ESCMControlApp::refresh_display();
        
#if 0
        ESCMControlApp::say("Unit %d is open", address);
#else
        ESCMControlApp::say("%s is open", 
            addressMap.getDescription(address));
#endif

    }
    else
    {
        //ignore it
    }
}

/*************************************************************************/

void ESCMControlApp::refresh_display(void)
{
    // Note: we only refresh the event log display to avoid LCD flashing
    // --
#if 1
    showEvents.update_needed=1;
#else
    Menu::getCurrentMenu()->update_needed=1
#endif
}

/*************************************************************************
 * The following function get time 
 *************************************************************************/
void ESCMControlApp::getTime(char *timeBuf)
{
    time_t rawtime;
    struct tm * timeinfo;
    
    time ( &rawtime );
    timeinfo = localtime ( &rawtime );
                
    int hours = timeinfo->tm_hour;
    int mins  = timeinfo->tm_min;
    int secs  = timeinfo->tm_sec;
    
    int years   = timeinfo->tm_year + 1900;
    int months  = timeinfo->tm_mon + 1 ;
    int days    = timeinfo->tm_mday;
    
    sprintf(timeBuf,"%0d/%0d/%04d %02d:%02d:%02d", 
        months,days,years,hours,mins,secs );
        
    printf("TIME: %s\n", timeBuf);
}

/*************************************************************************
 * The following functdion sets time which set the RTC
 *************************************************************************/
void ESCMControlApp::setTime(int hours, int mins, int sec, int months, int days, int years )
{
    
    struct tm t;
    
    t.tm_hour = hours;
    t.tm_min  = mins;
    t.tm_sec  = 0;
    
    t.tm_year = years - 1900;
    t.tm_mon  = months - 1;
    t.tm_mday = days;
    
    // set the time
    set_time(mktime(&t));
}

/*************************************************************************/
void ESCMControlApp::relayMessage (int address) 
{
    
    //printf("Current_Address is %d \n\r", cur_address );
    if (address >= 0 && address < 100  )
    {
        tx485Message(address); //send on rs485
#if 1 // currently 501 & 502 control output
        txCanMessage501(address);//send on can
        txCanMessage502(address);
#else
        txCanMessage580(address);
#endif
    } 
    else 
    {
        printf("ERROR: relayMessage INVALID_ADDR=%0x\n",  address);
    }
}
/*************************************************************************
 * The following information is from the MicroComm_gen_input.doc
 * 
 * Message is always 12 bytes with Check Sum
 *      Byte 1  : 40
 *      Byte 2  : Indictator 
 *           arrival_dn_arrow    :1, bit 1
 *           arrival_up_arrow    :1, bit 0
 *
 *      Byte 3  : Scan_slot. 
 *      Byte 4  : Message_number.
 *      Byte 5  : Indictor
 *           traveling dn arrow  :1, bit 1
 *           traveling up arrow  :1, bit 0
 *
 *      Byte 6  : ASCII floor char, msb
 *      Byte 7  : ASCII floor char, 
 *      Byte 8  : ASCII floor char, lsb
 *           Note:  floor ASCII range is 0x30 through 0x5a (‘0’ – ‘Z’). 
 *               0x3a  through 0x40 are special characters with 
 *               0x3b being the space or blank character(semicolon)
 *               0x3d is normally displayed as a minus character.
 *
 *
 *      Byte 9  : ASCII msg char, msb
 *      Byte 10 : ASCII msg char, 
 *      Byte 11 : ASCII msg char, lsb
 *           Note:  message ASCII range is 0x30 through 0x5b (‘0’ – ‘[’). 
 *              0x3a  through 0x40 are special characters with 
 *              0x3b being the space or blank character(semicolon)
 *              0x5d is sent when there is no message (instead of spaces).
 *
 *
 *      Byte 12 : checksum
 *
 * note messages are sent .5 - 1 sec using 485 Bus
 *************************************************************************/
void ESCMControlApp::tx485Message(int address) 
{

    int sum =0;
    char dataTxBuffer[12];

    if (address >= 0 && address < 100 ) {

        sum += dataTxBuffer[0]  = 0x40;  // fixed value
        sum += dataTxBuffer[1]  = 0x01;
        sum += dataTxBuffer[2]  = address; // floor number
        sum += dataTxBuffer[3]  = 0x0;
        sum += dataTxBuffer[4]  = 0x0;
        //---
        sum += dataTxBuffer[5]  = 0x0;
        sum += dataTxBuffer[6]  = (0x30 + address / 10);
        sum += dataTxBuffer[7]  = (0x30 + address % 10); 
        //---
        sum += dataTxBuffer[8]   = 0x0;
        sum += dataTxBuffer[9]   = 'F';
        sum += dataTxBuffer[10]  = 'X'; 
        
        sum += dataTxBuffer[11] = (char)(~sum +1);

        for(int i= 0; i<12; i++) {
            microCommRs485_Tx.putc(dataTxBuffer[i]);
        }
        
    } else {
        warning("tx485: invalid address %d",address );
    }
    
}


/*************************************************************************
 * The following information is from the ElevCANtoMC
 * for CAN Message 0x501
 *
 *      Byte 1
 *       Front arrows
 *       Bit 0 – Front arrival arrow up
 *       Bit 1 – Front arrival arrow down
 *       Bit 2 – Play Strobe
 *       Bit 3-5 – Reserved
 *       Bit 6 – Fire Warning Lamp
 *      Byte 2
 *       Floor Number.  A floor number of 0 is not a valid.
 *      Byte 3
 *       Priority message number
 *      Byte 4
 *       Travel and rear arrows
 *       Bit 0 – Travel arrow up
 *       Bit 1 – Travel arrow down
 *       Bit 2 – unused
 *       Bit 3 – Passing Chime
 *       Bit 4 – Message Level Low Bit
 *       Bit 5 – Message Level High Bit
 *       Bit 6 – Rear arrival arrow up
 *       Bit 7 – Rear arrival arrow down
 *      Byte 5-8
 *        Reserved
 *
 *
 * note messages are to come .5 - 1 sec using 50K CAN Bus
 ********************************************************************/
void ESCMControlApp::txCanMessage501 (int address)
{
    static int counterTx = 0;
    char data[8];


    if (address >= 0 && address < 100 ) {

        data[0]  = (counterTx%2)?0x1:0x2; // toggle  arrow
        data[1]  = (uint8_t)address;      // numerical floor #
        data[2]  = 0x0;
        data[3]  = (counterTx%2)?0x1:0x2; // toggle travel arrow
        data[4]  = 0x0;
        data[5]  = 0x0;
        data[6]  = 0x0;
        data[7]  = 0x0;

        if(microCommCanItf.write(CANMessage(0x501, data, 8))) {
            //printf("Message 501:(%d) sent via CAN: %d\n\r",counterTx, address);
            counterTx++;
        } else {
            //printf("Message 501: Reset\n\r");
            microCommCanItf.reset();
        }
        
    } else {
        warning("tx501: invalid address %d",address );
    }
    
}
/*************************************************************************
 * The following information is from the ElevCANtoMC 
 * for CAN Message 0x502
 *      Byte 1
 *       Floor marking character (leftmost)
 *      Byte 2
 *       Floor marking character
 *      Byte 3
 *       Floor marking character (rightmost)
 *      Byte 4
 *       Message marking character (leftmost)
 *      Byte 5
 *       Message marking character
 *      Byte 6
 *       Message marking character (rightmost)
 *      Byte 7
 *       Lantern Position (ThyssenKrupp)
 *      Byte 8
 *       Unused
 *
 * note messages are to come .5 - 1 sec using 50K CAN Bus
 ********************************************************************/
void ESCMControlApp::txCanMessage502 (int address)
{
    static int counterTx = 0;
    char data[8];
    
    if (address >= 0 && address < 100 ) {
        
        data[0]  = 0x0;
        data[1]  = (0x30 + address / 10); // assumes address is 0-9 range
        data[2]  = (0x30 + address % 10); // assumes address is 0-9 range
        data[3]  = 0x0;
        data[4]  = (0x30 + address / 10); // assumes address is 0-9 range
        data[5]  = (0x30 + address % 10); // assumes address is 0-9 range
        data[6]  = 'F';
        data[7]  = 0x0;


        if(microCommCanItf.write(CANMessage(0x502, data, 8))) {
            //printf("Message 502: (%d) sent via CAN: %d\n\r",counterTx, address);
            counterTx++;
        } else {
            microCommCanItf.reset();
        }

    } else {
        warning("tx503: invalid address %d",address );
    }
}

/*************************************************************************
 * The following information is from the ElevCANtoMC 
 * for CAN Message 0x580
 *      Byte 1
 *       Floor Number.  A floor number of 0 is not a valid floor position and should only be used when translating from a source that does not provide numeric position data.
 *      Byte 2
 *       Floor marking character (leftmost) [the MICRO COMM® converter does not support this character]
 *      Byte 3
 *       Floor marking character
 *      Byte 4
 *       Floor marking character
 *      Byte 5
 *       Floor marking character (rightmost)
 *      Byte 6
 *       Bit 0 – Front in-car arrow up
 *       Bit 1 – Front in-car arrow down
 *       Bit 2 – Rear in-car arrow up
 *       Bit 3 – Rear in-car arrow down
 *       Bit 4 – Travel arrow up
 *       Bit 5 – Travel arrow down
 *       Bit 6 – Passing chime
 *       Bit 7 – Unused
 *      Byte 7
 *       Hall Arrows
 *       Bit 0 – Front arrival arrow up
 *       Bit 1 – Front arrival arrow down
 *       Bit 2 – Front gong up
 *       Bit 3 – Front gong down
 *       Bit 4 – Rear arrival arrow up
 *       Bit 5 – Rear arrival arrow down
 *       Bit 6 – Rear gong up
 *       Bit 7 – Rear gong down
 *       Byte 8
 *       Target display number  (This is not used by most controllers.  
 *       Just leave this set to 0xFF for basic controller operation).  
 *       If this is used, it only applies to data located in Byte 7.
 *
 * note messages are to come .5 - 1 sec using 50K CAN Bus
 ********************************************************************/
void ESCMControlApp::txCanMessage580 (int address)
{
    static int counterTx = 0;
    char data[8];

    if (address >= 0 && address < 100 ) {

        data[0]  = address;
        data[1]  = 0x0;
        data[2]  = 0x0;
        data[3]  = (0x30 + address / 10);
        data[4]  = (0x30 + address % 10);
        data[5]  = (counterTx%2)?0x1:0x2;
        data[6]  = 0x0A;//front down and gong down
        data[7]  = 0xFF;

        if(microCommCanItf.write(CANMessage(0x580, data, 8))) {
            //printf("Message 580: (%d) sent via CAN: %d\n\r",counterTx, address);
            counterTx++;
        } else {
            microCommCanItf.reset();
        }


    } else {
        warning ("tx580: invalid address %d",address );
    }
    
}