#pragma warning(disable : 4706) // assignment within conditional expression #pragma warning(disable : 4996) // _CRT_SECURE_NO_DEPRECATE #include "stdafx.h" #include #include #include #include #include #include #include #include #include "SerialPort\SerialPort.h" #include "AVRInterface.h" //#include "AVRCommandDecoder.h" #include "ConsoleHandler.h" enum { COM_NO_PORT = -1, COM_MIN_PORT = 1, COM_MAX_PORT = 99 }; CSerialPort avrPort; int avrOnPort = COM_NO_PORT; // Serial Port 1, 2, ... taking note that some USB adapters can be up toward channel 11, 12, ... unsigned avrBaud = 9600; bool verboseMode = false; const uint32_t retryInterval = 1000; // ms AVRInterface *avr; // Each DT is the hex-character from the stream // // typedef struct { uint8_t DT0; // * Baud Rate '@' uint8_t DT1; // * Receive Buffer 'E' uint8_t DT2; // * Receive Buffer '0' uint8_t DT3; // * '1' uint8_t DT4; // * Command Timeout '9' uint8_t DT5; // * '0' uint8_t DT6; // * '0' uint8_t DT7; // * System '0':Ok, '1':Busy uint8_t DT8; // * Power 0:Off, 1:On uint8_t DT9; // Input 0: Phono, 1:CD, 2:Tuner, 3:CD-R, 4:MD-Tape, 5:DVD, 6:D-TV, 7:Cbl, 9:VCR1, A:VCR2 uint8_t DT10; // 6ch input 0:Off, 1:On uint8_t DT11; // Input Mode 0:AUTO, 2:DTS, 4:Analog, 5:Analog Only uint8_t DT12; // Audio Mute 0:Off, 1:On uint8_t DT13; // Zone2 Input 0: PHONO / 1: CD / 2: TUNER / 3: CD-R / 4: MD-TAPE / 5: DVD / 6: D-TV-LD / 7: CBL-SAT / 9: VCR1 / A: VCR2-DVR / C: V-AUX uint8_t DT14; // Zone2 Mute 0: OFF / 1: ON uint8_t DT15; // Master Volume Upper 4 bit uint8_t DT16; // Master Volume Lower 4 bit uint8_t DT17; // Zone2 Volume Upper 4 bit uint8_t DT18; // Zone2 Volume Lower 4 bit uint8_t DT19; // Program Upper 4 bit uint8_t DT20; // Program Lower 4 bit uint8_t DT21; // Effect 0: OFF / 1: ON uint8_t DT22; // 6.1/ES key status 0: OFF / 1: MATRIX ON / 2: DISCRETE ON / 3: AUTO uint8_t DT23; // OSD* 0: FULL / 1: SHORT / 2: OFF uint8_t DT24; // Sleep 0: 120 / 2: 90 / 3: 60 / 4: 30 / 5: OFF uint8_t DT25; // Tuner Page 0: Page A / 1: Page B / 2: Page C / 3: Page D / 4: PageE uint8_t DT26; // Tuner No. 0: No.1 / 1: No.2 / 2: No.3 / 3: No.4 / 4: No.5 / 5: No.6 / 6: No.7 / 7: No.8 uint8_t DT27; // Night mode 0: OFF / 1: ON uint8_t DT28; // Care uint8_t DT29; // Speaker relay A 0: OFF / 1: ON uint8_t DT30; // Speaker relay B 0: OFF / 1: ON uint8_t DT31; // Playback 0: 6ch input / 1: Analog / 2: PCM / 3: DD*(except 2.0) / 4: DD(2.0) / 5: DD.Karaoke / 6: DD.EX / 7: DTS / 8: DTS-ES / 9: Other DIGITAL / A: DTS Analog Mute / B: DTS ES Discrete uint8_t DT32; // Fs 0: Analog / 1: 32kHz / 2: 44.1kHz / 3: 48kiHz / 4: 64kHz / 5: 88.2kHz / 6: 96kHz / 7: Unknown B: DTS 96/24 uint8_t DT33; // EX/ES playback 0: OFF / 1: MATRIX ON / 2: DISCRETE ON uint8_t DT34; // Thr / Bypass 0: Normal / 1: Bypass uint8_t DT35; // RED dts 0: Release / 1: Wait uint8_t DT36; // Head Phone 0: OFF / 1: ON uint8_t DT37; // TUNER BAND 0: FM / 1: AM uint8_t DT38; // TUNER TUNED 0: NOT TUNED / 1: TUNED uint8_t DT39; // DC1 Control Out 0: LOW / 1: HIGH uint8_t DT40; // Don’t care uint8_t DT41; // Don't Care uint8_t DT42; // 0-2 DC1 TRG Ctrl. 0: Zone1 / 1: Zone2 / 2: Zone1&2 uint8_t DT43; // 0/1 dts 96/24 0: OFF / 1: ON uint8_t DT44; // 0-2 DC2 TRG Ctrl. 0: Zone1 / 1: Zone2 / 2: Zone1&2 uint8_t DT45; // 0/1 DC2 Trigger 0: LOW / 1: HIGH uint8_t DT46; // SP B set 0: Zone1 / 1: Zone2 uint8_t DT47; // Zone 2 SP out 0: OFF / 1: ON uint8_t DT48; // MAIN R Upper 4bit uint8_t DT49; // Lower 4bit uint8_t DT50; // MAIN L Upper 4bit uint8_t DT51; // Lower 4bit uint8_t DT52; // CENTER Upper 4bit uint8_t DT53; // Lower 4bit uint8_t DT54; // REAR R Upper 4bit uint8_t DT55; // Lower 4bit uint8_t DT56; // REAR L Upper 4bit uint8_t DT57; // Lower 4bit uint8_t DT58; // SUR BACK Upper 4bit uint8_t DT59; // R Lower 4bit uint8_t DT60; // SUR BACK Upper 4bit uint8_t DT61; // L Lower 4bit uint8_t DT62; // FRONT R Upper 4bit uint8_t DT63; // Lower 4bit uint8_t DT64; // FRONT L Upper 4bit uint8_t DT65; // Lower 4bit uint8_t DT66; // SWFR 1 Upper 4bit uint8_t DT67; // Lower 4bit uint8_t DT68; // Don't Care uint8_t DT69; // Don't Care uint8_t DT70; // Don't Care uint8_t DT71; // Don't Care uint8_t DT72; // Don't Care uint8_t DT73; // Don't Care uint8_t DT74; // LFE Lvl. SP Upper 4bit uint8_t DT75; // Lower 4bit uint8_t DT76; // LFE Lvl. HP Upper 4bit uint8_t DT77; // Lower 4bit uint8_t DT78; // Audio Delay Upper 4bit uint8_t DT79; // Lower 4bit uint8_t DT80; // Don't Care uint8_t DT81; // Don't Care uint8_t DT82; // Don't Care uint8_t DT83; // Don't Care uint8_t DT84; // Input mode set 0: AUTO / 1: LAST uint8_t DT85; // Dimmer 0: -4 / 1: -3 / 2: -2 / 3: -1 / 4: 0 uint8_t DT86; // OSD Message uint8_t DT87; // OSD shift Upper 4bit uint8_t DT88; // Lower 4bit uint8_t DT89; // Glay back 0: OFF / 1: AUTO uint8_t DT90; // Video conversion 0: OFF / 1: ON uint8_t DT91; // D. Range SP 0: MAX / 1: STD / 2: MIN uint8_t DT92; // HP 0: MAX / 1: STD / 2: MIN uint8_t DT93; // Zone 2 vol. Out uint8_t DT94; // Don't Care uint8_t DT95; // Memory guard 0: OFF / 1: ON uint8_t DT96; // SP set Center 0: Large / 1: Small / 2: None uint8_t DT97; // Main 0: Large / 1: Small uint8_t DT98; // Rear L/R 0: Large / 1: Small / 2: None uint8_t DT99; // Rear CT 0: Large / 1: Small / 2: None uint8_t DT100; // Front 0: Yes / 1: None uint8_t DT101; // LFE/BASS 0: SWFR / 1: Main / 2: Both uint8_t DT102; // 6CH Center 0: Center / 1: Main uint8_t DT103; // SWFR 0: SWFR / 1: Main uint8_t DT104; // Main level 0: Normal / 1: -10dB uint8_t DT105; // Test mode 0: OFF / 1: Dolby / 2: DTS uint8_t DT106; // Don't Care uint8_t DT107; // LVL 6CH MAIN L Upper 4bit uint8_t DT108; // Lower 4bit uint8_t DT109; // MAIN R Upper 4bit uint8_t DT110; // Lower 4bit uint8_t DT111; // CENTER Upper 4bit uint8_t DT112; // Lower 4bit uint8_t DT113; // SL Upper 4bit uint8_t DT114; // Lower 4bit uint8_t DT115; // SR Upper 4bit uint8_t DT116; // Lower 4bit uint8_t DT117; // SBL Upper 4bit uint8_t DT118; // Lower 4bit uint8_t DT119; // SBR Upper 4bit uint8_t DT120; // Lower 4bit uint8_t DT121; // FRONT L Upper 4bit uint8_t DT122; // Lower 4bit uint8_t DT123; // FRONT R Upper 4bit uint8_t DT124; // Lower 4bit uint8_t DT125; // SWFR Upper 4bit uint8_t DT126; // Lower 4bit uint8_t DT127; // 0 - C Z3 Input uint8_t DT128; // 0/1 Z3 Mute uint8_t DT129; // 0 - F Z3 Volume Upper 4bit uint8_t DT130; // 0 - F Lower 4bit uint8_t DT131; // Don't Care uint8_t DT132; // MULTI_CH SELECT 00:6CH / 01:8CH TUNER / 02: 8CH CD / 04: 8CH CD-R / 05: 8CH DVD / 06: DTV / 07: 8CH CBL/SAT / 09: 8CH VCR1 / 0A: VCR2/DVR / 0C: VAUX uint8_t DT133; // MULTI_CH SURROUND to 00: Surround / 01: Main uint8_t DT134; // SP SET SW1 00: L-R / 01: F-R / 02: NONE uint8_t DT135; // SP SET CROSSOVER 00: 40Hz / 01: 60Hz / 02: 80Hz / 03: 90Hz / 04: 100Hz / 05: 110Hz / 06: 120Hz / 07: 160Hz / 08: 200Hz uint8_t DT136; // COMPONENT OSD 00: OFF / 01: ON uint8_t DT137; // PB/SB SELECT 00: PR / 01: SB uint8_t DT138[100]; // From here on is just buffer in case it sends more data } AVR_Configuration_T; typedef struct { uint8_t type[5]; // Model ID uint8_t version; // A-Z uint8_t length[2]; // 1 - 255 } AVR_StatusHeader_T; typedef struct { bool headerValid; bool configValid; AVR_StatusHeader_T header; AVR_Configuration_T config; } AVR_Status_T; //AVR_StatusHeader_T avrStatusHeader; //AVR_Configuration_T avrConfigData; AVR_Status_T avrStatus; typedef enum { EXIT_OK = 0, EXIT_IllegalOption, EXIT_InternalSanityCheckFail, EXIT_ExternalQuit, EXIT_AbnormalError, } ExitCode; constexpr auto MAXTEXTLEN = 512; char progname[MAXTEXTLEN]; int AttachToSerialPort(); int DetachSerialPort(); // SerialSend // // Try to send the message. // // @param[in] p is a pointer to the message to send // @param[in] len is the count of bytes in the message // @returns true if the serial interface accepted it. // bool SerialSend(const uint8_t *p, uint16_t len); // Just big enough to hold an OSD message which is a 1 message command, 4 messages with text #define SERIALQUEUESIZE 5 typedef struct { uint8_t *message; uint16_t len; } SerialQueue_T; static SerialQueue_T serialQueue[SERIALQUEUESIZE]; static int serialQueueCount = 0; // ProcessSerialQueue // // If there are parameters passed, insert a message into the queue. // Process the queue (with zero or more messages) to send // // @param[in] p is a pointer to the message to send // @param[in] len is the count of bytes in the message // @returns false if the queue (which is a fixed size) is full. // //bool ProcessSerialQueue(const uint8_t *p = NULL, uint16_t len = 0); // ProcessSerialReceive // // See if anything came in, and if so, process it. // // @returns true if a message was processed. // SerialQueue_T ProcessSerialReceive(void); void HandleMessage(uint8_t *szBuffer, uint32_t num); void EnumerateComPorts(); unsigned long Hex2Dec(uint8_t *p, int dig); //void ShowAllStatusInfo(); void GetAndSendCustomMessage(); void GetAndSendOSDMessage(); bool UserExitRequested = false; bool SystemExitRequested = false; typedef struct { const char *pMsg; uint16_t MsgLen; } Message_T; typedef struct { const char Char; const char *pDescription; Message_T TxMsg; } UserCmd_T; // Runtime commands from the keyboard to send to the AVR // const UserCmd_T UserCommands[] = { { 'R', "Ready", {"\x11" "000" "\x03", 5 } }, //{ 'P', "Power On", {"\x02" "07A1D" "\x03", 7} }, //{ 'p', "Power Off", {"\x02" "07A1E" "\x03", 7} }, { '1', "Zone 1 On", {"\x02" "07E7E" "\x03", 7} }, { '!', "Zone 1 Standby", {"\x02" "07E7F" "\x03", 7} }, { '2', "Zone 2 On", {"\x02" "07EBA" "\x03", 7} }, { '@', "Zone 2 Standby", {"\x02" "07EBB" "\x03", 7} }, { '3', "Zone 3 On", {"\x02" "07AED" "\x03", 7} }, { '#', "Zone 3 Standby", {"\x02" "07AEE" "\x03", 7} }, { 'V', "Vol 1 Up", {"\x02" "07A1A" "\x03", 7} }, { 'v', "Vol 1 Down", {"\x02" "07A1B" "\x03", 7} }, { 'M', "Vol 1 Mute", {"\x02" "07EA2" "\x03", 7} }, { 'm', "Vol 1 UnMute", {"\x02" "07EA3" "\x03", 7} }, { 'O', "OSD Test Message", }, { 'T', "Tuning freq rqst", {"\x02" "22000" "\x03", 7} }, { 'Y', "Zone 1 Vol rqst", {"\x02" "22001" "\x03", 7} }, { 'y', "Zone 2 Vol rqst", {"\x02" "22002" "\x03", 7} }, { 'U', "Zone 1 Input rqst", {"\x02" "22003" "\x03", 7} }, { 'u', "Zone 2 Input rqst", {"\x02" "22004" "\x03", 7} }, { 'I', "Zone 3 Vol rqst", {"\x02" "22005" "\x03", 7} }, { 'i', "Zone 3 Input rqst", {"\x02" "22006" "\x03", 7} }, //{ 'J', "Dual Mono - Main", {"\x02" "07E93" "\x03", 7} }, //{ 'K', "Dual Mono - Sub", {"\x02" "07E94" "\x03", 7} }, //{ 'L', "Dual Mono - All", {"\x02" "07E95" "\x03", 7} }, { ':', "Custom Message" }, { '/', "Show All" }, { '?', "Help on runtime commands" }, { 'S', "Set RTS" }, { 's', "Clear RTS" }, { '\x1B', "Quit App" }, }; typedef struct { //uint8_t type; // 0 to 4, and 0xFF for don't care //uint8_t guard; // 0 to 2, and 0xFF for don't care uint8_t rCmd; uint8_t configOffset; // offset into the Config DT0 - DT155 array, or 0xFF for no action uint8_t numToTransfer; // number of bytes to transfer into the Config array, or 0 for no action bool showAll; // true to call ShowAllStatusInfo after processing const char *TextFormatter; // used primarily for development printf(TextFormatter, value) const char *(*fncValueToText)(uint8_t rDat); } MessageHandler_T; void UserCommandsSanityCheck() { uint8_t usedKey[256] = { 0 }; bool fail = false; size_t numCommands = sizeof(UserCommands) / sizeof(UserCmd_T); for (size_t i = 0; i < numCommands; i++) { usedKey[UserCommands[i].Char]++; if (usedKey[UserCommands[i].Char] > 1) { printf("UserCommandsSanityCheck: Command key %c used more than once\n", UserCommands[i].Char); fail = true; } } if (fail) { printf("***** UserCommandsSanityCheck: Errors found in UserCommands table\n"); exit(EXIT_InternalSanityCheckFail); } } int CS_KeyHit() { int h = _kbhit(); return h; } int CS_GetChar() { int k = _getch(); // More leadin chars return k; } BOOL WINAPI ControlIntercept(DWORD CtrlType) { switch (CtrlType) { case CTRL_C_EVENT: case CTRL_CLOSE_EVENT: UserExitRequested = true; return FALSE; break; case CTRL_BREAK_EVENT: case CTRL_LOGOFF_EVENT: case CTRL_SHUTDOWN_EVENT: SystemExitRequested = true; return FALSE; default: return FALSE; } } void ProcessWindowsMessage(void) { MSG msgx; bool msgReturn; msgReturn = PeekMessage(&msgx, NULL, WM_QUIT, WM_QUIT, PM_NOREMOVE); if (msgReturn == true && msgx.message == WM_QUIT) { SystemExitRequested = true; } } bool SerialSend(const uint8_t *p, uint16_t len) { bool retVal = false; //Console_SetCursor(0, -1); //EmitBuffer("> ", p, len); //Console_ScrollBottomRegion(); if (avrPort.Write((const LPVOID)p, len) == len) { retVal = true; } else { Console_WriteAt(0, -1, "***** Failed to send. Port not open?"); } return retVal; } void EmitSpinner() { static int x = 0; const char arrow[] = "|/-\\|/-\\"; printf("%c\b", arrow[x++]); if (x == 8) x = 0; } void EmitRuntimeHelp() { for (int i = 0; i < sizeof(UserCommands) / sizeof(UserCmd_T); i++) { char tinyBuf[6]; if (isprint(UserCommands[i].Char)) { sprintf_s(tinyBuf, sizeof(tinyBuf), "%c", UserCommands[i].Char); } else if (UserCommands[i].Char == '\x1B') { sprintf_s(tinyBuf, sizeof(tinyBuf), "%s", ""); } else if (UserCommands[i].Char < ' ') { sprintf_s(tinyBuf, sizeof(tinyBuf), "^%c", UserCommands[i].Char + 64); } else { sprintf_s(tinyBuf, sizeof(tinyBuf), "0x%02X", (unsigned char)UserCommands[i].Char); } char buf[MAXTEXTLEN]; sprintf_s(buf, MAXTEXTLEN, " %5s %-25s", tinyBuf, UserCommands[i].pDescription); Console_AdvanceToNextLineIfNotRoomFor((short)strlen(buf), 1); Console_Write(buf); } if (avrPort.IsOpen()) { Console_ScrollBottomRegion(); Console_SetCursor(0, -1); printf(" Com Status: RTS: %-3s ", avrPort.Get_RTS_State() ? "ON" : "OFF"); printf("DTR: %-3s ", avrPort.Get_DTR_State() ? "ON" : "OFF"); printf("CTS: %-3s ", avrPort.Get_CTS_State() ? "ON" : "OFF"); printf("DSR: %-3s ", avrPort.Get_DSR_State() ? "ON" : "OFF"); printf("RI: %-3s", avrPort.Get_RI_State() ? "ON" : "OFF"); Console_ScrollBottomRegion(); Console_SetCursor(0, -1); } } void ProcessKeyboard(void) { static uint32_t spin = 0; bool cmdFound = false; Console_SetCursor(0, -1); if (spin++ % 16 == 0) EmitSpinner(); if (CS_KeyHit()) { int c = CS_GetChar(); switch (c) { case 'P': avr->AVRCommand(AVRInterface::subMain, AVRInterface::fncPower, AVRInterface::eOn); break; case 'p': avr->AVRCommand(AVRInterface::subMain, AVRInterface::fncPower, AVRInterface::eOff); break; case 'O': GetAndSendOSDMessage(); break; case ':': GetAndSendCustomMessage(); break; case '?': EmitRuntimeHelp(); break; case '/': avr->ReportAllStatus(); //ShowAllStatusInfo(); break; case '\x1B': UserExitRequested = true; break; case 'S': avrPort.Set_RTS_State(TRUE); Console_SetCursor(0, -1); printf("RTS set ON"); Console_ScrollBottomRegion(); break; case 's': avrPort.Set_RTS_State(FALSE); Console_SetCursor(0, -1); printf("RTS set OFF"); Console_ScrollBottomRegion(); break; default: for (int i = 0; i < sizeof(UserCommands) / sizeof(UserCmd_T); i++) { if (UserCommands[i].Char == c) { //ProcessSerialQueue((const uint8_t *)UserCommands[i].TxMsg.pMsg, UserCommands[i].TxMsg.MsgLen); avr->ProcessSerialQueue((const uint8_t *)UserCommands[i].TxMsg.pMsg, UserCommands[i].TxMsg.MsgLen); cmdFound = true; break; } } if (!cmdFound) { Console_SetCursor(0, -1); printf("Unrecognized command '%c' (0x%02X)", isprint(c) ? c : '.', (unsigned char)c); Console_ScrollBottomRegion(); } break; } } return; } void HexUppercase(char *p) { const char *hex = "0123456789ABCDEF"; while (*p) { if (*p >= 'a' && *p <= 'f') *p = *p - 'a' + 'A'; if (NULL == strchr(hex, *p)) { *p = '\0'; return; } p++; } } void GetAndSendOSDMessage() { char buf[MAXTEXTLEN]; Console_ScrollBottomRegion(); Console_ScrollBottomRegion(); Console_WriteAt(0, -2, "Enter OSD string to send: |________________|\rEnter OSD string to send: |"); gets_s(buf, sizeof(buf)); if (strlen(buf) == 0) { return; } else if (strlen(buf) > 16) { Console_WriteAt(0, -1, " Invalid. String must be 16 chars or less"); Console_ScrollBottomRegion(); } else { Console_ScrollBottomRegion(); if (avr->AVRSendOSDMessage(buf)) { // success } else { Console_WriteAt(0, -1, " Failed to send OSD message, Invalid chars perhaps."); Console_ScrollBottomRegion(); } } } void GetAndSendCustomMessage() { char buf[20]; char msg[30]; Console_ScrollBottomRegion(); Console_ScrollBottomRegion(); Console_WriteAt(0, -2, "Enter Hex string to send: "); gets_s(buf, sizeof(buf)); HexUppercase(buf); if (strlen(buf) != 5) { Console_WriteAt(0, -1, " Invalid. String must be exactly 5 HEX chars"); Console_ScrollBottomRegion(); } else { Console_ScrollBottomRegion(); sprintf_s(msg, sizeof(msg), "\x02%s\x03", buf); avr->ProcessSerialQueue(msg, (uint16_t)strlen(msg)); } } void GetProgName(char *name) { char *p = strrchr(name, '\\'); if (p) { p++; } else { p = name; } strcpy_s(progname, MAXTEXTLEN, p); p = strrchr(progname, '.'); if (p && 0 == stricmp(p, ".exe")) { *p = '\0'; } } void InformationUpdate(AVRInterface::AVRMessageType_T type, const char *msg) { char buf[MAXTEXTLEN] = ""; if (verboseMode) { switch (type) { case AVRInterface::AVRMessageType_T::mtStatus: sprintf_s(buf, MAXTEXTLEN, "AVR Status: %-60s", msg); Console_WriteAt(0, 1, buf); break; case AVRInterface::AVRMessageType_T::mtInfo: Console_WriteAt(0, -1, msg); Console_ScrollBottomRegion(); break; case AVRInterface::AVRMessageType_T::mtModelInfo: Console_WriteAt(0, 2, msg); break; case AVRInterface::AVRMessageType_T::mtStreamStart: Console_SetCursor(0, 3); // break; // fall through case AVRInterface::AVRMessageType_T::mtStream: Console_AdvanceToNextLineIfNotRoomFor(40); // @TODO hard-coded - ick Console_Write(msg); break; default: break; } } } void EmitCommandLineHelp() { printf("%s [Options] by Smartware Computing\n", progname); printf("\n"); printf(" This program can control a Yamaha AVR RX-2400 receiver via the RS-232 port.\n"); printf(" It may work for other similar models, and the API docs used to build this\n"); printf(" showed commands not in the RX-2400, but they were implemented herein.\n"); printf("\n"); printf(" Options:\n"); printf(" -SerialPort=X[,yyyy] Set to Com port X to baud rate yyyy.\n"); printf(" Defaults baud is %d\n", avrBaud); printf(" -Command=S,F,V Execute a command and exit (based on the numeric values):\n"); printf(" S - Subsystem\n"); printf(" F - Function\n"); printf(" V - Value\n"); printf(" Use the -ExportInfo command to see the values.\n"); printf(" NOTE: Program will auto-exit after performing the command,\n"); printf(" or after 5 seconds without obvious success.\n"); printf(" -Verbose Shows progress during -Command operation.\n"); printf(" NOTE: It is always verbose in interactive mode.\n"); printf(" -ExportInfo Export AVR Control Parameters and exit.\n"); printf("\n"); printf(" Example: avr -SerialPort=2 -Command=0,0,0 (Turn Main Power On)\n"); printf("\n"); printf(" Exit Codes:\n"); printf(" 0 = Normal exit\n"); printf(" 1 = Illegal Option or unrecognized command\n"); printf(" 2 = Program Internal Error - Table data failed santity check.\n"); printf(" 3 = External Quit (e.g. Windows command to close).\n"); printf(" 4 = Something very abnormal happened to cause exit.\n"); if (avrOnPort == COM_NO_PORT) { EnumerateComPorts(); } exit(EXIT_OK); } DWORD AppTime() { static bool init = false; static DWORD startTime = 0; if (!init) { init = true; startTime = timeGetTime(); } return timeGetTime() - startTime; } /******************************************************/ /* m a i n ( ) */ /******************************************************/ // 0 Width // +---------------------------------------------------------------+ // | Program banner information | 0 // | State machine status information starts on line 2 | // | Overall information is on line 3 and onward | // | | // | | // | | // + Down near the bottom is the scroll region + 59 // | | // | | // | | 79 // +---------------------------------------------------------------+ 80 int __cdecl main(int argc, char *argv[]) { short consoleWidth = 122; short consoleHeight = 80; short consoleScrollHeight = 30; short consoleLeft = 15; short consoleTop = 15; int CommandToExecute[3] = { -1, -1, -1 }; // MessageHandlerSanityCheck(); // If the table is bad, we exit here GetProgName(argv[0]); UserCommandsSanityCheck(); // If the table is bad, we exit here avr = new AVRInterface(SerialSend); avr->RegisterInformationCallback(InformationUpdate); for (int i = 1; i < argc; i++) { int param1, param2; if (1 == sscanf_s(argv[i], "-SerialPort=%d", ¶m1)) { avrOnPort = param1; } else if (2 == sscanf_s(argv[i], "-SerialPort=%d,%d", ¶m1, ¶m2)) { avrOnPort = param1; avrBaud = param2; } else if (3 == sscanf_s(argv[i], "-Command=%d,%d,%d", &CommandToExecute[0], &CommandToExecute[1], &CommandToExecute[2])) { // After the port is open, execute this and exit } else if (0 == strcmp(argv[i], "-Verbose")) { verboseMode = true; } else if (0 == strcmp(argv[i], "-ExportInfo")) { bool saveVerbose = verboseMode; verboseMode = true; avr->ExportInformation(); verboseMode = saveVerbose; exit(EXIT_OK); } else { printf("***** Unrecognized command '%s' *****\n", argv[i]); EmitCommandLineHelp(); exit(EXIT_IllegalOption); } } if (argc == 0 || avrOnPort == COM_NO_PORT) { EmitCommandLineHelp(); exit(EXIT_OK); } if (AttachToSerialPort()) { if (CommandToExecute[0] != -1 && CommandToExecute[1] != -1 && CommandToExecute[2] != -1) { DWORD refTime = AppTime(); DWORD nowTime = AppTime(); DWORD elapsedTime = nowTime - refTime; AVRInterface::AVRState_T state = AVRInterface::stPoweringUp; do { state = avr->Tick(nowTime); Sleep(10); // @TODO how long to wait should not be based on lucky timing SerialQueue_T anyRcvdMsg = ProcessSerialReceive(); if (anyRcvdMsg.len) { avr->HandleMessage(anyRcvdMsg.message, anyRcvdMsg.len); } nowTime = AppTime(); elapsedTime = nowTime - refTime; } while ((elapsedTime < 5000) && (state != AVRInterface::stReady)); if (elapsedTime < 5000) { avr->AVRCommand((AVRInterface::AVRSubsystem_T)CommandToExecute[0], (AVRInterface::AVRFunction_E)CommandToExecute[1], (AVRInterface::AVRArg_T)CommandToExecute[2]); refTime = nowTime = AppTime(); do { Sleep(10); // @TODO how long to wait should not be based on lucky timing nowTime = AppTime(); SerialQueue_T anyRcvdMsg = ProcessSerialReceive(); if (anyRcvdMsg.len) { avr->HandleMessage(anyRcvdMsg.message, anyRcvdMsg.len); } state = avr->Tick(nowTime); } while (((nowTime - refTime) < 5000) && (state != AVRInterface::stReady)); } else { printf("%u\n", elapsedTime); } DetachSerialPort(); exit(EXIT_OK); } verboseMode = true; // Always verbose when entering interactive mode Console_Init(consoleLeft, consoleTop, consoleWidth, consoleHeight, consoleScrollHeight); Console_Cls(); if (!SetConsoleCtrlHandler(ControlIntercept, TRUE)) { printf("WARN: Cannot install Console Control Handler\n"); } printf("AVR - a command shell utility for a Yamaha RX-2400 AVR by D.Smart "); printf("[COM%i at %i baud]", avrOnPort, avrBaud); Console_WriteAt(0, consoleHeight - consoleScrollHeight -1, "----------------------------------------"); do { avr->Tick(AppTime()); ProcessKeyboard(); SerialQueue_T anyRcvdMsg = ProcessSerialReceive(); if (anyRcvdMsg.len) { bool showAllFlag = avr->HandleMessage(anyRcvdMsg.message, anyRcvdMsg.len); if (showAllFlag) { bool priorState = Console_SetCursorVisibility(false); avr->ReportAllStatus(); Console_SetCursorVisibility(priorState); } } ProcessWindowsMessage(); } while (!UserExitRequested && !SystemExitRequested); DetachSerialPort(); } else { printf("Failed to attach to Serial Port on COM%i\n", avrOnPort); } if (UserExitRequested) { exit(EXIT_OK); } else if (SystemExitRequested) { exit(EXIT_ExternalQuit); } else { exit(EXIT_AbnormalError); } } void SetData(uint8_t *targ, const uint8_t *string, size_t len = 0) { uint8_t *p = (uint8_t *)string; if (len == 0) len = strlen((const char *)string); memcpy(targ, p, len); } // Dec2Hex // // Convert a number to a hex-ascii string // const uint8_t *Dec2Hex(uint32_t val, uint16_t numDigits) { static uint8_t buf[16]; sprintf_s((char *)buf, 16, "%0*X", numDigits, val); return buf; } // Hex2Dec // // All responses are pretty much Hex-ASCII, so // we sometimes want to convert it to decimal // This takes a buffer and converts the specified // number of characters. // unsigned long Hex2Dec(uint8_t *p, int dig) { unsigned long x = 0; while (dig--) { if (*p >= '0' && *p <= '9') x = x * 16 + *p - '0'; else if (*p >= 'a' && *p <= 'f') x = x * 16 + 0x0a + *p - 'a'; else if (*p >= 'A' && *p <= 'F') x = x * 16 + 0x0a + *p - 'A'; p++; } return x; } bool CheckTheChecksum(uint8_t *szBuffer, uint32_t num) { uint8_t sum = 0; for (uint16_t i = 1; i < num - 3; i++) { sum += szBuffer[i]; } uint8_t cksum = (uint8_t)Hex2Dec(&szBuffer[num - 3], 2); //printf("CheckSum: %02X v. %c%c\n", sum, szBuffer[num - 3], szBuffer[num - 2]); return (sum == cksum); } // HandleMessage // // Given a response string, typically of the form: // [11] .... [03] // [12] .... [03] // ... etc // // void HandleMessage(uint8_t *szBuffer, uint32_t num) { switch (szBuffer[0]) { case 0x02: // STX //ProcessReportResponse(szBuffer, num); break; case 0x11: // DC1 break; case 0x12: // DC2 if (CheckTheChecksum(szBuffer, num)) { if (num == 21) { memcpy(&avrStatus.header, &szBuffer[1], sizeof(AVR_StatusHeader_T)); num = Hex2Dec(&avrStatus.header.length[0], 2); memcpy(&avrStatus.config.DT0, &szBuffer[9], num); // Copy bits of the config avrStatus.headerValid = true; } else if (num == 150) { memcpy(&avrStatus.header, &szBuffer[1], sizeof(AVR_StatusHeader_T)); num = Hex2Dec(&avrStatus.header.length[0], 2); memcpy(&avrStatus.config.DT0, &szBuffer[9], num); // Copy the config //memcpy(&avrStatus.header, &szBuffer[1], sizeof(AVR_StatusHeader_T) + sizeof(AVR_Configuration_T)); avrStatus.headerValid = true; avrStatus.configValid = true; } else { printf("***** Received message of unexpected length [%u]\n", num); } } else { Console_WriteAt(0, -1, "Checksum failure on Status Header"); } //PrintConfiguration(szBuffer); break; case 0x14: // DC4 Extended Response //DecodeExtended(szBuffer); // Decode Extended response break; case '0': //rcmd = Hex2Dec(&szBuffer[0], 2); //DecodeString(rcmd, &szBuffer[2]); break; default: //printf("[%d] %s\n", i, szBuffer); break; } } // ProcessSerialReceive // // All character sequences end with \x03 (this is the equiv of a ) // // SerialQueue_T ProcessSerialReceive() { uint8_t partialRx[MAXTEXTLEN] = { 0 }; static uint8_t bufInUse = 0; static uint8_t messageBuf[2][MAXTEXTLEN] = { 0 }; static uint8_t *p = messageBuf[bufInUse]; // used to fill the partialRx as data comes in SerialQueue_T retInfo = { NULL, 0 }; uint32_t num = avrPort.Read(partialRx, MAXTEXTLEN); if (num) { for (uint32_t i = 0; i < num; i++) { *p = partialRx[i]; if (*p++ == '\x03') { // End of message *p = '\0'; // null terminate it // It is ready to return now retInfo.message = messageBuf[bufInUse]; retInfo.len = (uint16_t)(p - messageBuf[bufInUse]); // Prepare the alternate buffer for the next message bufInUse = (++bufInUse & 1); p = messageBuf[bufInUse]; // Reset the buffer for the next message, which might be in partialRx *p = '\0'; } } if (messageBuf[0]) { //EmitBuffer("~", messageBuf, 0, true); // Show them the partial receipt if anything is there } } return retInfo; } void EnumerateComPorts() { printf("Com Port Scan:\n"); int foundPorts = 0; for (int pNum = COM_MIN_PORT; pNum < COM_MAX_PORT; pNum++) { char cBuf[20]; // generously sized sprintf_s(cBuf, sizeof(cBuf), "\\\\.\\COM%d", pNum); bool portFound = false; HANDLE port = CreateFile(cBuf, GENERIC_READ | GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, 0, nullptr); if (port == INVALID_HANDLE_VALUE) { uint32_t dwError = GetLastError(); if ((dwError == ERROR_ACCESS_DENIED) || (dwError == ERROR_GEN_FAILURE) || (dwError == ERROR_SHARING_VIOLATION) || (dwError == ERROR_SEM_TIMEOUT)) { foundPorts++; portFound = true; } } else { foundPorts++; portFound = true; CloseHandle(port); } if (portFound) { printf(" Com Port %d found.\n", pNum); } } if (foundPorts == 0) { printf(" No Com Ports found, perhaps you need to plug in an adapter?\n"); } } int DetachSerialPort() { if (avrPort.IsOpen()) { avrPort.Close(); } return true; } int AttachToSerialPort() { bool success = false; if (avrOnPort >= COM_MIN_PORT && avrOnPort <= COM_MAX_PORT) { char buf[20]; // generously sized. sprintf_s(buf, sizeof(buf), "\\\\.\\COM%d", avrOnPort); uint32_t Access = GENERIC_WRITE | GENERIC_READ; if (avrPort.Open(buf, avrBaud, 8, NOPARITY, ONESTOPBIT, Access)) { success = true; avrPort.Set_RTS_State(false); Sleep(50); avrPort.Set_RTS_State(true); Sleep(50); } } return success; }