Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependencies: LCD Menu ButtonCtrl TimeManagement EventLog AddressMap emic2
ESCMControlApp.cpp
- Committer:
- foxbrianr
- Date:
- 2019-09-17
- Revision:
- 6:010ceb99f7b0
- Parent:
- 5:65f21c0b6b79
- Child:
- 7:0244f1a26545
File content as of revision 6:010ceb99f7b0:
/**************************************************************************
* @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;
/* for incoming messages */
CircularBuffer<event_t, 64> message_queue;
/* for button presses */
CircularBuffer<event_t, 64> event_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 < 10 && (value = escmRs485_Input.getc())) {
//printf("%02x ",value);
dataRxBuffer[nChar++]=value;
if(nChar==4) { // read 4 characters
if (dataRxBuffer[2] == 0xd && dataRxBuffer[3] == 0xa) {
new_address = 10*(dataRxBuffer[0] -0x30) + (dataRxBuffer[1] -0x30);
printf("ADDR = %d ",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]);
error("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::processMessageQueue ()
{
event_t e;
if (message_queue.empty())
return;
while (!message_queue.empty()) {
message_queue.pop(e);
tx485Message(e.event);
ThisThread::sleep_for(100);
}
}
/*************************************************************************/
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");
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");
va_end(args);
}
/*************************************************************************/
void ESCMControlApp::error (char *format, ...)
{
char buffer[128];
va_list args;
va_start(args, format);
vsprintf(buffer,format,args);
printf("\n---------------------------------------------------");
printf("\nERROR:%s");
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);
}
/*************************************************************************/
void ESCMControlApp::postEvent (uint16_t address)
{
event_t e;
e.event = address;
if (!message_queue.full()) {
message_queue.push(e);
} else {
printf("Message Queue Full\n");
}
}
/*************************************************************************
* 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
{
error("relay: INVALID_ADDR=%d\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 );
}
}