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: mbed CANBuffer Watchdog MODSERIAL mbed-rtos xbeeRelay IAP
Fork of SystemManagement by
outDiagnostics/outDiagnostics.cpp
- Committer:
- pspatel321
- Date:
- 2015-01-22
- Revision:
- 36:0afc0fc8f86b
- Parent:
- 34:18bcf276d3bf
- Child:
- 38:8efacce315ae
File content as of revision 36:0afc0fc8f86b:
#include "outDiagnostics.h"
osThreadId serialID = 0; // RTOS thread ID of thread_serialOut
const int max_charsPerLine = 80; // Max chars per line of printed information
// Use the txEmpty interrupt from MODSERIAL to pace the thread so it always runs as fast as possible
void empty(MODSERIAL_IRQ_INFO *q) {
osSignalSet(serialID, 1);
}
// Output a string to the MODSERIAL tx buffer, wait when buffer full
void AddtoBuffer(char *str, bool newline=true)
{
const int waitT = TX_SIZE * CHAR_TIME*1000; // Max wait time to empty the tx buffer = max time to send out all chars
int len = strlen(str);
int times = newline ? (len + 2) : (len); // If newline requested, add 2 chars for "\r\n"
for (int i = 0; i < times; i++) {
if (!pc.writeable()) { // Keep writing till it fills
osSignalSet((osThreadId)(tempData.wdtThreadId), 1<<3); // Check-in with watchdog thread
Thread::signal_wait(1, waitT); // Wait for empty signal from MODSERIAL tx empty interrupt, timeout in waitT ms
}
if (i < len) pc.putc(str[i]); // Print the string
else if (i == len) pc.putc('\r'); // Add carriage return
else if (i > len) pc.putc('\n'); // Add newline
}
}
// Print to internal string buffer, pad to maxLen chars and center it with char pad, str must be null terminated!
void padCenter(int LineLen, char *str, char pad)
{
static char line[max_charsPerLine+5]; // String buffer to work with one line at a time
int len = strlen(str); // Length of input string
int padL = (LineLen-len)/2; // How many pad chars needed on left side?
for (int i=0; i<padL; i++) line[i] = pad; // Fill in the left padding chars
strcpy(line+padL, str); // Copy to line string
for (int i = padL+len; i<LineLen; i++) line[i] = pad; // Fill remaining with padding chars
line[LineLen-1] = '\0'; // Add null terminator
AddtoBuffer(line);
}
// Macros for working with the strings
#define ADD_SPRINTF_LINE padCenter(max_charsPerLine, temp, ' '); // Cetner the string, then add newlines, and add to chunk
#define DIVIDER_LINE padCenter(max_charsPerLine, "", 196); // Generate a line full of divider char 196, add to chunk
#define TITLE(string) padCenter(max_charsPerLine, string, 196); // Generate a title line (centered, surrounded by char 196), add to chunk
#define BLANK_LINE padCenter(max_charsPerLine, "", ' '); // Generate a line full of spaces (blank), add to chunk
#define BOOL(VAR) (VAR)?"ERR":"OK"
// Generates the serial dashboard, uses MODSERIAL, self-paced (thread yields when buffer is full, resumes when empty)
void outDiagnostics::thread_serialOut(void const *args)
{
serialID = Thread::gettid(); // Record thread ID so empty() can signal this thread
char temp[max_charsPerLine+5]; // String buffer to sprintf into, max 1 line
pc.attach(&empty, MODSERIAL::TxEmpty); // Attach the tx empty interrupt which paces this thread
tempData.parseGoodChar = ' ';
tempData.inputStr[0] = 0;
Timer serialLoop; // Timer to track the serial loop time, since this thread paces itself
int serialTime_ms = 0;
serialLoop.reset();
serialLoop.start(); // Start the counter for tracking serial loop time
//const char barSpace[4] = { ' ', 179, ' ', 0 }; // Commonly used string with char 179
while(1) {
serialTime_ms = serialLoop.read_ms(); // Update loop timer, reset for next loop
serialLoop.reset();
sprintf(temp, "\033[0;0H\033[0;0H");
AddtoBuffer(temp, false); // Move to 0,0, do not append newline
DIVIDER_LINE
TITLE(" Penn Electric Racing - REV0 System Management Controller Dashboard ")
DIVIDER_LINE
int tempLength=0;
tempLength += sprintf(temp, "Command Input:%c %s%c", tempData.parseGoodChar, tempData.inputStr, 176); // Command input: print header, reply, input string, and cursor marker
for (int i = 0; i < max_charsPerLine - tempLength - 1; i++) { // Fill in the rest of the line with blanks
tempLength += sprintf(temp+tempLength, " "); // Append spaces
}
AddtoBuffer(temp); // Add this to the chunk
TITLE(" GLV Battery ")
BLANK_LINE
sprintf(temp, "Current: %4.3fA Capacity: %4.3fAh", data.glvCurrent, data.glvCapacity);
ADD_SPRINTF_LINE
sprintf(temp, "Amphours: %4.3fAh SOC: %5.3f Overcurrent: %s", data.glvAmphours, data.glvSOC, BOOL(data.glvOverCurrent));
ADD_SPRINTF_LINE
BLANK_LINE
TITLE(" DC-DC Converter ")
BLANK_LINE
char DCDC = data.dcdcStatus;
char dcdcModesStr[5][12] = {"INVALID","POWER-UP","POWER-DOWN","ON","OFF"};
int dcdcMode = 0;
if (DCDC & PowerUp) dcdcMode = 1;
else if (DCDC & PowerDown) dcdcMode = 2;
else if (DCDC & SetOn) dcdcMode = 3;
else if (!(DCDC & SetOn)) dcdcMode = 4;
sprintf(temp, "Active: %3s Mode: %10s AIRS: %6s StatusByte: 0x%02x", (DCDC & ConvOn)?"YES":"NO", dcdcModesStr[dcdcMode], CANdata.airsClosed?"CLOSED":"OPEN", DCDC);
ADD_SPRINTF_LINE
sprintf(temp, "Current: %5.2fA Overcurrent: %3s SensorFault: %3s", data.dcdcCurrent, BOOL(DCDC & OverCurrent), BOOL(DCDC & SensorFault));
ADD_SPRINTF_LINE
sprintf(temp, "StartFault: %3s StopFault: %3s CritErrors: %3s", BOOL(DCDC & FailStart), BOOL(DCDC & FailStop), BOOL(data.dcdcError));
ADD_SPRINTF_LINE
BLANK_LINE
TITLE(" PWM Channels ")
BLANK_LINE
sprintf(temp, "Actual: FAN1: %5.3f FAN2: %5.3f PUMP1: %5.3f PUMP2: %5.3f", data.dcdcFan1Duty, data.dcdcFan2Duty, data.dcdcPump1Duty, data.dcdcPump2Duty);
ADD_SPRINTF_LINE
sprintf(temp, "Requestd: FAN1: %5.3f FAN2: %5.3f PUMP1: %5.3f PUMP2: %5.3f", CANdata.dcdcFan1Duty, CANdata.dcdcFan2Duty, CANdata.dcdcPump1Duty, CANdata.dcdcPump2Duty);
ADD_SPRINTF_LINE
BLANK_LINE
TITLE(" IMD ")
BLANK_LINE
const char IMDstr[7][12] = {"OFF","NORMAL","UNDERVOLT","SPEEDSTART","ERROR","GROUNDFLT","INVALID"};
sprintf(temp, "Status: %10s Resistance: %7.0fkohm CritError: %3s",IMDstr[data.imdStatus], data.imdResistance/1e3, BOOL(data.imdError));
ADD_SPRINTF_LINE
BLANK_LINE
TITLE(" Latch Circuit Monitors ")
BLANK_LINE
char AMSerr = data.AMSlatchError;
char IMDerr = data.IMDlatchError;
sprintf(temp, "AMS - OK: %4s Latch: %3s SoftFault: %3s HardFault: %3s", (AMSerr & 1)?"LOW":"HIGH", BOOL(AMSerr & 2), BOOL(AMSerr & 4), BOOL(AMSerr & 8));
ADD_SPRINTF_LINE
sprintf(temp, "IMD - OK: %4s Latch: %3s SoftFault: %3s HardFault: %3s", (IMDerr & 1)?"LOW":"HIGH", BOOL(IMDerr & 2), BOOL(IMDerr & 4), BOOL(IMDerr & 8));
ADD_SPRINTF_LINE
BLANK_LINE
TITLE(" Shutdown Switches ")
BLANK_LINE
char switches = data.switchState;
const char switchNames[12][26] = {"FUSE","AMS LATCH","IMD LATCH","PCM RELAY","BRAKE PLAUSIBILITY RELAY","LEFT E-STOP","INERTIA SWITCH","BRAKE OVER-TRAVEL SWITCH","COCKPIT E-STOP","RIGHT E-STOP","HVD","TSMS"};
if (switches == 0) sprintf(temp, "All switches are CLOSED.");
else sprintf(temp, "%s is OPEN.", switchNames[switches-1]);
ADD_SPRINTF_LINE
BLANK_LINE
TITLE(" Telemetry ")
BLANK_LINE
sprintf(temp, "Channel 1 - MessagesIn: %5d MessagesOut: %5d", xbeeRelay.counterX1in, xbeeRelay.counterX1out);
ADD_SPRINTF_LINE
sprintf(temp, "Channel 2 - MessagesIn: %5d MessagesOut: %5d", xbeeRelay.counterX2in, xbeeRelay.counterX2out);
ADD_SPRINTF_LINE
BLANK_LINE
TITLE(" Miscellaneous ")
BLANK_LINE
sprintf(temp, "Temp: %5.1fC %3s canFault: %3s Watchdog: %3s ErrorCode: 0x%02x SerialTime: %3dms", data.internalTemp, BOOL(data.internalOverTemp), BOOL(data.canFault), BOOL(data.watchdogReset), data.errorFrame, serialTime_ms);
ADD_SPRINTF_LINE
// Erase screen every 5sec to remove glitches
static int count=0;
if (count%50==0) {
pc.printf("\033[2J"); // Clear the screen
}
count++;
}
}
// Macros to streamline can bus message formatting and sending
#define SEND_CAN(LEN, ID) msg.len=LEN; msg.id=ID; msg.type = CANData; msg.format = CANStandard; if (!can.txWrite(msg)) data.canFault = true; xbeeRelay.send(msg);
#define FLOAT(DATA, ID) *((float*)((void*)&msg.data[0])) = DATA; SEND_CAN(sizeof(float), ID)
#define UINT(DATA, ID) *((uint32_t*)((void*)&msg.data[0])) = DATA; SEND_CAN(sizeof(uint32_t), ID)
#define FLOAT_PAIR(DATA1, DATA2, ID) *((float*)((void*)&msg.data[0])) = DATA1; *((float*)((void*)&msg.data[4])) = DATA2; SEND_CAN(2*sizeof(float), ID)
#define UINT_PAIR(DATA1, DATA2, ID) *((uint32_t*)((void*)&msg.data[0])) = DATA1; *((uint32_t*)((void*)&msg.data[4])) = DATA2; SEND_CAN(2*sizeof(uint32_t), ID)
#define CHAR(DATA, ID) msg.data[0] = DATA; SEND_CAN(sizeof(char), ID)
void outDiagnostics::thread_canOut(void const *args)
{
CANMessage msg;
while(1) {
// Sys Mgmt Error Frame
CHAR(data.errorFrame, SYS_ERROR_ID)
// Xbee1 Counter
UINT_PAIR(xbeeRelay.counterX1in, xbeeRelay.counterX1out, SYS_XBEE1_ID)
// Xbee2 Counter
UINT_PAIR(xbeeRelay.counterX2in, xbeeRelay.counterX2out, SYS_XBEE2_ID)
// Internal temperature
FLOAT(data.internalTemp, SYS_TEMP_ID)
// GLV Battery
FLOAT(data.glvCurrent, SYS_GLV_CURRENT_ID)
FLOAT(data.glvCapacity, SYS_GLV_CAPACITY_ID)
FLOAT(data.glvAmphours, SYS_GLV_AH_ID)
FLOAT(data.glvSOC, SYS_GLV_SOC_ID)
// DC-DC Converter
FLOAT(data.dcdcCurrent, SYS_DCDC_CURRENT_ID)
CHAR(data.dcdcStatus, SYS_DCDC_STATUS_ID)
// PWM Channels
FLOAT_PAIR(data.dcdcFan1Duty, data.dcdcFan2Duty, SYS_PWM_FAN_ID)
FLOAT_PAIR(data.dcdcPump1Duty, data.dcdcPump2Duty, SYS_PWM_PUMP_ID)
// IMD
FLOAT(data.imdStatus, SYS_IMD_STATUS_ID)
CHAR(data.imdResistance, SYS_IMD_RESIST_ID)
// Latches
CHAR(data.IMDlatchError, SYS_IMD_LATCH_ID)
CHAR(data.AMSlatchError, SYS_AMS_LATCH_ID)
// Shutdown Switches
CHAR(data.switchState, SYS_SWITCHES_ID)
osSignalSet((osThreadId)(tempData.wdtThreadId), 1<<4); // Signal watchdog thread
Thread::wait(CAN_LOOP*1000);
}
}
