1
0
mirror of https://github.com/kalymos/PsNee.git synced 2026-05-09 08:37:11 +00:00

Refactor: Clean up serial debug logs and header organization

This commit is contained in:
kalymos
2026-03-20 23:37:02 +01:00
parent 37cc9927d3
commit 97f4f8e645
2 changed files with 202 additions and 228 deletions

View File

@@ -1,136 +1,125 @@
// PSNee-V9.0
/*********************************************************************************************************************
* CONSOLE MODEL SELECTION (SCPH Hardware Configuration)
*********************************************************************************************************************/
/*--------------------------------------------------------------------------------------------------------------------
* Models below do not require BIOS patching.
* Standard USB injection is supported.
*
* SCPH model number // region code | region
*--------------------------------------------------------------------------------------------------------------------*/
#define SCPH_xxx1 // NTSC U/C | America.
// #define SCPH_xxx2 // PAL | Europ.
// #define SCPH_xxx3 // NTSC J | Asia.
// #define SCPH_xxxx // Universal
/*------------------------------------------------------------------------------------------------
Console selection
--------------------------------------------------------------------------------------------------
No BIOS patching.
You can use injection via USB.
SCPH model number // region code | region
-------------------------------------------------------------------------------------------------*/
#define SCPH_xxx1 // NTSC U/C | America.
//#define SCPH_xxx2 // PAL | Europ.
//#define SCPH_xxx3 // NTSC J | Asia.
//#define SCPH_xxxx // Universal
//#define SCPH_5903 // NTSC J | Asia VCD:
// Models that require a BIOS patch.
/*XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Caution!
It is only possible to inject the code via ISP!
For the patch to work, the BIOS version is more important than the SCPH number.
The delay in starting up caused by the bootloader of the Arduino cards prevents the injection of the BIOS patch within the delay,
that's why you have to use the ISP which eliminates the bootloader.
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
// #define SCPH_5903 // NTSC J | Asia VCD:
| Adres pin |
SCPH model number // Data pin | 32-pin BIOS | 40-pin BIOS | BIOS version
-------------------------------------------------------------------------------------------------*/
//#define SCPH_102 // DX - D0 | AX - A7 | | 4.4e - CRC 0BAD7EA9, 4.5e -CRC 76B880E5
//#define SCPH_100 // DX - D0 | AX - A7 | | 4.3j - CRC F2AF798B
//#define SCPH_7500_9000 // DX - D0 | AX - A7 | | 4.0j - CRC EC541CD0
//#define SCPH_7000 // DX - D0 | AX - A7 | | 4.0j - CRC EC541CD0 Enables hardware support for disabling BIOS patching.
//#define SCPH_3500_5500 // DX - D0 | AX - A16 | AX - A15 | 3.0j - CRC FF3EEB8C, 2.2j - CRC 24FC7E17, 2.1j - CRC BC190209
//#define SCPH_3000 // DX - D5 | AX - A7, AY - A8 | AX - A6, AY - A7 | 1.1j - CRC 3539DEF6
//#define SCPH_1000 // DX - D5 | AX - A7, AY - A8 | AX - A6, AY - A7 | 1.0j - CRC 3B601FC8
/*------------------------------------------------------------------------------------------------------------------
* WARNING: These models REQUIRE a BIOS patch.
*
* ISP programming is MANDATORY.
* The Arduino bootloader introduces a delay that prevents the BIOS patch injection.
* Using an ISP programmer eliminates this delay.
*
* Note: BIOS version is more critical than the SCPH number for patch success.
*-------------------------------------------------------------------------------------------------------------------
*
* | Adres pin |
* SCPH model number // Data pin | 32-pin BIOS | 40-pin BIOS | BIOS version
*------------------------------------------------------------------------------------------------------------------*/
// #define SCPH_102 // DX - D0 | AX - A7 | | 4.4e - CRC 0BAD7EA9, 4.5e -CRC 76B880E5
// #define SCPH_100 // DX - D0 | AX - A7 | | 4.3j - CRC F2AF798B
// #define SCPH_7500_9000 // DX - D0 | AX - A7 | | 4.0j - CRC EC541CD0
// #define SCPH_7000 // DX - D0 | AX - A7 | | 4.0j - CRC EC541CD0 Enables hardware support for disabling BIOS patching.
// #define SCPH_3500_5500 // DX - D0 | AX - A16 | AX - A15 | 3.0j - CRC FF3EEB8C, 2.2j - CRC 24FC7E17, 2.1j - CRC BC190209
// #define SCPH_3000 // DX - D5 | AX - A7, AY - A8 | AX - A6, AY - A7 | 1.1j - CRC 3539DEF6
// #define SCPH_1000 // DX - D5 | AX - A7, AY - A8 | AX - A6, AY - A7 | 1.0j - CRC 3B601FC8
/*------------------------------------------------------------------------------------------------
Options
------------------------------------------------------------------------------------------------*/
/*******************************************************************************************************************
* Options
*******************************************************************************************************************/
#define LED_RUN // Turns on the LED when injections occur.
// D13 for Arduino, ATtiny add a led between PB3 (pin 2) and gnd with a 1k resistor in series, ATmega32U4 (Pro Micro) add a led between PB6 (pin 10) and gnd with a 1k resistor in series.
#define LED_RUN // Turns on the LED when injections occur.
// D13 for Arduino, ATtiny add a led between PB3 (pin 2) and gnd with a 1k resistor in series,
// ATmega32U4 (Pro Micro) add a led between PB6 (pin 10) and gnd with a 1k resistor in series.
// #define DEBUG_SERIAL_MONITOR // Enables serial monitor output.
//#define PSNEE_DEBUG_SERIAL_MONITOR // Enables serial monitor output.
/* Requires compilation with Arduino libs!
For Arduino connect TXD and GND, for ATtiny PB3 (pin 2) and GND, to your serial card RXD and GND.
Arduino | ATtiny
---------------------
TXD--RXD | PB3--RXD
GND--GND | GND--GND */
/******************************************************************************************************************
* Requires compilation with Arduino libs!
* For Arduino connect TXD and GND, for ATtiny PB3 (pin 2) and GND, to your serial card RXD and GND.
* Arduino | ATtiny
* ---------------------
* TXD--RXD | PB3--RXD
* GND--GND | GND--GND
*******************************************************************************************************************/
/*------------------------------------------------------------------------------------------------
Hysteresis
------------------------------------------------------------------------------------------------*/
#define HYSTERESIS_MAX 25 // Coupled with hysteresis-reset post-injection; allows for a
// wider tuning range without drifting out of the sweet spot
// between hysteresis_max and hysteresis_reset.
/******************************************************************************************************************
* Summary of practical information. Fuses. Pinout
*******************************************************************************************************************
* Fuses
* MCU | High | Low | Extended
* --------------------------------------------------
* ATmega | DF | EE | FF
* ATtiny | DF | E2 | FF
*
* Pinout
* Arduino | PSNee |
* ---------------------------------------------------
* VCC | VCC |
* GND | GND |
* RST | RESET | Only for JAP_FAT
* D2 | BIOS AX | Only for Bios patch
* D3 | BIOS AY | Only for BIOS patch ver, 1.0j, 1.1j
* D4 | BIOS DX | Only for Bios patch
* D5 | SWITCH | Optional for disabling Bios patch
* D6 | SQCK |
* D7 | SUBQ |
* D8 | DATA |
* D9 | WFCK |
* D13 ^ D10 | LED | D10 only for ATmega32U4_16U4
*
* ATtiny | PSNee | ISP |
* ---------------------------------------------------
* Pin1 | | RESET |
* Pin2 | LED ^ serial | | serial only for PSNEE_DEBUG_SERIAL_MONITOR
* Pin3 | WFCK | |
* Pin4 | GND | GND |
* Pin5 | SQCK | MOSI |
* Pin6 | SUBQ | MISO |
* Pin7 | DATA | SCK |
* Pin8 | VCC | VCC |
*******************************************************************************************************************/
/*------------------------------------------------------------------------------------------------
Summary of practical information. Fuses. Pinout
------------------------------------------------------------------------------------------------*/
/*
Fuses
MCU | High | Low | Extended
--------------------------------------------------
ATmega | DF | EE | FF
ATtiny | DF | E2 | FF
Pinout
Arduino | PSNee |
---------------------------------------------------
VCC | VCC |
GND | GND |
RST | RESET | Only for JAP_FAT
D2 | BIOS AX | Only for Bios patch
D3 | BIOS AY | Only for BIOS patch ver, 1.0j, 1.1j
D4 | BIOS DX | Only for Bios patch
D5 | SWITCH | Optional for disabling Bios patch
D6 | SQCK |
D7 | SUBQ |
D8 | DATA |
D9 | WFCK |
D13 ^ D10 | LED | D10 only for ATmega32U4_16U4
ATtiny | PSNee | ISP |
---------------------------------------------------
Pin1 | | RESET |
Pin2 | LED ^ serial | | serial only for PSNEE_DEBUG_SERIAL_MONITOR
Pin3 | WFCK | |
Pin4 | GND | GND |
Pin5 | SQCK | MOSI |
Pin6 | SUBQ | MISO |
Pin7 | DATA | SCK |
Pin8 | VCC | VCC |
*/
/*------------------------------------------------------------------------------------------------
pointer and variable section
------------------------------------------------------------------------------------------------*/
/*******************************************************************************************************************
* pointer and variable section
*******************************************************************************************************************/
#include "MCU.h"
#include "settings.h"
#include "BIOS_patching.h"
//Flag initializing for automatic console generation selection 0 = old, 1 = pu-22 end ++
uint8_t wfck_mode = 0;
uint8_t wfck_mode = 0; //Flag initializing for automatic console generation selection 0 = old, 1 = pu-22 end ++
uint8_t SUBQBuffer[12]; // Global buffer to store the 12-byte SUBQ channel data
#define HYSTERESIS_MAX 25 // Coupled with hysteresis-reset post-injection; allows for a
// wider tuning range without drifting out of the sweet spot
// between hysteresis_max and hysteresis_reset.
// Variables de contrôle globales
// Global buffer to store the 12-byte SUBQ channel data
uint8_t subqBuffer[12];
uint8_t hysteresis = 0;
//Initializing values for region code injection timing
#define DELAY_BETWEEN_BITS 4000 // 250 bits/s (microseconds) (ATtiny 8Mhz works from 3950 to 4100) PU-23 PU-22 MAX 4250 MIN 3850
/*------------------------------------------------------------------------------------------------
Code section
------------------------------------------------------------------------------------------------*/
/*----------------------------------------------------------------------
/*******************************************************************************************************************
* Code section
********************************************************************************************************************/
/*******************************************************************************************************************
* FUNCTION : BoardDetection
*
* DESCRIPTION :
@@ -156,11 +145,12 @@ uint8_t hysteresis = 0;
* - Initial/Protection Phase: ~7.3 kHz.
* - Standard Data Reading: ~14.6 kHz.
*
*-----------------------------------------------------------------------*/
*******************************************************************************************************************/
void BoardDetection() {
// Default state: 0 (Static/GATE mode for PU-7 to PU-20)
wfck_mode = 0;
uint8_t debounce = 100;
/**
* INITIAL STABILIZATION DELAY (300ms)
@@ -181,7 +171,6 @@ void BoardDetection() {
*/
if (!PIN_WFCK_READ) {
// Software debounce to filter out micro-glitches or parasitic noise
uint8_t debounce = 100;
while (--debounce);
/**
@@ -198,7 +187,7 @@ void BoardDetection() {
}
#if defined(PSNEE_DEBUG_SERIAL_MONITOR)
Debug_Log(debounce, wfck_mode);
BoardDetectionLog(debounce, wfck_mode, INJECT_SCEx);
#endif
}
@@ -209,14 +198,14 @@ void BoardDetection() {
*
* DESCRIPTION :
* Captures a complete 12-byte SUBQ frame from the CD controller.
* Synchronizes with the SQCK (Sub-Q Clock) pin to shift in serial data from
* Synchronizes with the SQCK (SUBQ Clock) pin to shift in serial data from
* the SUBQ pin. This implementation follows the standard PlayStation CDIC
* protocol (Synchronous Serial, LSB first).
******************************************************************************************************************/
void CaptureSUBQ(void) {
uint8_t bytesRemaining = 12; // Total frame size for a complete SUBQ
uint8_t* bufferPtr = subqBuffer;
uint8_t* bufferPtr = SUBQBuffer;
do {
uint8_t currentByte = 0;
@@ -247,7 +236,7 @@ void CaptureSUBQ(void) {
} while (--bytesRemaining); // Efficient countdown for AVR binary size
#if defined(PSNEE_DEBUG_SERIAL_MONITOR)
LogSUBQ(subqBuffer);
CaptureSUBQLog(SUBQBuffer);
#endif
}
@@ -275,8 +264,8 @@ void CaptureSUBQ(void) {
// --- STEP 1: SUBQ Frame Synchronization ---
// Fast filtering: ignore raw data if sync markers (index 1 and 6) are not 0x00.
if (subqBuffer[1] == 0x00 && subqBuffer[6] == 0x00) {
uint8_t pointAddress = subqBuffer[2];
if (SUBQBuffer[1] == 0x00 && SUBQBuffer[6] == 0x00) {
uint8_t pointAddress = SUBQBuffer[2];
/**
* HIT INCREMENT CONDITIONS:
@@ -284,8 +273,8 @@ void CaptureSUBQ(void) {
* (uint8_t)(pointAddress - 0xA0) <= 2 is an optimized check for 0xA0, 0xA1, 0xA2.
* B. TRACKING MAINTENANCE: Keeps count if already synced and reading Mode 0x01 or Data.
*/
if ( (isDataSector && (uint8_t)(pointAddress - 0xA0) <= 2 && subqBuffer[3] != 0x02) ||
(currentHysteresis > 0 && (subqBuffer[0] == 0x01 || isDataSector)) )
if ( (isDataSector && (uint8_t)(pointAddress - 0xA0) <= 2 && SUBQBuffer[3] != 0x02) ||
(currentHysteresis > 0 && (SUBQBuffer[0] == 0x01 || isDataSector)) )
{
hysteresis = currentHysteresis + 1;
return;
@@ -316,23 +305,30 @@ void CaptureSUBQ(void) {
* INPUT : isDataSector (bool) - Filtered flag based on raw sector control bits.
******************************************************************************************/
void FilterSUBQSamples(uint8_t isDataSector) {
uint8_t currentHysteresis = hysteresis;
// --- STEP 1: SUBQ Frame Synchronization ---
// Ignore the raw bitstream unless sync markers (1 & 6) are 0x00.
if (subqBuffer[1] == 0x00 && subqBuffer[6] == 0x00) {
uint8_t pointAddress = subqBuffer[2];
if (SUBQBuffer[1] == 0x00 && SUBQBuffer[6] == 0x00) {
uint8_t pointAddress = SUBQBuffer[2];
// uint8_t syncMarker1 = SUBQBuffer[1];
// uint8_t syncMarker2 = SUBQBuffer[6];
// if (syncMarker1 == 0x00 && syncMarker2 == 0x00) {
// uint8_t addrControl = SUBQBuffer[0];
// uint8_t pointAddress = SUBQBuffer[2];
// uint8_t trackNumber = SUBQBuffer[3];
/*
* HIT INCREMENT CONDITIONS:
* A. LEAD-IN PATTERNS: Detects TOC markers (A0-A2) or Track 01 at spiral start.
* (uint8_t)(subqBuffer[3] - 0x03) >= 0xF5 handles the 0x98 to 0x02 wrap-around.
* (uint8_t)(SUBQBuffer[3] - 0x03) >= 0xF5 handles the 0x98 to 0x02 wrap-around.
* B. TRACKING LOCK: Maintains count if already synced and reading valid sectors.
*/
if ( (isDataSector && (pointAddress >= 0xA0 || (pointAddress == 0x01 && ( (uint8_t)(subqBuffer[3] - 0x03) >= 0xF5)))) ||
(currentHysteresis > 0 && (subqBuffer[0] == 0x01 || isDataSector)) )
if ( (isDataSector && (pointAddress >= 0xA0 || (pointAddress == 0x01 && ( (uint8_t)(SUBQBuffer[3] - 0x03) >= 0xF5)))) ||
(currentHysteresis > 0 && (SUBQBuffer[0] == 0x01 || isDataSector)) )
{
hysteresis = currentHysteresis + 1;
return;
@@ -374,15 +370,15 @@ void PerformInjectionSequence(uint8_t injectSCEx) {
{ 0x59, 0xC9, 0x4B, 0x5D, 0xFA, 0x02 }, // SCEA
{ 0x59, 0xC9, 0x4B, 0x5D, 0xEA, 0x02 } // SCEE
};
const uint16_t BIT_DELAY = 4000; // 4000us is the standard bit timing for SCEx signal (approx. 250 bps)
const uint8_t isWfck = wfck_mode; // Cache wfck_mode to save CPU cycles during the bit loop
hysteresis = (HYSTERESIS_MAX - 10); // Reset hysteresis to mid-point after triggering
#ifdef LED_RUN
PIN_LED_ON;
#endif
// Cache wfck_mode to save CPU cycles during the bit loop
uint8_t isWfck = wfck_mode;
PIN_DATA_OUTPUT;
PIN_DATA_CLEAR;
@@ -416,13 +412,13 @@ void PerformInjectionSequence(uint8_t injectSCEx) {
// BIT 0: Pull DATA low-
PIN_DATA_CLEAR;
if (!isWfck) PIN_DATA_OUTPUT;
_delay_us(DELAY_BETWEEN_BITS);
_delay_us(BIT_DELAY);
} else {
// BIT 1: Handle based on board generation
if (!isWfck) {
// Legacy Mode: Set DATA to High-Z (floating)
PIN_DATA_INPUT;
_delay_us(DELAY_BETWEEN_BITS);
_delay_us(BIT_DELAY);
} else {
/*
WFCK Modulation Loop: Syncs to 7.3kHz or 14.6kHz.
@@ -476,7 +472,7 @@ void PerformInjectionSequence(uint8_t injectSCEx) {
#if defined(PSNEE_DEBUG_SERIAL_MONITOR)
DebugInject();
InjectLog();
#endif
}
@@ -522,7 +518,7 @@ void Init() {
// --- Console Analysis ---
// Identify board revision (PU-7 to PU-22+) to set correct injection timings
BoardDetection();
}
}
int main() {
@@ -540,14 +536,17 @@ int main() {
// Masking bits 7, 6, and 4 simultaneously using 0xD0 (binary 11010000).
// This verifies that the "Data/TOC" bit (0x40) is SET, while bits 7 and 4 are CLEARED.
// Equivalent to: (bit7 == 0 && bit6 == 1 && bit4 == 0).
//uint8_t isDataSector = ((scbuf[0] & 0xD0) == 0x40);
uint8_t isDataSector = ((subqBuffer[0] & 0xD0) == 0x40);
// uint8_t firstByte = SUBQBuffer[0];
// uint8_t isDataSector = ((firstByte & 0xD0) == 0x40);
uint8_t isDataSector = ((SUBQBuffer[0] & 0xD0) == 0x40);
// Execute selected logic
FilterSUBQSamples(isDataSector);
// 5. Execution: Trigger SCEx injection once confidence (hysteresis) is reached
// uint8_t currentHysteresis = hysteresis;
// if (currentHysteresis >= HYSTERESIS_MAX) {
if (hysteresis >= HYSTERESIS_MAX) {
PerformInjectionSequence(INJECT_SCEx);
}

View File

@@ -210,115 +210,90 @@
#endif
/*------------------------------------------------------------------------------------------------
serial debug section
------------------------------------------------------------------------------------------------*/
#if defined(PSNEE_DEBUG_SERIAL_MONITOR)
#if defined(DEBUG_SERIAL_MONITOR)
extern uint8_t hysteresis;
void Debug_Log (uint16_t debounce, int Wfck_mode){ //Information about the MCU, and old or late console mode.
void BoardDetectionLog (uint16_t debounce, uint8_t Wfck_mode, uint8_t region){ //Information about the MCU, and old or late console mode.
#if defined(IS_ATTINY_FAMILY)
mySerial.print("m "); mySerial.println(Wfck_mode);
#else
Serial.print(" MCU frequency: "); Serial.print(F_CPU); Serial.println(" Hz");
static const char* regionNames[] = {
"NTSC-J", // 0
"NTSC-U/C", // 1
"PAL", // 2
"Universal" // 3
};
Serial.print(F(" MCU frequency: ")); Serial.print(F_CPU / 1000000L); Serial.println(F(" MHz"));
Serial.print(" debounce: "); Serial.println(debounce);
Serial.print(" wfck_mode: "); Serial.println(Wfck_mode);
Serial.print(" region: "); Serial.print(region[0]); Serial.print(region[1]); Serial.println(region[2]);
Serial.print(" region: "); Serial.print(regionNames[region]);
#endif
}
// log SUBQ packets. We only have 12ms to get the logs written out. Slower MCUs get less formatting.
void Debug_Scbuf (uint8_t *Scbuf){ // Data from the DATA bus
#if defined(IS_ATTINY_FAMILY)
if (!(Scbuf[0] == 0 && Scbuf[1] == 0 && Scbuf[2] == 0 && Scbuf[3] == 0)) { // a bad sector read is all 0 except for the CRC fields. Don't log it.
for (int i = 0; i < 12; i++) {
if (Scbuf[i] < 0x10) {
mySerial.print("0"); // padding
#if defined(IS_ATTINY_FAMILY)
// --- COMPACT SYSTEM LOG (ATtiny) ---
// Minimalistic output to save CPU cycles and maintain timing precision.
mySerial.print("m ");
mySerial.println(Wfck_mode); // Mode indicator
#else
// --- VERBOSE DIAGNOSTICS (ATmega) ---
// Detailed system information for standard development and debugging.
Serial.print(" MCU frequency: ");
Serial.print(F_CPU);
Serial.println(" Hz");
Serial.print(" wfck_mode: ");
Serial.println(Wfck_mode); // Board generation (0: Legacy, 1: Modern)
Serial.print(" region: ");
Serial.print(region[0]);
Serial.print(region[1]);
Serial.println(region[2]); // Active injection string (e.g., SCEE)
#endif
}
#endif
/******************************************************************************************
* FUNCTION : LogSUBQ
*
* DESCRIPTION :
* Logs captured SUBQ frames to the serial interface.
* Timing is critical: the entire 12-byte frame must be processed and transmitted
* within the ~12ms window before the next SUBQ packet arrives.
*
* - ATtiny: Uses minimal formatting (no spaces) to stay within the timing budget.
* - Standard MCUs: Includes spaces for better readability.
*
* INPUT : dataBuffer (uint8_t*) - Pointer to the 12-byte SUBQ frame.
******************************************************************************************/
void LogSUBQ(uint8_t *dataBuffer) {
void CaptureSUBQLog(uint8_t *dataBuffer) {
static uint16_t errorCount = 0; // Tracks missed sectors between valid reads
/**
* ERROR FILTERING:
* A failed sector read usually results in zeroed data (excluding CRC).
* Skip logging if the first 4 bytes are null to reduce bus traffic.
*/
if (!(dataBuffer[0] == 0 && dataBuffer[1] == 0 && dataBuffer[2] == 0 && dataBuffer[3] == 0)) {
// Cache the first 4 bytes for a quick null check (failed read)
uint8_t b0 = dataBuffer[0], b1 = dataBuffer[1], b2 = dataBuffer[2], b3 = dataBuffer[3];
// --- ERROR FILTERING ---
// If the sector is empty (all zeros), increment error counter and exit
if (b0 == 0 && b1 == 0 && b2 == 0 && b3 == 0) {
errorCount++;
return;
}
// --- ERROR REPORTING ---
// If errors occurred before this valid read, report the total count
if (errorCount > 0) {
#if defined(IS_ATTINY_FAMILY)
mySerial.print(F(" [Err x")); mySerial.print(errorCount); mySerial.println(F("]"));
#else
Serial.print(F(" [Missed sectors: ")); Serial.print(errorCount); Serial.println(F("]"));
#endif
errorCount = 0; // Reset counter after reporting
}
// --- DATA & HYSTERESIS DISPLAY ---
#if defined(IS_ATTINY_FAMILY)
// Compact hex output for ATtiny to maintain 12ms timing
for (uint8_t i = 0; i < 12; i++) {
uint8_t val = dataBuffer[i];
if (val < 0x10) mySerial.print("0");
mySerial.print(val, HEX);
}
// Append global hysteresis on the same line
mySerial.print(F(" h:"));
mySerial.println(hysteresis);
#else
// Formatted hex output for ATmega
for (uint8_t i = 0; i < 12; i++) {
uint8_t val = dataBuffer[i];
if (val < 0x10) Serial.print("0");
Serial.print(val, HEX);
Serial.print(" ");
}
// Append global hysteresis on the same line
Serial.print(F("| Hyst: "));
Serial.println(hysteresis);
#endif
}
void InjectLog(){
#if defined(IS_ATTINY_FAMILY)
// --- COMPACT FORMATTING (ATtiny) ---
// Minimal formatting to meet the strict 12ms timing constraint on slower MCUs.
for (uint8_t i = 0; i < 12; i++) {
if (dataBuffer[i] < 0x10) {
mySerial.print("0"); // Leading zero padding for hex alignment
}
mySerial.print(Scbuf[i, HEX]);
}
mySerial.println("");
}
#elif !defined(IS_ATTINY_FAMILY)
if (!(Scbuf[0] == 0 && Scbuf[1] == 0 && Scbuf[2] == 0 && Scbuf[3] == 0)) {
for (int i = 0; i < 12; i++) {
if (Scbuf[i] < 0x10) {
Serial.print("0"); // padding
}
Serial.print(Scbuf[i], HEX);
Serial.print(" ");
}
Serial.println("");
}
#endif
}
void Debug_Inject(){ // Confirmation of region code injection
#if defined(IS_ATTINY_FAMILY)
// --- MINIMALIST NOTIFICATION (ATtiny) ---
mySerial.print("!");
#else
// --- VERBOSE NOTIFICATION (ATmega) ---
// Standard visual feedback for debugging and monitoring.
mySerial.print("!"); // --- MINIMALIST NOTIFICATION (ATtiny) ---
#else
Serial.println(" INJECT ! ");
#endif
#endif
}
#endif