From 49edca02386646eb6652a70d363389f60ea3155e Mon Sep 17 00:00:00 2001 From: David Date: Sat, 31 Jan 2026 14:39:37 -0600 Subject: [PATCH] Add support for .ini file, Add support for extended messages (advanced protocol), Add handy command lines for Power, Volume in db, and Mute. --- AVR Working Controller/AVR.cpp | 256 +++++++++++++++--- AVR Working Controller/AVR.vcxproj | 6 +- AVR Working Controller/AVRCommandDecoder.cpp | 10 +- AVR Working Controller/AVRInterface.cpp | 66 ++++- AVR Working Controller/AVRInterface.h | 22 +- .../SerialPort/SerialPort.cpp | 183 ++++--------- .../SerialPort/SerialPort.h | 20 +- .../Yamaha RX-V2400_RS232C_Standard.pdf | Bin 183025 -> 185917 bytes 8 files changed, 368 insertions(+), 195 deletions(-) diff --git a/AVR Working Controller/AVR.cpp b/AVR Working Controller/AVR.cpp index 72b9bb9..fce84fd 100644 --- a/AVR Working Controller/AVR.cpp +++ b/AVR Working Controller/AVR.cpp @@ -210,6 +210,8 @@ typedef enum { constexpr auto MAXTEXTLEN = 512; char progname[MAXTEXTLEN]; +char ininame[MAXTEXTLEN] = ""; + int AttachToSerialPort(); int DetachSerialPort(); @@ -261,7 +263,9 @@ unsigned long Hex2Dec(uint8_t *p, int dig); //void ShowAllStatusInfo(); void GetAndSendCustomMessage(); +void GetAndSendExtendedMessage(); void GetAndSendOSDMessage(); +bool SendExtendedMessage(char *buf); void EmitRuntimeHelp(); @@ -383,7 +387,8 @@ const UserCmd_T UserCommands[] = { //{ '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" }, + { ':', "Send Command Message" }, + { '=', "Send Extended Message" }, { '/', "Show All" }, { '?', "Help on runtime commands" }, { 'S', "Set RTS" }, @@ -413,6 +418,9 @@ void ProcessKeyboard(void) { case ':': GetAndSendCustomMessage(); break; + case '=': + GetAndSendExtendedMessage(); + break; case '?': EmitRuntimeHelp(); break; @@ -557,6 +565,35 @@ void GetAndSendCustomMessage() { } } +bool SendExtendedMessage(char * buf) { + HexUppercase(buf); // Gets nulled if there are non-Hex + if (strlen(buf)) { + char msg[75]; // Hope it is large enough + const char * prefix = "\x14" "20"; + unsigned len = (unsigned)strlen(buf); + sprintf_s(msg, 70, "%s%02X%s", prefix, len, buf); + char *p = &msg[1]; + uint8_t checksum = 0; + do { + checksum += *p++; + } while (*p && (p < msg + 70)); // 70 < 75 to generously reserve space for SUM0,SUM1,ETX,\0 + sprintf_s(p, 75 - strlen(msg), "%02X\x03", checksum); + Console_ScrollBottomRegion(); + avr->ProcessSerialQueue(msg, (uint16_t)strlen(msg)); + return true; + } + return false; +} + +void GetAndSendExtendedMessage() { + char buf[60]; + Console_ScrollBottomRegion(); + Console_ScrollBottomRegion(); + Console_WriteAt(0, -2, "Enter DT0...DTX Hex string to send: "); + gets_s(buf, sizeof(buf) - 1); + SendExtendedMessage(buf); +} + void GetProgName(char *name) { char *p = strrchr(name, '\\'); @@ -570,6 +607,8 @@ void GetProgName(char *name) { if (p && 0 == stricmp(p, ".exe")) { *p = '\0'; } + strcpy_s(ininame, MAXTEXTLEN, progname); + strcat_s(ininame, MAXTEXTLEN, ".ini"); } @@ -602,35 +641,88 @@ void InformationUpdate(AVRInterface::AVRMessageType_T type, const char *msg) { } void EmitCommandLineHelp() { - printf("%s [Options] by Smartware Computing\n", progname); + const char * by = "by Smartware Computing"; + printf("%s [Options] %80s\n", progname, by); 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(" This program can control a Yamaha AVR RX-V2400 receiver via the RS-232 port.\n"); + printf(" It may work for other similar models.\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,O[,V] Execute a command and exit (based on the numeric values):\n"); - printf(" S - Subsystem,\n"); - printf(" F - Function,\n"); - printf(" O - Operation,\n"); - printf(" V - Value used by only certain command.\n"); - printf(" Use the -ExportInfo command to see the Command List.\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(" This software, and/or material is the property of Smartware Computing. All use,\n"); + printf(" disclosure, and/or reproduction not specifically authorized by Smartware\n"); + printf(" Computing is prohibited. This program has no warranty or declaration of fitness\n"); + printf(" for your use, and no claim that it will function as intended. While the author is\n"); + printf(" using this regularly with an RX-V2400 receiver, it was developed using the full\n"); + printf(" capability expressed in the Yamaha RS232C Standard document, even though not all\n"); + printf(" features have been tested and NOT ALL AVRs support all commands.\n"); printf("\n"); - printf(" Example: avr -SerialPort=2 -Command=0,0,0 (Turn Main Power On)\n"); + printf(" There is no claim that it cannot damage your AVR, and you are accepting the risk!\n"); + printf("\n"); + printf(" Reference Documents:\n"); + printf(" + Yamaha RX-V2400 RS232C Standard - with decoding of most parameters.\n"); + printf(" + Yamaha RX-V2400 RS232C Extended - raw hex streams are supported.\n"); + printf("\n"); + printf(" This program is copyright (c) 2025 by Smartware Computing, all rights reserved.\n"); + printf("\n"); + printf(" Author David Smart, Smartware Computing\n"); + + printf("\n"); + printf("Options:\n"); + printf("\n"); + printf(" -SerialPort=X[,yyyy] Set to Com port X to baud rate yyyy.\n"); + printf(" Defaults baud is %d\n", avrBaud); + printf(" NOTE: At least for the RX-V2400, 9600,8,N,1 is required.\n"); + printf(" NOTE: At least for the RX-V2400, the host must assert RTS.\n"); + printf("\n"); + printf(" -Command=S,F,O[,V] Execute command and exit (based on the numeric values):\n"); + printf(" S - Subsystem,\n"); + printf(" F - Function,\n"); + printf(" O - Operation,\n"); + printf(" V - Value used by only certain commands. This value is\n"); + printf(" entered as a decimal value (like the S,F, and O).\n"); + printf(" Use the -ExportInfo command to see the Command List.\n"); + printf(" NOTE: Program will auto-exit after performing the command,\n"); + printf(" or after about 5 seconds without obvious success.\n"); + printf("\n"); + printf(" -Extended=xxxxxxxx... Send Extended Command defined by ASCII hex sequence of DT0...DTx.\n"); + printf(" The length and checksum will be computed in the overall message.\n"); + printf(" [DC4][SW][KIND][L0][L1][DT0][DT1]...[DTx][SUM0][SUM1][ETX]\n"); + printf("\n"); + printf(" -Verbose Shows progress during -Command operation.\n"); + printf(" NOTE: %s is always verbose in interactive mode.\n", progname); + printf("\n"); + printf(" -ExportInfo[=S[,F[,O]]] Export AVR Control Parameters and exit, optionally \n"); + printf(" filtered to a subsystem, function, and operation.\n"); + printf(" NOTE: The unfiltered list is quite long.\n"); + printf("\n"); + printf("Handy Commands: Here's a few commands, made 'simpler' to access.\n"); + printf("\n"); + printf(" -Power=On|Off Control Main Power - On or Off\n"); + printf(" -Zone1Volume=+xx.y Set the zone 1 volume db level in the range -80.0 to +16.5.\n"); + printf(" -Zone2Volume=+xx.y Set the zone 2 volume db level in the range -80.0 to +16.5.\n"); + printf(" -Zone3Volume=+xx.y Set the zone 2 volume db level in the range -80.0 to +16.5.\n"); + printf(" CAUTION: Take care that you don't blow your speakers/ears!\n"); + printf(" -Mute=On|Off Mute on or Mute off\n"); + printf("\n"); + printf("ini File:\n"); + printf("\n"); + printf(" %s.ini, if it exists in the same folder as the program, will be read before any command\n", progname); + printf(" line parameters are processed. Supported items: \n"); + printf(" + Either form of the SerialPort command.\n"); + printf(" + Anything else is silently ignored.\n"); + printf("\n"); + printf("Examples:\n"); + printf("\n"); + printf(" > avr -ExportInfo=1,0 (See the options for Main Power)\n"); + printf(" > avr -SerialPort=2 -Command=1,0,0 (Turn Main Power On)\n"); + printf("\n"); + printf("Exit Codes:\n"); + printf("\n"); + printf(" 0 = Normal exit\n"); + printf(" 1 = Illegal Option or unrecognized command\n"); + printf(" 2 = Program Internal Error - Data table 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"); 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(); } @@ -647,6 +739,24 @@ DWORD AppTime() { return timeGetTime() - startTime; } +void ReadIniFile() { + char buf[MAXTEXTLEN] = ""; + FILE *fp = fopen(ininame, "r"); + int param1, param2; + + if (fp) { + while (!feof(fp)) { + fgets(buf, sizeof(buf) - 1, fp); + if (1 == sscanf_s(buf, "-SerialPort=%d", ¶m1)) { + avrOnPort = param1; + } else if (2 == sscanf_s(buf, "-SerialPort=%d,%d", ¶m1, ¶m2)) { + avrOnPort = param1; + avrBaud = param2; + } + } + } +} + /******************************************************/ /* m a i n ( ) */ /******************************************************/ @@ -672,6 +782,9 @@ int __cdecl main(int argc, char *argv[]) { short consoleLeft = 15; short consoleTop = 15; int CommandToExecute[4] = { -1, -1, -1, -1 }; + float db; + int parseCount = 0; + char *pExtendedMessage = NULL; // MessageHandlerSanityCheck(); // If the table is bad, we exit here GetProgName(argv[0]); @@ -680,6 +793,8 @@ int __cdecl main(int argc, char *argv[]) { avr = new AVRInterface(SerialSend); avr->RegisterInformationCallback(InformationUpdate); + ReadIniFile(); + for (int i = 1; i < argc; i++) { int param1, param2; if (1 == sscanf_s(argv[i], "-SerialPort=%d", ¶m1)) { @@ -691,6 +806,8 @@ int __cdecl main(int argc, char *argv[]) { // After the port is open, execute this and exit } else if (4 == sscanf_s(argv[i], "-Command=%d,%d,%d,%d", &CommandToExecute[0], &CommandToExecute[1], &CommandToExecute[2], &CommandToExecute[3])) { // After the port is open, execute this and exit + } else if (0 == strncmp(argv[i], "-Extended=", 10)) { + pExtendedMessage = argv[i] + 10; } else if (0 == strcmp(argv[i], "-Verbose")) { verboseMode = true; } else if (0 == strcmp(argv[i], "-ExportInfo")) { @@ -699,6 +816,74 @@ int __cdecl main(int argc, char *argv[]) { avr->ExportInformation(); verboseMode = saveVerbose; exit(EXIT_OK); + } else if (1 <= (parseCount = sscanf_s(argv[i], "-ExportInfo=%d,%d,%d", &CommandToExecute[0], &CommandToExecute[1], &CommandToExecute[2]))) { + // + // // If they didn't provide all three, set the others to default (wildcard) + // + switch (parseCount) { + case 1: + CommandToExecute[1] = AVRInterface::AVRFunction_E::fncFunctionCount; + // break; // fall thru on purpose + case 2: + CommandToExecute[2] = AVRInterface::AVRArg_T::eARGCount; + // break; // fall thru on purpose + default: + // nothing to do + break; + } + bool saveVerbose = verboseMode; + verboseMode = true; + avr->ExportInformation((AVRInterface::AVRSubsystem_T)CommandToExecute[0], + (AVRInterface::AVRFunction_E)CommandToExecute[1], + (AVRInterface::AVRArg_T)CommandToExecute[2]); + verboseMode = saveVerbose; + exit(EXIT_OK); + } else if (0 == strcmp(argv[i], "-Power=On")) { + CommandToExecute[0] = AVRInterface::subMain; + CommandToExecute[1] = AVRInterface::fncPower; + CommandToExecute[2] = AVRInterface::eOn; + } else if (0 == strcmp(argv[i], "-Power=Off")) { + CommandToExecute[0] = AVRInterface::subMain; + CommandToExecute[1] = AVRInterface::fncPower; + CommandToExecute[2] = AVRInterface::eOff; + } else if (0 == strcmp(argv[i], "-Mute=On")) { + CommandToExecute[0] = AVRInterface::subMain; + CommandToExecute[1] = AVRInterface::fncMute; + CommandToExecute[2] = AVRInterface::eOn; + } else if (0 == strcmp(argv[i], "-Mute=Off")) { + CommandToExecute[0] = AVRInterface::subMain; + CommandToExecute[1] = AVRInterface::fncMute; + CommandToExecute[2] = AVRInterface::eOff; + } else if (1 == sscanf_s(argv[i], "-Zone1Volume=%4f", &db)) { + if (db >= -80.0f && db <= +16.5f) { + CommandToExecute[0] = AVRInterface::sysCommand; + CommandToExecute[1] = AVRInterface::fncSetValue; + CommandToExecute[2] = AVRInterface::eMasterVol; + CommandToExecute[3] = avr->VolumeDBtoAPIValue(db); + } else { + printf("***** Volume %+4.1f not in the range '-80.0 <= value <= 16.5' *****\n", db); + exit(EXIT_IllegalOption); + } + } else if (1 == sscanf_s(argv[i], "-Zone2Volume=%4f", &db)) { + if (db >= -80.0f && db <= +16.5f) { + CommandToExecute[0] = AVRInterface::sysCommand; + CommandToExecute[1] = AVRInterface::fncSetValue; + CommandToExecute[2] = AVRInterface::eZone2Vol; + CommandToExecute[3] = avr->VolumeDBtoAPIValue(db); + } else { + printf("***** Volume %+4.1f not in the range '-80.0 <= value <= 16.5' *****\n", db); + exit(EXIT_IllegalOption); + } + } else if (1 == sscanf_s(argv[i], "-Zone3Volume=%4f", &db)) { + if (db >= -80.0f && db <= +16.5f) { + CommandToExecute[0] = AVRInterface::sysCommand; + CommandToExecute[1] = AVRInterface::fncSetValue; + CommandToExecute[2] = AVRInterface::eZone3Vol; + CommandToExecute[3] = avr->VolumeDBtoAPIValue(db); + } else { + printf("***** Volume %+4.1f not in the range '-80.0 <= value <= 16.5' *****\n", db); + exit(EXIT_IllegalOption); + } } else { printf("***** Unrecognized command '%s' *****\n", argv[i]); EmitCommandLineHelp(); @@ -711,7 +896,7 @@ int __cdecl main(int argc, char *argv[]) { } if (AttachToSerialPort()) { - if (CommandToExecute[0] != -1 && CommandToExecute[1] != -1 && CommandToExecute[2] != -1) { + if (pExtendedMessage || (CommandToExecute[0] != -1 && CommandToExecute[1] != -1 && CommandToExecute[2] != -1)) { DWORD refTime = AppTime(); DWORD nowTime = AppTime(); DWORD elapsedTime = nowTime - refTime; @@ -726,11 +911,16 @@ int __cdecl main(int argc, char *argv[]) { 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]); + if (pExtendedMessage) { + SendExtendedMessage(pExtendedMessage); + } else { + avr->AVRCommand((AVRInterface::AVRSubsystem_T)CommandToExecute[0], + (AVRInterface::AVRFunction_E)CommandToExecute[1], + (AVRInterface::AVRArg_T)CommandToExecute[2], + (uint8_t)CommandToExecute[3]); + } refTime = nowTime = AppTime(); do { Sleep(10); // @TODO how long to wait should not be based on lucky timing @@ -741,8 +931,6 @@ int __cdecl main(int argc, char *argv[]) { } state = avr->Tick(nowTime); } while (((nowTime - refTime) < 5000) && (state != AVRInterface::stReady)); - } else { - // printf("%u\n", elapsedTime); } DetachSerialPort(); exit(EXIT_OK); @@ -755,7 +943,7 @@ int __cdecl main(int argc, char *argv[]) { printf("WARN: Cannot install Console Control Handler\n"); } - printf("AVR - a command shell utility for a Yamaha RX-2400 AVR by D.Smart "); + printf("AVR - a command shell utility for a Yamaha RX-V2400 AVR by D.Smart "); printf("[COM%i at %i baud]", avrOnPort, avrBaud); Console_WriteAt(0, consoleHeight - consoleScrollHeight -1, "----------------------------------------"); diff --git a/AVR Working Controller/AVR.vcxproj b/AVR Working Controller/AVR.vcxproj index bf2740a..8a72ada 100644 --- a/AVR Working Controller/AVR.vcxproj +++ b/AVR Working Controller/AVR.vcxproj @@ -50,7 +50,7 @@ false v143 true - Unicode + MultiByte @@ -112,12 +112,14 @@ - Level3 + Level4 true true true NDEBUG;_CONSOLE;%(PreprocessorDefinitions) true + MultiThreaded + Size Console diff --git a/AVR Working Controller/AVRCommandDecoder.cpp b/AVR Working Controller/AVRCommandDecoder.cpp index bdb332a..834c9e2 100644 --- a/AVR Working Controller/AVRCommandDecoder.cpp +++ b/AVR Working Controller/AVRCommandDecoder.cpp @@ -523,7 +523,7 @@ const char *VolumeDB(uint8_t val) { b = -99.5f; //} float db = m * val + b; - sprintf_s(buf, sizeof(buf), "%+3.1f db", db); + sprintf_s(buf, sizeof(buf), "%+4.1f db", db); return buf; } @@ -537,7 +537,7 @@ const char *PM10dbText(uint8_t val) { m = 0.5f; b = -10.0f; float db = m * (val - 0x14) + b; - sprintf_s(buf, sizeof(buf), "%+3.1f db", db); + sprintf_s(buf, sizeof(buf), "%+4.1f db", db); } return buf; } @@ -552,7 +552,7 @@ const char *M20P0dbText(uint8_t val) { m = 0.5f; b = -20.0f; float db = m * (val)+b; - sprintf_s(buf, sizeof(buf), "%+3.1f db", db); + sprintf_s(buf, sizeof(buf), "%+4.1f db", db); } return buf; } @@ -567,7 +567,7 @@ const char *M10P10dbText(uint8_t val) { m = 0.5f; b = -10.0f; float db = m * (val - 0x14) + b; - sprintf_s(buf, sizeof(buf), "%+3.1f db", db); + sprintf_s(buf, sizeof(buf), "%+4.1f db", db); } return buf; } @@ -644,7 +644,7 @@ const char *ZeroTo30msText(uint8_t val) { m = 0.5f; b = 0; float delay = m * (val + b); - sprintf_s(buf, sizeof(buf), "%+3.1f ms", delay); + sprintf_s(buf, sizeof(buf), "%+4.1f ms", delay); } return buf; } diff --git a/AVR Working Controller/AVRInterface.cpp b/AVR Working Controller/AVRInterface.cpp index 57f32f2..b5c975a 100644 --- a/AVR Working Controller/AVRInterface.cpp +++ b/AVR Working Controller/AVRInterface.cpp @@ -1,9 +1,7 @@ // +// The AVR Interface object and interaction methods // -// -#ifdef _DEBUG #include -#endif #include #include #include @@ -480,6 +478,7 @@ bool AVRInterface::ProcessSerialQueue(const void *msg, uint16_t len) { } retVal = true; } + state = stAwaitingResponse; } return retVal; } @@ -950,7 +949,9 @@ bool AVRInterface::AVRCommand(AVRInterface::AVRSubsystem_T subsystem, return true; } -void AVRInterface::ExportInformation() { +void AVRInterface::ExportInformation(AVRInterface::AVRSubsystem_T subsystem, + AVRInterface::AVRFunction_E function, + AVRArg_T arg) { char buf[LONGESTTEXT] = ""; typedef struct { uint16_t value; @@ -1127,6 +1128,12 @@ void AVRInterface::ExportInformation() { { eZoneXVolDB, "Zone X Volume" }, { eZoneXInputName, "Zone X Input Name" }, + { eMasterVol, "Master Volume" }, + { eZone2Vol, "Zone 2 Volume" }, + { eMainLRBal, "Left-Right Balance" }, + { eMainLevel, "Main Level" }, + { eZone3Vol, "Zone 3 Volume" }, + { eMainLevelR, "Main Level R" }, { eMainLevelL, "Main Level L" }, { eCenterLevel, "Center Level" }, @@ -1160,10 +1167,23 @@ void AVRInterface::ExportInformation() { ); ReportInformation(mtInfo, buf); - AVRSubsystem_T refSub = subSubsystemCount; + AVRSubsystem_T refSub = subsystemCount; AVRFunction_E refFnc = fncFunctionCount; const char *pSubsystemText = NULL, *pFunctionText = NULL, *pValueText = NULL; for (int i = 0; i < sizeof(MessageTable) / sizeof(MessageTable_T); i++) { + + // Subsystem scan or filter + // + if (subsystem != subsystemCount && subsystem != MessageTable[i].subsystem) { + continue; + } + if (function != fncFunctionCount && function != MessageTable[i].function) { + continue; + } + if (arg != eARGCount && arg != MessageTable[i].arg) { + continue; + } + if (refSub != MessageTable[i].subsystem) { refSub = MessageTable[i].subsystem; for (int j = 0; j < sizeof(subsysList) / sizeof(ValuePurpose_T); j++) { @@ -1177,6 +1197,9 @@ void AVRInterface::ExportInformation() { } else { pSubsystemText = ""; } + // + // function choices + // if (refFnc != MessageTable[i].function) { refFnc = MessageTable[i].function; for (int j = 0; j < sizeof(funcList) / sizeof(ValuePurpose_T); j++) { @@ -1190,7 +1213,9 @@ void AVRInterface::ExportInformation() { } else { pFunctionText = ""; } - // @TODO Probably all the enum values must be merged into one big enum list for this to work. + // + // argument choices + // for (int j = 0; j < sizeof(valueList) / sizeof(ValuePurpose_T); j++) { if (valueList[j].value == MessageTable[i].arg) { pValueText = valueList[j].helpText; @@ -1224,6 +1249,23 @@ const char *AVRInterface::MessageToText(const char *p, size_t len) { return privateBuffer; } + +// +// Single Linear according to the on-screen display of Volume +// db = 0.5 x - 99.5 +// therefore: +// db + 99.5 = 0.5 x +// 0.5 x == db + 99.5 +// x = 2 * (db + 99.5) +// +uint8_t AVRInterface::VolumeDBtoAPIValue(float db) { + if (db >= -80.0f && db <= +16.5f) { + return (uint8_t)(2 * (db + 99.5)); + } else { + return (0); // mute + } +} + /// ReportAllStatus /// /// This emits (via a callback) all the status, in DT0 to DT137 order. @@ -1246,14 +1288,14 @@ void AVRInterface::ReportAllStatus() { PCMessage("Busy", 1, &p, BusyToText); PCMessage("Power", 1, &p, OffOnText); if (avrStatus.configValid) { - PCMessage("Input", 1, &p, InputText); + PCMessage("Zone 1 Input", 1, &p, InputText); PCMessage("6 ch", 1, &p, OffOnText); PCMessage("Inp mode", 1, &p, InputModeText); PCMessage("Mute", 1, &p, OffOnText); - PCMessage("Zone 2", 1, &p, InputText); - PCMessage("Mute 2", 1, &p, OffOnText); + PCMessage("Zone 2 Input", 1, &p, InputText); + PCMessage("Zone 2 Mute", 1, &p, OffOnText); PCMessage("Volume", 2, &p, VolumeDB); - PCMessage("Volume 2", 2, &p, VolumeDB); + PCMessage("Zone 2 Volume", 2, &p, VolumeDB); PCMessage("Prog", 2, &p, ProgramName); PCMessage("Effect", 1, &p, OffOnText); PCMessage("6.1/es status", 1, &p, OffMatrixDiscreteAutoText); @@ -1328,9 +1370,9 @@ void AVRInterface::ReportAllStatus() { PCMessage("Lvl 6 Ch F L", 2, &p, M10P10dbText); PCMessage("Lvl 6 Ch F R", 2, &p, M10P10dbText); PCMessage("Lvl 6 Ch swfr", 2, &p, M20P0dbText ); - PCMessage("Zone 3 Inp", 1, &p, PlaybackToText); + PCMessage("Zone 3 Input", 1, &p, PlaybackToText); PCMessage("Zone 3 Mute", 1, &p, OffOnText); - PCMessage("Zone 3 Vol", 2, &p, VolumeDB); + PCMessage("Zone 3 Volume", 2, &p, VolumeDB); PCMessage("?????", 1, &p); PCMessage("MultiCh Select", 1, &p, SixEightText); PCMessage("MultiCh Surround", 1, &p, SurrMainText); diff --git a/AVR Working Controller/AVRInterface.h b/AVR Working Controller/AVRInterface.h index 13febac..c238f67 100644 --- a/AVR Working Controller/AVRInterface.h +++ b/AVR Working Controller/AVRInterface.h @@ -54,7 +54,7 @@ public: subZone1, subZone2, subZone3, - subSubsystemCount + subsystemCount } AVRSubsystem_T; typedef enum { @@ -249,7 +249,8 @@ public: /// @param[in] function : Power, Speaker, Volume, etc. /// @param[in] arg: on/off, etc. /// @param[in] variableData is for those functions where it needs to integrate variable value, such as setting the volume directly - /// @return + /// @return true if accepted + /// bool AVRCommand(AVRSubsystem_T subsystem, AVRFunction_E function, AVRArg_T arg, @@ -316,7 +317,22 @@ public: /// /// @brief Export all the numeric data to support a command line control option /// - void ExportInformation(); + /// @param[in] subsystem: Main | Zone 1 | Zone 2 | Zone 3 + /// @param[in] function : Power, Speaker, Volume, etc. + /// @param[in] arg: on/off, etc. + /// + void ExportInformation(AVRInterface::AVRSubsystem_T subsystem = subsystemCount, + AVRInterface::AVRFunction_E function = fncFunctionCount, + AVRArg_T arg = eARGCount); + + /// VolumeDBtoAPIValue + /// + /// @brief Convert a floating point value to the internal API db units for volume + /// @param db in the range of -80.0 to +16.5 db + /// @return scaled to internal volume units + /// + uint8_t VolumeDBtoAPIValue(float db); + private: uint32_t sentAtTime_ms; diff --git a/AVR Working Controller/SerialPort/SerialPort.cpp b/AVR Working Controller/SerialPort/SerialPort.cpp index c790ab4..c968efd 100644 --- a/AVR Working Controller/SerialPort/SerialPort.cpp +++ b/AVR Working Controller/SerialPort/SerialPort.cpp @@ -17,7 +17,7 @@ #ifdef _DEBUG #undef THIS_FILE -static char THIS_FILE[]=__FILE__; +static char THIS_FILE[] = __FILE__; #define new DEBUG_NEW #endif @@ -25,23 +25,18 @@ static char THIS_FILE[]=__FILE__; // Construction/Destruction ////////////////////////////////////////////////////////////////////// -CSerialPort::CSerialPort() -: m_PortHandle(INVALID_HANDLE_VALUE) -{ +CSerialPort::CSerialPort() : m_PortHandle(INVALID_HANDLE_VALUE) { } -CSerialPort::~CSerialPort() -{ +CSerialPort::~CSerialPort() { Close(); } -BOOL CSerialPort::Open(LPCTSTR PortName, uint32_t BaudRate, BYTE ByteSize, BYTE Parity, BYTE StopBits, uint32_t DesiredAccess) -{ +bool CSerialPort::Open(LPCTSTR PortName, uint32_t BaudRate, BYTE ByteSize, BYTE Parity, BYTE StopBits, uint32_t DesiredAccess) { Close(); m_PortHandle = CreateFile(PortName, DesiredAccess, 0, NULL, OPEN_EXISTING, 0, 0); - if (m_PortHandle != INVALID_HANDLE_VALUE) - { + if (m_PortHandle != INVALID_HANDLE_VALUE) { DCB dcb; CString s; dcb.DCBlength = sizeof(dcb); @@ -67,7 +62,7 @@ BOOL CSerialPort::Open(LPCTSTR PortName, uint32_t BaudRate, BYTE ByteSize, BYTE dcb.fInX = 0; dcb.fOutX = 0; dcb.fDtrControl = DTR_CONTROL_DISABLE; //DTR and RTS 0 - dcb.fRtsControl = RTS_CONTROL_DISABLE; + dcb.fRtsControl = RTS_CONTROL_DISABLE; SetCommState(m_PortHandle, &dcb); @@ -78,186 +73,116 @@ BOOL CSerialPort::Open(LPCTSTR PortName, uint32_t BaudRate, BYTE ByteSize, BYTE touts.WriteTotalTimeoutConstant = 1; touts.WriteTotalTimeoutMultiplier = 0; SetCommTimeouts(m_PortHandle, &touts); - + //SetCommMask (m_PortHandle, EV_CTS | EV_DSR | EV_RING | EV_RLSD); PurgeComm(m_PortHandle, PURGE_TXCLEAR | PURGE_RXCLEAR); - return TRUE; - } - else - { - printf("Open COM Error: %i\n", GetLastError()); - return FALSE; // Use GetLastError() to know the reason + return true; + } else { + return false; // Use GetLastError() to know the reason } } -void CSerialPort::Close() -{ - if (m_PortHandle != INVALID_HANDLE_VALUE) - { +void CSerialPort::Close() { + if (m_PortHandle != INVALID_HANDLE_VALUE) { CloseHandle(m_PortHandle); m_PortHandle = INVALID_HANDLE_VALUE; } } -BOOL CSerialPort::IsOpen() -{ +bool CSerialPort::IsOpen() { return (m_PortHandle != INVALID_HANDLE_VALUE); } -uint32_t CSerialPort::Read(LPVOID Buffer, uint32_t BufferSize) -{ +uint32_t CSerialPort::Read(LPVOID Buffer, uint32_t BufferSize) { DWORD Res(0); - if (m_PortHandle != INVALID_HANDLE_VALUE) - { + if (m_PortHandle != INVALID_HANDLE_VALUE) { ReadFile(m_PortHandle, Buffer, BufferSize, &Res, NULL); } - return Res; + return Res; } -uint32_t CSerialPort::Write(const LPVOID Buffer, uint32_t BufferSize) -{ +uint32_t CSerialPort::Write(const LPVOID Buffer, uint32_t BufferSize) { DWORD Res(0); - if (m_PortHandle != INVALID_HANDLE_VALUE) - { + if (m_PortHandle != INVALID_HANDLE_VALUE) { (void)WriteFile(m_PortHandle, Buffer, BufferSize, &Res, NULL); } return Res; } -BOOL CSerialPort::Get_CD_State() -{ - if (m_PortHandle != INVALID_HANDLE_VALUE) - { +bool CSerialPort::Get_CD_State() { + if (m_PortHandle != INVALID_HANDLE_VALUE) { DWORD ModemStat; - if (GetCommModemStatus(m_PortHandle, &ModemStat)) - { + if (GetCommModemStatus(m_PortHandle, &ModemStat)) { return (ModemStat & MS_RLSD_ON) > 0; //Not sure } - else - { - return FALSE; - } - } - else - { - return FALSE; } + return false; } -BOOL CSerialPort::Get_CTS_State() -{ - if (m_PortHandle != INVALID_HANDLE_VALUE) - { +bool CSerialPort::Get_CTS_State() { + if (m_PortHandle != INVALID_HANDLE_VALUE) { DWORD ModemStat; - if (GetCommModemStatus(m_PortHandle, &ModemStat)) - { + if (GetCommModemStatus(m_PortHandle, &ModemStat)) { return (ModemStat & MS_CTS_ON) > 0; } - else - { - return FALSE; - } - } - else - { - return FALSE; } + return false; } -BOOL CSerialPort::Get_DSR_State() -{ - if (m_PortHandle != INVALID_HANDLE_VALUE) - { +bool CSerialPort::Get_DSR_State() { + if (m_PortHandle != INVALID_HANDLE_VALUE) { DWORD ModemStat; - if (GetCommModemStatus(m_PortHandle, &ModemStat)) - { + if (GetCommModemStatus(m_PortHandle, &ModemStat)) { return (ModemStat & MS_DSR_ON) > 0; } - else - { - return FALSE; - } - } - else - { - return FALSE; } + return false; } -BOOL CSerialPort::Get_RI_State() -{ - if (m_PortHandle != INVALID_HANDLE_VALUE) - { +bool CSerialPort::Get_RI_State() { + if (m_PortHandle != INVALID_HANDLE_VALUE) { DWORD ModemStat; - if (GetCommModemStatus(m_PortHandle, &ModemStat)) - { + if (GetCommModemStatus(m_PortHandle, &ModemStat)) { return (ModemStat & MS_RING_ON) > 0; } - else - { - return FALSE; + } + return false; +} + +bool CSerialPort::Set_DTR_State(bool state) { + if (m_PortHandle != INVALID_HANDLE_VALUE) { + if (0 == EscapeCommFunction(m_PortHandle, (state ? SETDTR : CLRDTR))) { + return false; } } - else - { - return FALSE; - } + return true; } -void CSerialPort::Set_DTR_State(BOOL state) -{ - if (m_PortHandle != INVALID_HANDLE_VALUE) - { - EscapeCommFunction(m_PortHandle, (state ? SETDTR : CLRDTR)); - } -} - -BOOL CSerialPort::Get_DTR_State() -{ - if (m_PortHandle != INVALID_HANDLE_VALUE) - { +bool CSerialPort::Get_DTR_State() { + if (m_PortHandle != INVALID_HANDLE_VALUE) { DWORD ModemStat; - if (GetCommModemStatus(m_PortHandle, &ModemStat)) - { + if (GetCommModemStatus(m_PortHandle, &ModemStat)) { return (ModemStat & MS_DSR_ON) > 0; //Not sure } - else - { - return FALSE; - } - } - else - { - return FALSE; } + return false; } -void CSerialPort::Set_RTS_State(BOOL state) -{ - if (m_PortHandle != INVALID_HANDLE_VALUE) - { +bool CSerialPort::Set_RTS_State(bool state) { + if (m_PortHandle != INVALID_HANDLE_VALUE) { if (0 == EscapeCommFunction(m_PortHandle, (state ? SETRTS : CLRRTS))) { - printf("Set_RTS_State(%d) failed.\n", state ? 1 : 0); + return false; } } + return true; } -BOOL CSerialPort::Get_RTS_State() -{ - if (m_PortHandle != INVALID_HANDLE_VALUE) - { +bool CSerialPort::Get_RTS_State() { + if (m_PortHandle != INVALID_HANDLE_VALUE) { DWORD ModemStat; - if (GetCommModemStatus(m_PortHandle, &ModemStat)) - { + if (GetCommModemStatus(m_PortHandle, &ModemStat)) { return (ModemStat & MS_CTS_ON) > 0; //Not sure } - else - { - return FALSE; - } - } - else - { - return FALSE; } + return false; } diff --git a/AVR Working Controller/SerialPort/SerialPort.h b/AVR Working Controller/SerialPort/SerialPort.h index 28b9fd9..8b065b1 100644 --- a/AVR Working Controller/SerialPort/SerialPort.h +++ b/AVR Working Controller/SerialPort/SerialPort.h @@ -24,20 +24,20 @@ class CSerialPort { public: - void Set_RTS_State(BOOL state); - void Set_DTR_State(BOOL state); - BOOL Get_RTS_State(); - BOOL Get_DTR_State(); - BOOL Get_RI_State(); - BOOL Get_DSR_State(); - BOOL Get_CTS_State(); - BOOL Get_CD_State(); + bool Set_RTS_State(bool state); + bool Set_DTR_State(bool state); + bool Get_RTS_State(); + bool Get_DTR_State(); + bool Get_RI_State(); + bool Get_DSR_State(); + bool Get_CTS_State(); + bool Get_CD_State(); virtual uint32_t Write(const LPVOID Buffer, uint32_t BufferSize); virtual uint32_t Read(LPVOID Buffer, uint32_t BufferSize); - virtual BOOL IsOpen(); + virtual bool IsOpen(); virtual void Close(); // Use PortName usually "COM1:" ... "COM4:" note that the name must end by ":" - virtual BOOL Open(LPCTSTR PortName, uint32_t BaudRate, BYTE ByteSize, BYTE Parity, BYTE StopBits, uint32_t DesiredAccess = GENERIC_READ|GENERIC_WRITE); + virtual bool Open(LPCTSTR PortName, uint32_t BaudRate, BYTE ByteSize, BYTE Parity, BYTE StopBits, uint32_t DesiredAccess = GENERIC_READ|GENERIC_WRITE); CSerialPort(); virtual ~CSerialPort(); diff --git a/DataSheets/Yamaha RX-V2400_RS232C_Standard.pdf b/DataSheets/Yamaha RX-V2400_RS232C_Standard.pdf index 73e367a486460c00698d633591f509aa1560ca02..b4d940088e26f9cc3245db1502f7880a5be225bb 100644 GIT binary patch delta 10267 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 delta 8272 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