Files
AVR/AVR Working Controller/AVR.cpp

974 lines
30 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#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; // Dont 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", "<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 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", &param1)) {
avrOnPort = param1;
} else if (2 == sscanf_s(argv[i], "-SerialPort=%d,%d", &param1, &param2)) {
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 <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;
}