Add support for extended messages (advanced protocol), Add handy command lines for Power, Volume in db, and Mute.
1172 lines
38 KiB
C++
1172 lines
38 KiB
C++
#pragma warning(disable : 4706) // assignment within conditional expression
|
||
#pragma warning(disable : 4996) // _CRT_SECURE_NO_DEPRECATE
|
||
|
||
#include "stdafx.h"
|
||
#include <Windows.h>
|
||
#include <conio.h>
|
||
#include <stdio.h>
|
||
#include <stdlib.h>
|
||
#include <string.h>
|
||
#include <ctype.h>
|
||
#include <time.h>
|
||
#include <timeapi.h>
|
||
#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];
|
||
char ininame[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 GetAndSendExtendedMessage();
|
||
void GetAndSendOSDMessage();
|
||
bool SendExtendedMessage(char *buf);
|
||
|
||
void EmitRuntimeHelp();
|
||
|
||
bool UserExitRequested = false;
|
||
bool SystemExitRequested = false;
|
||
|
||
typedef struct {
|
||
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;
|
||
|
||
|
||
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;
|
||
}
|
||
|
||
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
|
||
//
|
||
// [ESC]
|
||
// [1!] [2@] [3#] 4 5 6 7 8 9 0 - =
|
||
// qQ wW eE r[R] t[T] [yY] [uU] [iI] o[O] [pP] [{ ]} \|
|
||
// aA [sS] dD f[F] gG hH jJ kK lL ;[:] '"
|
||
// zZ xX cC [vV] bB nN [mM] ,< .> [/?]
|
||
//
|
||
const UserCmd_T UserCommands[] = {
|
||
{ 'R', "Ready", {"\x11" "000" "\x03", 5 } },
|
||
{ 'P', "Power On", },
|
||
{ 'p', "Power Off", },
|
||
{ '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} },
|
||
{ 'F', "Factory Reset", {"\x13" "\x7F\x7F\x7F" "\x03", 5} },
|
||
|
||
//{ 'J', "Dual Mono - Main", {"\x02" "07E93" "\x03", 7} },
|
||
//{ 'K', "Dual Mono - Sub", {"\x02" "07E94" "\x03", 7} },
|
||
//{ 'L', "Dual Mono - All", {"\x02" "07E95" "\x03", 7} },
|
||
{ ':', "Send Command Message" },
|
||
{ '=', "Send Extended Message" },
|
||
{ '/', "Show All" },
|
||
{ '?', "Help on runtime commands" },
|
||
{ 'S', "Set RTS" },
|
||
{ 's', "Clear RTS" },
|
||
{ '\x1B', "Quit App" },
|
||
};
|
||
|
||
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 '=':
|
||
GetAndSendExtendedMessage();
|
||
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 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);
|
||
}
|
||
}
|
||
|
||
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", "<esc>");
|
||
} 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 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));
|
||
}
|
||
}
|
||
|
||
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, '\\');
|
||
if (p) {
|
||
p++;
|
||
} else {
|
||
p = name;
|
||
}
|
||
strcpy_s(progname, MAXTEXTLEN, p);
|
||
p = strrchr(progname, '.');
|
||
if (p && 0 == stricmp(p, ".exe")) {
|
||
*p = '\0';
|
||
}
|
||
strcpy_s(ininame, MAXTEXTLEN, progname);
|
||
strcat_s(ininame, MAXTEXTLEN, ".ini");
|
||
}
|
||
|
||
|
||
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() {
|
||
const char * by = "by Smartware Computing";
|
||
printf("%s [Options] %80s\n", progname, by);
|
||
printf("\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(" 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(" 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");
|
||
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;
|
||
}
|
||
|
||
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 ( ) */
|
||
/******************************************************/
|
||
|
||
// 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[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]);
|
||
UserCommandsSanityCheck(); // If the table is bad, we exit here
|
||
|
||
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)) {
|
||
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 (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")) {
|
||
bool saveVerbose = verboseMode;
|
||
verboseMode = true;
|
||
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();
|
||
exit(EXIT_IllegalOption);
|
||
}
|
||
}
|
||
if (argc == 0 || avrOnPort == COM_NO_PORT) {
|
||
EmitCommandLineHelp();
|
||
exit(EXIT_OK);
|
||
}
|
||
|
||
if (AttachToSerialPort()) {
|
||
if (pExtendedMessage || (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) {
|
||
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
|
||
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));
|
||
}
|
||
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-V2400 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 <cr>)
|
||
//
|
||
//
|
||
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;
|
||
}
|
||
|