From be6c7eb01c1a954ff4b210c11d05248fd661ec2d Mon Sep 17 00:00:00 2001 From: Sergey Ko Date: Mon, 10 Jun 2024 16:23:43 -0600 Subject: [PATCH] TEST: re-implemented webSocket server (passive client), Dev. Mode output now works through temporary buffer, WebUI fixes --- configure.json | 5 +- gbs-control.ino | 37 +- package.json | 2 +- public/src/index.html.tpl | 13 +- public/src/index.ts | 314 ++-- public/src/style.css | 8 +- src/OLEDMenuTranslations.h | 35 +- src/main.cpp | 34 +- src/options.h | 15 +- src/presets.cpp | 57 +- src/slot.cpp | 18 +- src/slot.h | 2 +- src/video.cpp | 19 +- src/wifiman.cpp | 173 +- src/wifiman.h | 6 +- src/wserial.cpp | 62 +- src/wserial.h | 20 +- src/wserver.cpp | 3213 +++++++++++++++++++----------------- src/wserver.h | 5 +- translation.hdwui.json | 16 +- translation.webui.json | 27 +- 21 files changed, 2195 insertions(+), 1886 deletions(-) diff --git a/configure.json b/configure.json index e6710e3..f6e0a5a 100644 --- a/configure.json +++ b/configure.json @@ -15,10 +15,11 @@ "oled_menu_scroll_lead_in_time_in_ms": 600, "oled_menu_screen_saver_kick_in_seconds": 180, "oled_menu_over_draw": 0, - "oled_menu_reset_always_scroll_on_selection": 0 + "oled_menu_reset_always_scroll_on_selection": 0, + "serial_buffer_max_len": 1024 }, "ui-lang": "en", "ui-hdw-fonts": "12@FreeSans,16@FreeSans", "ui-web-font": "Oswald Regular", - "version": "24.0607" + "version": "24.0610" } \ No newline at end of file diff --git a/gbs-control.ino b/gbs-control.ino index ada178e..8497045 100644 --- a/gbs-control.ino +++ b/gbs-control.ino @@ -1,14 +1,14 @@ //! This is autogenerated file //! Please do not edit if you're using PlatformIO -#define VERSION "24.0607" +#define VERSION "24.0610" /* ########################################################################### # File: main.cpp # # File Created: Friday, 19th April 2024 3:13:38 pm # # Author: Robert Neumann # -# Last Modified: Sunday, 2nd June 2024 11:15:45 pm # +# Last Modified: Monday, 10th June 2024 4:04:57 pm # # Modified By: Sergey Ko # # # # License: GPLv3 # @@ -173,6 +173,10 @@ void setup() // dev rto->invertSync = false; + rto->debugView = false; + rto->developerMode = false; + rto->freezeCapture = false; + rto->adcFilter = false; adco->r_gain = 0; adco->g_gain = 0; @@ -187,7 +191,7 @@ void setup() if (rto->webServerEnabled) { wifiInit(); - webSocket.begin(); + serverWebSocketInit(); serverInit(); #ifdef HAVE_PINGER_LIBRARY @@ -224,7 +228,7 @@ void setup() while (millis() - initDelay < 1500) { display.drawXbm(2, 2, gbsicon_width, gbsicon_height, gbsicon_bits); display.display(); - wifiLoop(0); + // wifiLoop(0); delay(1); } display.clear(); @@ -275,8 +279,8 @@ void setup() // ? WHY? initDelay = millis(); while (millis() - initDelay < 1000) { - wifiLoop(0); - delay(1); + // wifiLoop(0); + delay(10); } // dummy commands @@ -306,9 +310,8 @@ void setup() // restart and dummy startWire(); - delay(1); + delay(10); GBS::STATUS_00::read(); - delay(1); if (!checkBoardPower()) { stopWire(); @@ -335,6 +338,7 @@ void setup() } zeroAll(); + _DBGN(F("(!) reset runtime parameters while setup")); setResetParameters(); prepareSyncProcessor(); @@ -349,8 +353,6 @@ void setup() // FIXME double reset? // setResetParameters(); - // wifiLoop(1); - // startup reliability test routine /*rto->videoStandardInput = 1; writeProgramArrayNew(ntsc_240p, 0); @@ -391,11 +393,7 @@ void loop() static unsigned long lastTimeInterruptClear = millis(); menuLoop(); - wifiLoop(0); // WiFi + OTA + WS + MDNS, checks for server enabled + started - // Serial takes precedence - handleSerialCommand(); - // handle user commands - handleUserCommand(); + wifiLoop(); // WiFi + OTA + WS + MDNS, checks for server enabled + started // run FrameTimeLock if enabled if (uopt->enableFrameTimeLock && rto->sourceDisconnected == false @@ -640,8 +638,17 @@ void loop() } #endif // HAVE_PINGER_LIBRARY + // Serial takes precedence + handleSerialCommand(); + // skip the code below if we don't hava the web server + if (!rto->webServerEnabled || !rto->webServerStarted) + return; + // handle user commands + handleUserCommand(); // web client handler server.handleClient(); + MDNS.update(); + dnsServer.processNextRequest(); // websocket event handler webSocket.loop(); // handle ArduinoIDE diff --git a/package.json b/package.json index 814e4a3..0f69fa7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gbscontrol-webui", - "version": "1.2.0", + "version": "1.2.2", "description": "gbscontrol-webui", "author": "", "license": "GPL-3.0", diff --git a/public/src/index.html.tpl b/public/src/index.html.tpl index ee4694b..024e151 100644 --- a/public/src/index.html.tpl +++ b/public/src/index.html.tpl @@ -364,19 +364,19 @@
L{SLOT_SETTINGS_LEGEND}
- + -
  • L{MATCH_PRESETS_SWITCH_HELP_1}
  • + - +
    L{FULL_HEIGHT_SWITCH} @@ -540,7 +540,8 @@
  • L{DEVELOPER_MODE_SWITCH_HELP_1}
  • toggle_offtoggle_off
    @@ -553,7 +554,7 @@
    L{DEVELOPER_LEGEND}
    - diff --git a/public/src/index.ts b/public/src/index.ts index 6e799c5..1a92567 100644 --- a/public/src/index.ts +++ b/public/src/index.ts @@ -81,7 +81,7 @@ const Structs: StructDescriptors = { { name: 'motionAdaptive', type: 'byte', size: 1 }, { name: 'bob', type: 'byte', size: 1 }, { name: 'fullHeight', type: 'byte', size: 1 }, - { name: 'matchPreset', type: 'byte', size: 1 }, + // { name: 'matchPreset', type: 'byte', size: 1 }, { name: 'palForce60', type: 'byte', size: 1 }, ], } @@ -203,7 +203,7 @@ const GBSControl = { scanSSIDDone: false, serverIP: '', structs: null, - timeOutWs: 0, + // timeOutWs: 0, ui: { backupButton: null, backupInput: null, @@ -249,9 +249,42 @@ const GBSControl = { }, ws: null, wsCheckTimer: 0, - wsConnectCounter: 0, + // wsConnectCounter: 0, wsNoSuccessConnectingCounter: 0, - wsTimeout: 0, + developerMode: false, + wsHeartbeatInterval: null + // wsTimeout: 0 +} + +/** + * Description placeholder + * + * @type {({ lsObject: {}; write(key: string, value: any): void; read(key: string): string | number | boolean; })} + */ +const GBSStorage = { + lsObject: {}, + write(key: string, value: any) { + GBSStorage.lsObject = GBSStorage.lsObject || {} + GBSStorage.lsObject[key] = value + localStorage.setItem( + 'GBSControlSlotNames', + JSON.stringify(GBSStorage.lsObject) + ) + }, + read(key: string): string | number | boolean { + GBSStorage.lsObject = JSON.parse( + localStorage.getItem('GBSControlSlotNames') || '{}' + ) + return GBSStorage.lsObject[key] + }, +} + + +/** + * Reset current section(tab) to slots + */ +const resetCurrentPageSection = () => { + GBSStorage.write('section', 'presets') } /** websocket services */ @@ -283,16 +316,16 @@ const checkWebSocketServer = () => { /** * Description placeholder */ -const timeOutWs = () => { - console.log('timeOutWs') +// const timeOutWs = () => { +// console.log('timeOutWs') - if (GBSControl.ws) { - GBSControl.ws.close() - } +// if (GBSControl.ws) { +// GBSControl.ws.close() +// } - GBSControl.isWsActive = false - displayWifiWarning(true) -} +// GBSControl.isWsActive = false +// displayWifiWarning(true) +// } /** * Description placeholder @@ -316,14 +349,13 @@ const nodelistToArray = ( * @param {string} id * @returns {(button: HTMLElement, _index: any, _array: any) => void} */ -const toggleButtonActive = - (id: string) => (button: HTMLElement, _index: any, _array: any) => { - button.removeAttribute('active') +const toggleButtonActive = (id: string) => (button: HTMLElement, _index: any, _array: any) => { + button.removeAttribute('active') - if (button.getAttribute('gbs-element-ref') === id) { - button.setAttribute('active', '') - } + if (button.getAttribute('gbs-element-ref') === id) { + button.setAttribute('active', '') } +} /** * Description placeholder @@ -511,40 +543,42 @@ const createWebSocket = () => { GBSControl.wsNoSuccessConnectingCounter = 0 GBSControl.ws = new WebSocket(GBSControl.webSocketServerUrl, ['arduino']) + // change message data type + GBSControl.ws.binaryType = "arraybuffer"; - GBSControl.ws.onopen = () => { - // console.log("ws onopen"); + if(!GBSControl.ws) { + displayWifiWarning(true) + return + } + GBSControl.ws.onopen = () => { + console.log("ws.open") displayWifiWarning(false) - - GBSControl.wsConnectCounter++ - clearTimeout(GBSControl.wsTimeout) - GBSControl.wsTimeout = window.setTimeout(timeOutWs, 6000) GBSControl.isWsActive = true GBSControl.wsNoSuccessConnectingCounter = 0 } GBSControl.ws.onclose = () => { - // console.log("ws.onclose"); - - clearTimeout(GBSControl.wsTimeout) + console.log("ws.close") + displayWifiWarning(true) GBSControl.isWsActive = false } - GBSControl.ws.onmessage = async (message: any) => { - clearTimeout(GBSControl.wsTimeout) - GBSControl.wsTimeout = window.setTimeout(timeOutWs, 2700) + GBSControl.ws.onmessage = (message: any) => { + // GBSControl.wsTimeout = window.setTimeout(timeOutWs, 2700) GBSControl.isWsActive = true - // message data is blob - let buf = null - try { - buf = await message.data.arrayBuffer() - } catch (err) { - // must not exit here since we're filtering out - // terminal data and system state data with '#' - } + // let buf = null + // try { + // buf = await message.data.arrayBuffer() + // } catch (err) { + // // must not exit here since we're filtering out + // // terminal data and system state data with '#' + // } + if (!(message.data instanceof ArrayBuffer)) + return; // into array of DEC values - const bufArr = Array.from(new Uint8Array(buf)) + const bufUint8Arr = new Uint8Array(message.data) + const bufArr = Array.from(bufUint8Arr) const [ optionByte0, // always # optionByte1, // current slot ID (int) @@ -554,13 +588,17 @@ const createWebSocket = () => { optionByte4, // deintMode & wantTap6 & wantFullHeight & matchPresetSource & PalForce60 optionByte5, // wantOutputComponent & enableCalibrationADC & preferScalingRgbhv & disableExternalClockGenerator // developer tab - optionByte6, // printInfos, invertSync, oversampling, ADC Filter, debugView + optionByte6, // developerMode, printInfos, invertSync, oversampling, ADC Filter, debugView, freezeCapture // system tab optionByte7, // enableOTA ] = bufArr if (optionByte0 != '#'.charCodeAt(0)) { - GBSControl.queuedText += message.data + if (!("TextDecoder" in window)) + GBSControl.queuedText = 'L{DEVELOPER_JS_ALERT_TEXTDECODER}'; + const decoder = new TextDecoder("utf-8") + + GBSControl.queuedText += decoder.decode(bufUint8Arr) GBSControl.dataQueued += message.data.length if (GBSControl.dataQueued >= 70000) { @@ -637,12 +675,12 @@ const createWebSocket = () => { case 'fullHeight': toggleButtonCheck(button, (optionByte4 & 0x04) == 0x04) break - case 'matchPreset': - toggleButtonCheck(button, (optionByte4 & 0x08) == 0x08) - break case 'palForce60': - toggleButtonCheck(button, (optionByte4 & 0x10) == 0x10) + toggleButtonCheck(button, (optionByte4 & 0x08) == 0x08) break + // case 'matchPreset': + // toggleButtonCheck(button, (optionByte4 & 0x08) == 0x08) + // break /** 4: system preferences tab */ case 'wantOutputComponent': toggleButtonCheck(button, (optionByte5 & 0x01) == 0x01) @@ -675,19 +713,39 @@ const createWebSocket = () => { const debugView = document.querySelector( `button[gbs-message="D"][gbs-message-type="action"]` ) + const freezeCaptureButton = document.querySelector( + `button[gbs-message="F"][gbs-message-type="user"]` + ); if ((optionByte6 & 0x01) == 0x01) + GBSControl.developerMode = true; + else + GBSControl.developerMode = false; + toggleDeveloperMode() + + if ((optionByte6 & 0x02) == 0x02) printInfoButton.setAttribute('active', '') - else printInfoButton.removeAttribute('active') - if ((optionByte6 & 0x02) == 0x02) invertSync.setAttribute('active', '') - else invertSync.removeAttribute('active') + else + printInfoButton.removeAttribute('active') if ((optionByte6 & 0x04) == 0x04) + invertSync.setAttribute('active', '') + else + invertSync.removeAttribute('active') + if ((optionByte6 & 0x08) == 0x08) oversampling.setAttribute('active', '') - else oversampling.removeAttribute('active') - if ((optionByte6 & 0x08) == 0x08) adcFilter.setAttribute('active', '') - else adcFilter.removeAttribute('active') + else + oversampling.removeAttribute('active') if ((optionByte6 & 0x10) == 0x10) + adcFilter.setAttribute('active', '') + else + adcFilter.removeAttribute('active') + if ((optionByte6 & 0x20) == 0x20) debugView.setAttribute('active', '') - else debugView.removeAttribute('active') + else + debugView.removeAttribute('active') + if ((optionByte6 & 0x40) == 0x40) + freezeCaptureButton.setAttribute('active', '') + else + freezeCaptureButton.removeAttribute('active') // system tab const enableOTAButton = document.querySelector( @@ -751,7 +809,9 @@ const createIntervalChecks = () => { const loadDoc = (link: string) => { return fetch( `http://${GBSControl.serverIP}/sc?${link}&nocache=${new Date().getTime()}` - ) + ).catch(() => { + // do something + }) } /** @@ -765,11 +825,14 @@ const loadUser = (link: string) => { GBSControl.isWsActive = false GBSControl.ui.terminal.value += '\nL{DEVICE_RESTARTING_CONSOLE_MESSAGE}\n' GBSControl.ui.terminal.scrollTop = GBSControl.ui.terminal.scrollHeight + resetCurrentPageSection() } return fetch( `http://${GBSControl.serverIP}/uc?${link}&nocache=${new Date().getTime()}` - ) + ).catch(() => { + // do something + }) } /** WIFI management */ @@ -800,6 +863,8 @@ const wifiGetStatus = () => { ) GBSControl.ui.wifiStaSSID.innerHTML = `${GBSControl.wifi.ssid}` } + }).catch(() => { + // do something }) } @@ -831,6 +896,8 @@ const wifiConnect = () => { window.location.href = 'http://gbscontrol.local/' }) .catch(() => {}) + }).catch(() => { + // do something }) } @@ -847,6 +914,8 @@ const wifiScanSSID = () => { }).then(() => { GBSControl.scanSSIDDone = true window.setTimeout(wifiScanSSID, 3000) + }).catch(() => { + // do something }) return } @@ -885,6 +954,8 @@ const wifiScanSSID = () => { GBSControl.ui.wifiList.removeAttribute('hidden') GBSControl.ui.wifiConnect.setAttribute('hidden', '') } + }).catch(() => { + // do something }) } @@ -925,6 +996,8 @@ const wifiSetAPMode = () => { }) .catch(() => {}) return response + }).catch(() => { + // do something }) } @@ -1031,6 +1104,8 @@ const fetchSlotNames = () => { return true } return false + }).catch(() => { + // do something }) } @@ -1065,6 +1140,8 @@ const removePreset = () => { fetchSlotNamesErrorRetry() } }) + }).catch(() => { + // do something }) }) .catch(() => {}) @@ -1106,6 +1183,8 @@ const savePreset = () => { }) }, 500) // }); + }).catch(() => { + // do something }) } }) @@ -1142,6 +1221,9 @@ const getSlotsHTML = () => { */ const setSlot = (slot: string, el: HTMLElement) => { fetch(`http://${GBSControl.serverIP}/slot/set?index=${slot}&${+new Date()}`) + .catch(() => { + // do something + }) } /** @@ -1226,17 +1308,6 @@ const toggleHelp = () => { updateHelp(!help) } -/** - * Description placeholder - */ -const toggleDeveloperMode = () => { - const developerMode = GBSStorage.read('developerMode') || false - - GBSStorage.write('developerMode', !developerMode) - // updateDeveloperMode(!developerMode) - initDeveloperMode(); -} - /** * Description placeholder * @@ -1253,18 +1324,19 @@ const updateHelp = (help: boolean) => { /** * Toggle console visibility (see corresponding button) * - * @param {boolean} developerMode */ -const updateConsoleVisibility = (developerMode: boolean) => { - // const developerMode = GBSStorage.read("developerMode") as boolean; - if (developerMode) { +const updateConsoleVisibility = () => { + // const developerMode = GBSStorage.read('developerMode') || false + if (GBSControl.developerMode) { const consoleStatus = GBSStorage.read('consoleVisible') as boolean - if (consoleStatus !== true) { + if (consoleStatus != true) { GBSStorage.write('consoleVisible', true) + // GBSControl.consoleVisible = true GBSControl.ui.toggleConsole.setAttribute('active', '') document.body.classList.remove('gbs-output-hide') } else { GBSStorage.write('consoleVisible', false) + // GBSControl.consoleVisible = false GBSControl.ui.toggleConsole.removeAttribute('active') document.body.classList.add('gbs-output-hide') } @@ -1272,51 +1344,30 @@ const updateConsoleVisibility = (developerMode: boolean) => { } /** - * Description placeholder - * - * @param {boolean} developerMode + * Toggle developer mode (see WS heartbeat) */ -const updateDeveloperMode = (developerMode: boolean) => { - const el = document.querySelector( - '[gbs-section="developer"]' - ) as HTMLElement - if (developerMode) { - GBSStorage.write('consoleVisible', true) +const toggleDeveloperMode = () => { + const el = document.querySelector('[gbs-section="developer"]') as HTMLElement + const consoleStatus = GBSStorage.read('consoleVisible') as boolean + if (GBSControl.developerMode) { el.removeAttribute('hidden') + if(consoleStatus == true) { + document.body.classList.remove('gbs-output-hide') + } GBSControl.ui.developerSwitch.setAttribute('active', '') - document.body.classList.remove('gbs-output-hide') + GBSControl.ui.developerSwitch.querySelector('.gbs-icon').innerText = "toggle_on" + // if(!GBSControl.wsHeartbeatInterval) + // GBSControl.wsHeartbeatInterval = window.setInterval(webSocketHeartbeat, 900); } else { el.setAttribute('hidden', '') - GBSControl.ui.developerSwitch.removeAttribute('active') + GBSStorage.write('consoleVisible', true) document.body.classList.add('gbs-output-hide') + GBSControl.ui.developerSwitch.removeAttribute('active') + GBSControl.ui.developerSwitch.querySelector('.gbs-icon').innerText = "toggle_off" + window.clearInterval(GBSControl.wsHeartbeatInterval); } +}; - GBSControl.ui.developerSwitch.querySelector('.gbs-icon').innerText = - developerMode ? 'toggle_on' : 'toggle_off' -} - -/** - * Description placeholder - * - * @type {({ lsObject: {}; write(key: string, value: any): void; read(key: string): string | number | boolean; })} - */ -const GBSStorage = { - lsObject: {}, - write(key: string, value: any) { - GBSStorage.lsObject = GBSStorage.lsObject || {} - GBSStorage.lsObject[key] = value - localStorage.setItem( - 'GBSControlSlotNames', - JSON.stringify(GBSStorage.lsObject) - ) - }, - read(key: string): string | number | boolean { - GBSStorage.lsObject = JSON.parse( - localStorage.getItem('GBSControlSlotNames') || '{}' - ) - return GBSStorage.lsObject[key] - }, -} /** * Description placeholder @@ -1388,10 +1439,14 @@ const doRestore = (f: File) => { body: formData, }).then((response) => { // backupInput.removeAttribute("disabled"); + // start with 1st tab + resetCurrentPageSection() window.setTimeout(() => { window.location.reload() - }, 2000) + }, 4000) return response + }).catch(() => { + // do something }) } ) @@ -1450,10 +1505,14 @@ const initMenuButtons = () => { ) const scroll = document.querySelector('.gbs-scroll') - menuButtons.forEach((button) => + let currentPage = GBSStorage.read('section') || 'presets' + if(!GBSControl.developerMode && currentPage === 'developer') + currentPage = 'presets' + menuButtons.forEach((button) => { + const sectionName = button.getAttribute('gbs-section') button.addEventListener('click', () => { - const section = button.getAttribute('gbs-section') - + const section = sectionName; + GBSStorage.write('section', section) sections.forEach((section) => section.setAttribute('hidden', '')) document .querySelector(`section[name="${section}"]`) @@ -1463,7 +1522,10 @@ const initMenuButtons = () => { button.setAttribute('active', '') scroll.scrollTo(0, 1) }) - ) + // get back to the last section after reload + if(currentPage === sectionName) + button.dispatchEvent(new Event("click")) + }) } /** @@ -1490,7 +1552,7 @@ const initGBSButtons = () => { // custom events applied for some buttons button.addEventListener('click', () => { if(message == '1' && messageType == 'user') { - // reset to defaults button + // reset to defaults (factory) command gbsAlert( 'L{RESET_BUTTON_JS_ALERT_MESSAGE}', '
    close
    L{JS_NO}
    ', @@ -1508,6 +1570,11 @@ const initGBSButtons = () => { } ).catch(() => { }); + } else if(message == 'a' && messageType == 'user') { + // restart device command + window.setTimeout(() => { + window.location.reload() + }, 5000) } else { // all other buttons action(message, button) @@ -1635,7 +1702,11 @@ const initGeneralListeners = () => { GBSControl.ui.wifiConnectButton.addEventListener('click', wifiConnect) GBSControl.ui.wifiApButton.addEventListener('click', wifiSetAPMode) GBSControl.ui.wifiStaButton.addEventListener('click', wifiScanSSID) - GBSControl.ui.developerSwitch.addEventListener('click', toggleDeveloperMode) + // GBSControl.ui.developerSwitch.addEventListener('click', () => { + // const developerMode = GBSStorage.read('developerMode') || false + // GBSStorage.write('developerMode', !developerMode) + // updateConsoleVisibility() + // }) GBSControl.ui.removeSlotButton.addEventListener('click', () => { removePreset() }) @@ -1674,22 +1745,10 @@ const initGeneralListeners = () => { gbsPromptPromise.reject() } }) -} -/** - * Description placeholder - */ -const initDeveloperMode = () => { - const devMode = GBSStorage.read('developerMode') as boolean - if (devMode === undefined) { - GBSStorage.write('developerMode', false) - updateDeveloperMode(false) - } else { - updateDeveloperMode(devMode) - } // toggle console visibility button GBSControl.ui.toggleConsole.addEventListener('click', () => { - updateConsoleVisibility(devMode) + updateConsoleVisibility() }) } @@ -1719,7 +1778,6 @@ const initUI = () => { initClearButton() initControlMobileKeys() initUnloadListener() - initDeveloperMode() initHelp() } diff --git a/public/src/style.css b/public/src/style.css index e750898..b7c364f 100644 --- a/public/src/style.css +++ b/public/src/style.css @@ -16,7 +16,7 @@ --viewport-height: 100%; /*GBC - --color-black: black; + --color-black: black; --color-cod-gray-light: #181818; --color-cod-gray: #101010; --color-mine-shaft-light: #0f380f; @@ -60,16 +60,14 @@ body { height: 100%; touch-action: manipulation; width: 100%; + margin: 0; + padding: 0; } body { background-color: var(--color-cod-gray); font-family: "${webUIFontName}", sans-serif; - height: 100%; - margin: 0; overflow: hidden; - padding: 0; - width: 100%; } * { diff --git a/src/OLEDMenuTranslations.h b/src/OLEDMenuTranslations.h index 6ec20aa..acebe97 100644 --- a/src/OLEDMenuTranslations.h +++ b/src/OLEDMenuTranslations.h @@ -99,33 +99,36 @@ const unsigned char OM_PRESET [] PROGMEM = { }; -#define OM_RESET_RESTORE_WIDTH 78 +#define OM_RESET_RESTORE_WIDTH 84 #define OM_RESET_RESTORE_HEIGHT 15 const unsigned char OM_RESET_RESTORE [] PROGMEM = { 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, -0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3e,0x0, -0x0,0x0,0xf0,0x1,0x0,0x0,0x0,0x0,0xc2,0x0,0x0,0x40,0x14,0x6,0x0,0x4, -0x0,0x0,0x82,0x0,0x0,0x40,0x14,0x4,0x0,0x4,0x0,0x0,0xc2,0x3c,0x9e,0xc7, -0x10,0xe6,0xf1,0xec,0x71,0xf,0x7e,0x44,0x82,0x48,0xf2,0x23,0x12,0x24,0x12,0x11, -0x42,0x7c,0x8e,0x4f,0x12,0xe2,0x73,0x34,0x12,0x1f,0x82,0x4,0x90,0x40,0x10,0x24, -0x80,0x24,0x12,0x1,0x82,0x44,0x92,0x48,0x11,0x24,0x92,0x24,0x12,0x11,0x82,0x3c, -0x9e,0xc7,0x11,0xe4,0xf1,0xec,0x11,0xf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, -0x0,0x0,0x0,0x0,0x0,0x0, +0x0,0x3e,0x0,0x0,0x0,0x0,0x7c,0x0,0x0,0x0,0x0,0x0,0xc2,0x0,0x0,0x40, +0x20,0x84,0x1,0x0,0x1,0x0,0x0,0x82,0x0,0x0,0x40,0x20,0x4,0x1,0x0,0x1, +0x0,0x0,0xc2,0x3c,0x9e,0xc7,0x0,0x84,0x79,0x3c,0x7b,0xdc,0x3,0x7e,0x44,0x82, +0x48,0x10,0xfc,0x88,0x4,0x89,0x44,0x4,0x42,0x7c,0x8e,0x4f,0x10,0x84,0xf8,0x1c, +0x8d,0xc4,0x7,0x82,0x4,0x90,0x40,0x0,0x4,0x9,0x20,0x89,0x44,0x0,0x82,0x44, +0x92,0x48,0x8,0x4,0x89,0x24,0x89,0x44,0x4,0x82,0x3c,0x9e,0xc7,0x8,0x4,0x79, +0x3c,0x7b,0xc4,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, +0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, +0x0,0x0,0x0,0x0,0x0, }; -#define OM_RESET_GBS_WIDTH 60 +#define OM_RESET_GBS_WIDTH 78 #define OM_RESET_GBS_HEIGHT 15 const unsigned char OM_RESET_GBS [] PROGMEM = { 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, -0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3e,0x0,0x0,0x0,0xc0,0xe3,0xc3,0x3, -0xc2,0x0,0x0,0x40,0x20,0x26,0x66,0x6,0x82,0x0,0x0,0x40,0x10,0x24,0x24,0x4, -0xc2,0x3c,0x9e,0xc7,0x10,0x20,0x64,0x0,0x7e,0x44,0x82,0x48,0x10,0xe7,0xc3,0x3, -0x42,0x7c,0x8e,0x4f,0x10,0x2c,0x4,0x6,0x82,0x4,0x90,0x40,0x10,0x2c,0x24,0x4, -0x82,0x44,0x92,0x48,0x30,0x2c,0x24,0x4,0x82,0x3c,0x9e,0xc7,0xe0,0xeb,0xc3,0x7, +0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3e,0x0, +0x0,0x0,0x0,0x80,0x0,0x80,0x0,0x0,0xc2,0x0,0x80,0x0,0x20,0x80,0x0,0x0, +0x0,0x0,0x82,0x0,0x80,0x0,0x20,0x80,0x0,0x0,0x0,0x0,0xc2,0x3c,0x9e,0x2d, +0x6e,0xf8,0x3c,0xb3,0x3c,0xf,0x7e,0x44,0x82,0x40,0x22,0x88,0x44,0x92,0x4,0x11, +0x42,0x7c,0x8e,0x70,0x22,0x84,0x7c,0x92,0x2,0x1f,0x82,0x4,0x90,0x44,0x22,0x84, +0x4,0x8a,0x2,0x1,0x82,0x44,0x92,0x64,0x22,0x88,0x44,0x8c,0x64,0x11,0x82,0x3c, +0x9e,0x7d,0x62,0xf8,0x3c,0x8c,0x3c,0xf,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, -0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, +0x0,0x0,0x0,0x0,0x0,0x0, }; diff --git a/src/main.cpp b/src/main.cpp index 816f54f..eceba4b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3,7 +3,7 @@ # File: main.cpp # # File Created: Friday, 19th April 2024 3:13:38 pm # # Author: Robert Neumann # -# Last Modified: Friday, 7th June 2024 4:52:58 pm # +# Last Modified: Monday, 10th June 2024 4:04:57 pm # # Modified By: Sergey Ko # # # # License: GPLv3 # @@ -169,6 +169,9 @@ void setup() // dev rto->invertSync = false; rto->debugView = false; + rto->developerMode = false; + rto->freezeCapture = false; + rto->adcFilter = false; adco->r_gain = 0; adco->g_gain = 0; @@ -183,7 +186,7 @@ void setup() if (rto->webServerEnabled) { wifiInit(); - webSocket.begin(); + serverWebSocketInit(); serverInit(); #ifdef HAVE_PINGER_LIBRARY @@ -220,7 +223,7 @@ void setup() while (millis() - initDelay < 1500) { display.drawXbm(2, 2, gbsicon_width, gbsicon_height, gbsicon_bits); display.display(); - wifiLoop(0); + // wifiLoop(0); delay(1); } display.clear(); @@ -271,8 +274,8 @@ void setup() // ? WHY? initDelay = millis(); while (millis() - initDelay < 1000) { - wifiLoop(0); - delay(1); + // wifiLoop(0); + delay(10); } // dummy commands @@ -302,9 +305,8 @@ void setup() // restart and dummy startWire(); - delay(1); + delay(10); GBS::STATUS_00::read(); - delay(1); if (!checkBoardPower()) { stopWire(); @@ -331,6 +333,7 @@ void setup() } zeroAll(); + _DBGN(F("(!) reset runtime parameters while setup")); setResetParameters(); prepareSyncProcessor(); @@ -345,8 +348,6 @@ void setup() // FIXME double reset? // setResetParameters(); - // wifiLoop(1); - // startup reliability test routine /*rto->videoStandardInput = 1; writeProgramArrayNew(ntsc_240p, 0); @@ -387,11 +388,7 @@ void loop() static unsigned long lastTimeInterruptClear = millis(); menuLoop(); - wifiLoop(0); // WiFi + OTA + WS + MDNS, checks for server enabled + started - // Serial takes precedence - handleSerialCommand(); - // handle user commands - handleUserCommand(); + wifiLoop(); // WiFi + OTA + WS + MDNS, checks for server enabled + started // run FrameTimeLock if enabled if (uopt->enableFrameTimeLock && rto->sourceDisconnected == false @@ -636,8 +633,17 @@ void loop() } #endif // HAVE_PINGER_LIBRARY + // Serial takes precedence + handleSerialCommand(); + // skip the code below if we don't hava the web server + if (!rto->webServerEnabled || !rto->webServerStarted) + return; + // handle user commands + handleUserCommand(); // web client handler server.handleClient(); + MDNS.update(); + dnsServer.processNextRequest(); // websocket event handler webSocket.loop(); // handle ArduinoIDE diff --git a/src/options.h b/src/options.h index d7931c4..8640593 100644 --- a/src/options.h +++ b/src/options.h @@ -18,6 +18,13 @@ #define MENU_WIDTH 131 #define MENU_HEIGHT 19 #define AUTO_GAIN_INIT 0x48 +#define SCANLINE_STRENGTH_INIT 0x30 +#define WEBSOCK_HBEAT_INTVAL 1500UL +#define WEBSOCK_HBEAT_PONG_TOUT 1500UL +#define WEBSOCK_HBEAT_DISCONN_CNT 5 +#define WEBSOCK_HBEAT_DEV_INTVAL 500UL +#define WEBSOCK_HBEAT_DEV_PONG_TOUT 1000UL +#define WEBSOCK_HBEAT_DEV_DISCONN_CNT 3 #define THIS_DEVICE_MASTER #ifdef HAVE_BUTTONS #define INPUT_SHIFT 0 @@ -26,6 +33,9 @@ #define MENU_SHIFT 3 #define BACK_SHIFT 4 #endif // HAVE_BUTTONS +#if !defined(SERIAL_BUFFER_MAX_LEN) +#define SERIAL_BUFFER_MAX_LEN 512UL // use a number aligned with 4 +#endif // SERIAL_BUFFER_MAX_LEN #if !defined(DISPLAY_SDA) // SDA = D2 (Lolin), D14 (Wemos D1) // ESP8266 Arduino default map: SDA #define DISPLAY_SDA D2 @@ -152,7 +162,7 @@ struct userOptions uint8_t preferScalingRgbhv; uint8_t PalForce60; uint8_t disableExternalClockGenerator; - uint8_t matchPresetSource; + // uint8_t matchPresetSource; uint8_t wantStepResponse; uint8_t wantFullHeight; uint8_t enableCalibrationADC; @@ -227,6 +237,9 @@ struct runTimeOptions // dev bool invertSync; bool debugView; + bool developerMode; + bool freezeCapture; + bool adcFilter; }; // remember adc options across presets struct adcOptions diff --git a/src/presets.cpp b/src/presets.cpp index 2426d1f..632b261 100644 --- a/src/presets.cpp +++ b/src/presets.cpp @@ -3,7 +3,7 @@ # File: preset.cpp # # File Created: Thursday, 2nd May 2024 6:38:23 pm # # Author: # -# Last Modified: Friday, 7th June 2024 4:52:58 pm # +# Last Modified: Monday, 10th June 2024 4:04:57 pm # # Modified By: Sergey Ko # ##################################################################################### # CHANGELOG: # @@ -67,7 +67,6 @@ void loadPresetMdSection() */ void setResetParameters() { - _WSN(F("(!) runtime data reset")); // TODO: do we reset resolution here? rto->videoStandardInput = 0; rto->videoIsFrozen = false; @@ -94,9 +93,10 @@ void setResetParameters() rto->useHdmiSyncFix = 0; rto->notRecognizedCounter = 0; - // DEV - rto->invertSync = false; - rto->debugView = false; + // this may not be reset here + // rto->invertSync = false; + // rto->debugView = false; + // rto->developerMode = false; adco->r_gain = 0; adco->g_gain = 0; @@ -1075,13 +1075,13 @@ void doPostPresetLoadSteps() while ((!getStatus16SpHsStable()) && (millis() - timeout < 2002)) { delay(4); - wifiLoop(0); + // wifiLoop(0); updateSpDynamic(0); } while ((getVideoMode() == 0) && (millis() - timeout < 1505)) { delay(4); - wifiLoop(0); + // wifiLoop(0); updateSpDynamic(0); } @@ -1236,7 +1236,7 @@ void doPostPresetLoadSteps() // if (rto->presetID == 0x05 || rto->presetID == 0x15) { if (uopt->resolutionID == Output1080p || uopt->resolutionID == Output1080p50) { - _WS(F("(set your TV aspect ratio to 16:9!)")); + _WS(F("(set your TV aspect ratio to 16:9)")); } if (rto->videoStandardInput == 14) { @@ -1345,8 +1345,8 @@ void applyPresets(uint8_t videoMode) // inputAndSyncDetect() -> goLowPowerWithInputDetection() will // call setResetParameters() again. But if we don't call // setResetParameters() here, the second call will never happen. + _DBGN(F("(!) reset runtime parameters while applying preset")); setResetParameters(); - _DBGN(F("reset runtime parameters while applying preset")); // Deselect the output resolution in the web UI. We cannot call // doPostPresetLoadSteps() to select the right resolution, since @@ -1489,7 +1489,6 @@ void applyPresets(uint8_t videoMode) { const uint8_t *preset = loadPresetFromLFS(videoMode); writeProgramArrayNew(preset, false); - _WSF(PSTR("%d preset is now active\n"), preset); if (applySavedBypassPreset()) { _DBGN(F("Bypass preset applied")); @@ -1499,16 +1498,17 @@ void applyPresets(uint8_t videoMode) // } else if (uopt->presetPreference == 4) { else if (uopt->resolutionID == Output1024p) { - if (uopt->matchPresetSource && (videoMode != 8) && (GBS::GBS_OPTION_SCALING_RGBHV::read() == 0)) - { - writeProgramArrayNew(ntsc_240p, false); // pref = x1024 override to x960 - _WSN(F("ntsc_240p is now active")); - } - else - { + // comments are misleading, the functionality is unknown + // if (uopt->matchPresetSource && (videoMode != 8) && (GBS::GBS_OPTION_SCALING_RGBHV::read() == 0)) + // { + // writeProgramArrayNew(ntsc_240p, false); // pref = x1024 override to x960 + // _WSN(F("ntsc_240p is now active")); + // } + // else + // { writeProgramArrayNew(ntsc_1280x1024, false); _WSN(F("ntsc_1280x1024 is now active")); - } + // } } #endif // defined(ESP8266) @@ -1532,16 +1532,16 @@ void applyPresets(uint8_t videoMode) // if (uopt->presetPreference == 0) { if (uopt->resolutionID == Output240p) { - if (uopt->matchPresetSource) - { - writeProgramArrayNew(pal_1280x1024, false); // pref = x960 override to x1024 - _WSN(F("pal_1280x1024 is now active")); - } - else - { + // if (uopt->matchPresetSource) + // { + // writeProgramArrayNew(pal_1280x1024, false); // pref = x960 override to x1024 + // _WSN(F("pal_1280x1024 is now active")); + // } + // else + // { writeProgramArrayNew(pal_240p, false); _WSN(F("pal_240p is now active")); - } + // } // } else if (uopt->presetPreference == 1) { } else if (uopt->resolutionID == Output576p50) @@ -1566,7 +1566,6 @@ void applyPresets(uint8_t videoMode) { const uint8_t *preset = loadPresetFromLFS(videoMode); writeProgramArrayNew(preset, false); - _WSF(PSTR("%d preset is now active\n"), preset); if (applySavedBypassPreset()) { _DBGN(F("Bypass preset applied")); @@ -1718,7 +1717,7 @@ const uint8_t *loadPresetFromLFS(byte forVideoMode) f = LittleFS.open(buffer, "r"); if (!f) { - _WSN(F("no preset file for this slot and source")); + _WSN(F("no preset file for this slot and source, using 240p")); if (forVideoMode == 2 || forVideoMode == 4) return pal_240p; else @@ -1730,7 +1729,7 @@ const uint8_t *loadPresetFromLFS(byte forVideoMode) s = f.readStringUntil('}'); f.close(); } - +// FIXME char *tmp; uint16_t i = 0; tmp = strtok(&s[0], ","); diff --git a/src/slot.cpp b/src/slot.cpp index c246546..7c1b9d2 100644 --- a/src/slot.cpp +++ b/src/slot.cpp @@ -3,7 +3,7 @@ # File: slot.cpp # # File Created: Friday, 31st May 2024 8:41:15 am # # Author: Sergey Ko # -# Last Modified: Sunday, 2nd June 2024 10:57:25 pm # +# Last Modified: Monday, 10th June 2024 1:59:11 pm # # Modified By: Sergey Ko # ########################################################################### # CHANGELOG: # @@ -40,7 +40,7 @@ bool slotLoad(const uint8_t & slotIndex) { uopt->deintMode = slotsObject.slot[slotIndex].deintMode; uopt->wantTap6 = slotsObject.slot[slotIndex].wantTap6; uopt->wantFullHeight = slotsObject.slot[slotIndex].wantFullHeight; - uopt->matchPresetSource = slotsObject.slot[slotIndex].matchPresetSource; + // uopt->matchPresetSource = slotsObject.slot[slotIndex].matchPresetSource; uopt->PalForce60 = slotsObject.slot[slotIndex].PalForce60; return true; } @@ -78,7 +78,7 @@ void slotUpdate(SlotMetaArray & slotsObject, const uint8_t & slotIndex, String * slotsObject.slot[slotIndex].deintMode = uopt->deintMode; slotsObject.slot[slotIndex].wantTap6 = uopt->wantTap6; slotsObject.slot[slotIndex].wantFullHeight = uopt->wantFullHeight; - slotsObject.slot[slotIndex].matchPresetSource = uopt->matchPresetSource; + // slotsObject.slot[slotIndex].matchPresetSource = uopt->matchPresetSource; slotsObject.slot[slotIndex].PalForce60 = uopt->PalForce60; } @@ -118,7 +118,7 @@ int8_t slotGetData(SlotMetaArray & slotsObject) { slotsObject.slot[i].deintMode = 0; slotsObject.slot[i].wantTap6 = 0; slotsObject.slot[i].wantFullHeight = 0; - slotsObject.slot[i].matchPresetSource = 0; + // slotsObject.slot[i].matchPresetSource = 0; slotsObject.slot[i].PalForce60 = 0; strncpy(slotsObject.slot[i].name, slot_name.c_str(), @@ -174,7 +174,7 @@ bool slotFlush() { if(!slotSetData(slotObject)) return false; - _DBGF(PSTR("slot %d updated\n"), uopt->slotID); + _DBGF(PSTR("slotID: %d has been updated\n"), uopt->slotID); return true; } @@ -195,7 +195,7 @@ bool slotResetFlush(const uint8_t & slotIndex) { if(!slotSetData(slotObject)) return false; - _DBGF(PSTR("slot %d reset success\n"), slotIndex); + _DBGF(PSTR("slotID: %d reset success\n"), slotIndex); return true; } @@ -220,7 +220,7 @@ void slotResetDefaults(SlotMetaArray & slotsObject, const uint8_t & slotIndex) { slotsObject.slot[slotIndex].deintMode = 0; slotsObject.slot[slotIndex].wantTap6 = 0; slotsObject.slot[slotIndex].wantFullHeight = 0; - slotsObject.slot[slotIndex].matchPresetSource = 0; + // slotsObject.slot[slotIndex].matchPresetSource = 0; slotsObject.slot[slotIndex].PalForce60 = 0; strncpy( slotsObject.slot[slotIndex].name, @@ -232,7 +232,7 @@ void slotResetDefaults(SlotMetaArray & slotsObject, const uint8_t & slotIndex) { if(slotIndex == uopt->slotID) { uopt->resolutionID = Output240p; uopt->wantScanlines = 0; - uopt->scanlineStrength = 0x30; + uopt->scanlineStrength = SCANLINE_STRENGTH_INIT; uopt->wantVdsLineFilter = 0; uopt->wantStepResponse = 1; uopt->wantPeaking = 1; @@ -242,7 +242,7 @@ void slotResetDefaults(SlotMetaArray & slotsObject, const uint8_t & slotIndex) { uopt->deintMode = 0; uopt->wantTap6 = 1; uopt->wantFullHeight = 1; - uopt->matchPresetSource = 1; + // uopt->matchPresetSource = 1; uopt->PalForce60 = 0; // uopt->wantOutputComponent = 0; diff --git a/src/slot.h b/src/slot.h index 0bf4ed6..4ef109b 100644 --- a/src/slot.h +++ b/src/slot.h @@ -25,7 +25,7 @@ typedef struct uint8_t deintMode; uint8_t wantTap6; uint8_t wantFullHeight; - uint8_t matchPresetSource; + // uint8_t matchPresetSource; uint8_t PalForce60; } SlotMeta; diff --git a/src/video.cpp b/src/video.cpp index 165fc49..edcff64 100644 --- a/src/video.cpp +++ b/src/video.cpp @@ -3,7 +3,7 @@ # File: video.cpp # # File Created: Thursday, 2nd May 2024 4:07:57 pm # # Author: # -# Last Modified: Friday, 7th June 2024 4:57:34 pm # +# Last Modified: Monday, 10th June 2024 4:04:57 pm # # Modified By: Sergey Ko # ##################################################################################### # CHANGELOG: # @@ -457,8 +457,7 @@ void externalClockGenResetClock() GBS::PAD_CKIN_ENZ::write(0); // 0 = clock input enable (pin40) FrameSync::clearFrequency(); - _DBG(F("clock gen reset: ")); - _DBGN(rto->freqExtClockGen); + _DBGF(PSTR("reset ext. clock gen. to: %ld\n"), rto->freqExtClockGen); } /** @@ -1935,7 +1934,7 @@ void runAutoGain() for (uint8_t i = 0; i < loopCeiling; i++) { if (i % 20 == 0) { - wifiLoop(0); + // wifiLoop(0); limit_found = 0; } greenValue = GBS::TEST_BUS_2F::read(); @@ -2586,8 +2585,8 @@ void goLowPowerWithInputDetection() GBS::OUT_SYNC_CNTRL::write(0); // no H / V sync out to PAD GBS::DAC_RGBS_PWDNZ::write(0); // direct disableDAC() // zeroAll(); + _DBGN(F("(!) reset runtime parameters while going LowPower")); setResetParameters(); // includes rto->videoStandardInput = 0 - _DBGN(F("reset runtime parameters while going LowPower")); prepareSyncProcessor(); delay(100); rto->isInLowPowerMode = true; @@ -4178,7 +4177,7 @@ void runSyncWatcher() rto->noSyncCounter = 0x07fe; // will cause a return break; } - wifiLoop(0); + // wifiLoop(0); delay(1); } @@ -4832,7 +4831,7 @@ void runSyncWatcher() if (RGBHVNoSyncCounter > limitNoSync) { RGBHVNoSyncCounter = 0; - _DBGN(F("reset runtime parameters while running syncWatcher")); + _DBGN(F("(!) reset runtime parameters while running syncWatcher")); setResetParameters(); prepareSyncProcessor(); resetSyncProcessor(); // todo: fix MD being stuck in last mode when sync disappears @@ -5025,7 +5024,7 @@ uint8_t detectAndSwitchToActiveInput() unsigned long timeout = millis(); while (millis() - timeout < 450) { delay(10); - wifiLoop(0); + // wifiLoop(0); bool stable = getStatus16SpHsStable(); if (stable) { @@ -5047,7 +5046,7 @@ uint8_t detectAndSwitchToActiveInput() // 360ms good up to 5_34 SP_V_TIMER_VAL = 0x0b while (!vsyncActive && ((millis() - timeOutStart) < 360)) { vsyncActive = GBS::STATUS_SYNC_PROC_VSACT::read(); - wifiLoop(0); // wifi stack + // wifiLoop(0); // wifi stack delay(1); } @@ -5060,7 +5059,7 @@ uint8_t detectAndSwitchToActiveInput() timeOutStart = millis(); while (!hsyncActive && millis() - timeOutStart < 400) { hsyncActive = GBS::STATUS_SYNC_PROC_HSACT::read(); - wifiLoop(0); // wifi stack + // wifiLoop(0); // wifi stack delay(1); } diff --git a/src/wifiman.cpp b/src/wifiman.cpp index 041c006..96979ed 100644 --- a/src/wifiman.cpp +++ b/src/wifiman.cpp @@ -3,7 +3,7 @@ # File: wifiman.cpp # # File Created: Friday, 19th April 2024 2:25:33 pm # # Author: Sergey Ko # -# Last Modified: Friday, 7th June 2024 4:52:58 pm # +# Last Modified: Saturday, 8th June 2024 4:19:54 pm # # Modified By: Sergey Ko # ##################################################################################### # CHANGELOG: # @@ -13,7 +13,7 @@ #include "wifiman.h" static unsigned long _connectCheckTime = 0; -static unsigned long _lastTimePing = 0; +// static unsigned long _lastTimePing = 0; #ifdef THIS_DEVICE_MASTER @@ -86,82 +86,82 @@ static void wifiEventHandler(System_Event_t *e) * which is in webUI (see: createWebSocket()) * */ -void updateWebSocketData() -{ - // assert free heap - if (ESP.getFreeHeap() > 14000) { - webSocket.disconnect(); - } +// void updateWebSocketData() +// { +// // assert free heap +// // if (ESP.getFreeHeap() > 14000) { +// // webSocket.disconnect(); +// // } - if (rto->webServerEnabled && rto->webServerStarted) { - if (webSocket.connectedClients() > 0) { - constexpr size_t MESSAGE_LEN = 8; - uint8_t toSend[MESSAGE_LEN]; - memset(toSend, 0, MESSAGE_LEN); - // special character # used for message filtering in WebUI - toSend[0] = '#'; - toSend[1] = uopt->slotID + '0'; - // TODO: resolutionID must be INTEGER too? - toSend[2] = (char)uopt->resolutionID; - // - if (uopt->wantScanlines) - toSend[3] |= (1 << 0); - if (uopt->wantVdsLineFilter) - toSend[3] |= (1 << 1); - if (uopt->wantStepResponse) - toSend[3] |= (1 << 2); - if (uopt->wantPeaking) - toSend[3] |= (1 << 3); - if (uopt->enableAutoGain) - toSend[3] |= (1 << 4); - if (uopt->enableFrameTimeLock) - toSend[3] |= (1 << 5); +// if (rto->webServerEnabled && rto->webServerStarted) { +// if (webSocket.connectedClients() > 0) { +// constexpr size_t MESSAGE_LEN = 8; +// uint8_t toSend[MESSAGE_LEN]; +// memset(toSend, 0, MESSAGE_LEN); +// // special character # used for message filtering in WebUI +// toSend[0] = '#'; +// toSend[1] = uopt->slotID + '0'; +// // TODO: resolutionID must be INTEGER too? +// toSend[2] = (char)uopt->resolutionID; +// // +// if (uopt->wantScanlines) +// toSend[3] |= (1 << 0); +// if (uopt->wantVdsLineFilter) +// toSend[3] |= (1 << 1); +// if (uopt->wantStepResponse) +// toSend[3] |= (1 << 2); +// if (uopt->wantPeaking) +// toSend[3] |= (1 << 3); +// if (uopt->enableAutoGain) +// toSend[3] |= (1 << 4); +// if (uopt->enableFrameTimeLock) +// toSend[3] |= (1 << 5); - // - if (uopt->deintMode == 0) // motion adaptive if == 0 - toSend[4] |= (1 << 0); - if (uopt->deintMode == 1) // bob if == 1 - toSend[4] |= (1 << 1); - // if (uopt->wantTap6) { - // toSend[4] |= (1 << 1); - // } - if (uopt->wantFullHeight) - toSend[4] |= (1 << 2); - if (uopt->matchPresetSource) - toSend[4] |= (1 << 3); - if (uopt->PalForce60 == 1) - toSend[4] |= (1 << 4); +// // +// if (uopt->deintMode == 0) // motion adaptive if == 0 +// toSend[4] |= (1 << 0); +// if (uopt->deintMode == 1) // bob if == 1 +// toSend[4] |= (1 << 1); +// // if (uopt->wantTap6) { +// // toSend[4] |= (1 << 1); +// // } +// if (uopt->wantFullHeight) +// toSend[4] |= (1 << 2); +// // if (uopt->matchPresetSource) +// // toSend[4] |= (1 << 3); +// if (uopt->PalForce60 == 1) +// toSend[4] |= (1 << 3); - // system preferences - if (uopt->wantOutputComponent) - toSend[5] |= (1 << 0); - if (uopt->enableCalibrationADC) - toSend[5] |= (1 << 1); - if (uopt->preferScalingRgbhv) - toSend[5] |= (1 << 2); - if (uopt->disableExternalClockGenerator) - toSend[5] |= (1 << 3); +// // system preferences +// if (uopt->wantOutputComponent) +// toSend[5] |= (1 << 0); +// if (uopt->enableCalibrationADC) +// toSend[5] |= (1 << 1); +// if (uopt->preferScalingRgbhv) +// toSend[5] |= (1 << 2); +// if (uopt->disableExternalClockGenerator) +// toSend[5] |= (1 << 3); - // developer panel controls status - if(rto->printInfos) - toSend[6] |= (1 << 0); - if(rto->invertSync) - toSend[6] |= (1 << 1); - if(rto->osr != 0) - toSend[6] |= (1 << 2); - if(GBS::ADC_FLTR::read() != 0) - toSend[6] |= (1 << 3); - if(rto->debugView) - toSend[6] |= (1 << 4); +// // developer panel controls status +// if(rto->printInfos) +// toSend[6] |= (1 << 0); +// if(rto->invertSync) +// toSend[6] |= (1 << 1); +// if(rto->osr != 0) +// toSend[6] |= (1 << 2); +// if(GBS::ADC_FLTR::read() != 0) +// toSend[6] |= (1 << 3); +// if(rto->debugView) +// toSend[6] |= (1 << 4); - // system tab controls - if(rto->allowUpdatesOTA) - toSend[7] |= (1 << 0); +// // system tab controls +// if(rto->allowUpdatesOTA) +// toSend[7] |= (1 << 0); - webSocket.broadcastBIN(toSend, MESSAGE_LEN); - } - } -} +// webSocket.broadcastBIN(toSend, MESSAGE_LEN); +// } +// } +// } /** * @brief @@ -270,7 +270,7 @@ bool wifiStartWPS() { * @brief Put this method inside of main loop * */ -void wifiLoop(bool instant) { +void wifiLoop() { if(WiFi.status() != WL_CONNECTED && _connectCheckTime != 0 && millis() > (_connectCheckTime + 10000UL)) { @@ -285,20 +285,19 @@ void wifiLoop(bool instant) { _connectCheckTime = 0; } } - if (rto->webServerEnabled && rto->webServerStarted) { - MDNS.update(); - dnsServer.processNextRequest(); - - if ((millis() - _lastTimePing) > 953) { // slightly odd value so not everything happens at once - webSocket.broadcastPing(); - } - if (((millis() - _lastTimePing) > 973) || instant) { - if ((webSocket.connectedClients(false) > 0) || instant) { // true = with compliant ping - updateWebSocketData(); - } - _lastTimePing = millis(); - } - } + // if (rto->webServerEnabled && rto->webServerStarted) { + // MDNS.update(); + // dnsServer.processNextRequest(); + // if ((millis() - _lastTimePing) > 953) { // slightly odd value so not everything happens at once + // webSocket.broadcastPing(); + // } + // if (((millis() - _lastTimePing) > 973) || instant) { + // if ((webSocket.connectedClients(false) > 0) || instant) { // true = with compliant ping + // updateWebSocketData(); + // } + // _lastTimePing = millis(); + // } + // } } /** diff --git a/src/wifiman.h b/src/wifiman.h index c3dc254..e421d81 100644 --- a/src/wifiman.h +++ b/src/wifiman.h @@ -3,7 +3,7 @@ # File: wifiman.h # # File Created: Friday, 19th April 2024 2:25:42 pm # # Author: Sergey Ko # -# Last Modified: Tuesday, 28th May 2024 4:42:17 pm # +# Last Modified: Saturday, 8th June 2024 4:13:45 pm # # Modified By: Sergey Ko # ##################################################################################### # CHANGELOG: # @@ -38,13 +38,13 @@ extern struct runTimeOptions *rto; // THE OBJECTIVE: Do not rely on build-in autoreconnect routine, // use the custom one instead. -void updateWebSocketData(); +// void updateWebSocketData(); void wifiInit(); void wifiDisable(); bool wifiStartStaMode(const String & ssid, const String & pass = ""); bool wifiStartApMode(); bool wifiStartWPS(); -void wifiLoop(bool instant); +void wifiLoop(); const String wifiGetApSSID(); const String wifiGetApPASS(); int8_t wifiGetRSSI(); diff --git a/src/wserial.cpp b/src/wserial.cpp index 1380739..ff816f7 100644 --- a/src/wserial.cpp +++ b/src/wserial.cpp @@ -3,7 +3,7 @@ # File: wserial.cpp # # File Created: Friday, 19th April 2024 4:13:35 pm # # Author: Sergey Ko # -# Last Modified: Monday, 27th May 2024 12:23:15 pm # +# Last Modified: Monday, 10th June 2024 12:39:38 pm # # Modified By: Sergey Ko # ########################################################################### # CHANGELOG: # @@ -12,6 +12,25 @@ #include "wserial.h" +static char * serialBuffer = nullptr; + +/** + * @brief If developer mode enabled serialBuffer will be created. + * If developer mode disabled serialBuffer will be destroyed. + */ +void serialDevModeToggle() { + if(rto->developerMode) { + if(serialBuffer == nullptr) { + serialBuffer = (char *)malloc(SERIAL_BUFFER_MAX_LEN); + serialBufferClean(); + } + return; + } + // destroy buffer if not the developer mode + free(serialBuffer); + serialBuffer = nullptr; +} + /** * @brief * @@ -24,3 +43,44 @@ void discardSerialRxData() maxThrowAway--; } } + +/** + * @brief Returns serial buffer data. bufferLength (string length) + * may be retrieved by passing bufferLength argumentß + * + * @param bufferLength + * @return const uint8_t* + */ +const char * serialGetBuffer() { + return serialBuffer; +} + +/** + * @brief Empty serial buffer + * + */ +void serialBufferClean() { + memset(serialBuffer, '\0', SERIAL_BUFFER_MAX_LEN); +} + +/** + * @brief adding serial output data to accumulator (serialBuffer) + * + * @param data + * @param size + */ +void SerialMirror::pushToBuffer(void * data, size_t size) { + if(!rto->developerMode) + return; + uint16_t i = 0; + const char * d = reinterpret_cast(data); + size_t buffLen = strlen(serialBuffer); + while((buffLen + i) < SERIAL_BUFFER_MAX_LEN && i < size) { + *(serialBuffer + buffLen + i) = *(d + i); + i++; + } + // do not clog the output, use only for debug + // if(i < size) + // _DBGN(F("(!) serial buffer is full, data truncated...")); +} + diff --git a/src/wserial.h b/src/wserial.h index 8186211..00d1e75 100644 --- a/src/wserial.h +++ b/src/wserial.h @@ -3,7 +3,7 @@ # File: wserial.h # # File Created: Friday, 19th April 2024 4:13:41 pm # # Author: Sergey Ko # -# Last Modified: Friday, 7th June 2024 12:17:54 pm # +# Last Modified: Monday, 10th June 2024 12:14:34 pm # # Modified By: Sergey Ko # ##################################################################################### # CHANGELOG: # @@ -14,10 +14,13 @@ #define _WSERIAL_H_ #include +#include extern char serialCommand; extern char userCommand; extern WebSocketsServer webSocket; +extern runTimeOptions *rto; +extern userOptions *uopt; // TODO: useless ? /* #define LOMEM_SP ((ESP.getFreeHeap() > 10000)) @@ -26,35 +29,42 @@ extern WebSocketsServer webSocket; } while(0) */ void discardSerialRxData(); +void serialDevModeToggle(); +const char * serialGetBuffer(); +void serialBufferClean(); #if defined(ESP8266) // serial mirror class for websocket logs class SerialMirror : public Stream { + void pushToBuffer(void * data, size_t size); + size_t write(const uint8_t *data, size_t size) { - webSocket.broadcastTXT(data, size); + pushToBuffer((void *)data, size); Serial.write(data, size); return size; } size_t write(const char *data, size_t size) { - webSocket.broadcastTXT(data, size); + pushToBuffer((void *)data, size); Serial.write(data, size); return size; } size_t write(uint8_t data) { - webSocket.broadcastTXT(&data, 1); + size_t s = 1; + pushToBuffer(&data, s); Serial.write(data); return 1; } size_t write(char data) { - webSocket.broadcastTXT(&data, 1); + size_t s = 1; + pushToBuffer(&data, s); Serial.write(data); return 1; } diff --git a/src/wserver.cpp b/src/wserver.cpp index 0965dbb..14c2377 100644 --- a/src/wserver.cpp +++ b/src/wserver.cpp @@ -3,7 +3,7 @@ # fs::File: server.cpp # # fs::File Created: Friday, 19th April 2024 3:11:40 pm # # Author: Sergey Ko # -# Last Modified: Friday, 7th June 2024 4:55:29 pm # +# Last Modified: Monday, 10th June 2024 4:21:05 pm # # Modified By: Sergey Ko # ##################################################################################### # CHANGELOG: # @@ -63,6 +63,39 @@ void serverInit() server.begin(); // Webserver for the site } +/** + * @brief If rto->developerMode is true decrease timings on webSocket + * + */ +void serverWebSocketToggleDeveloperMode() { + webSocket.disableHeartbeat(); + delay(10); + if(rto->developerMode) { + webSocket.enableHeartbeat( + WEBSOCK_HBEAT_DEV_INTVAL, + WEBSOCK_HBEAT_DEV_PONG_TOUT, + WEBSOCK_HBEAT_DEV_DISCONN_CNT + ); + return; + } + webSocket.enableHeartbeat( + WEBSOCK_HBEAT_INTVAL, + WEBSOCK_HBEAT_PONG_TOUT, + WEBSOCK_HBEAT_DISCONN_CNT + ); +} + +/** + * @brief Initialize web socket server + * Should be only called once on setup() + * + */ +void serverWebSocketInit() { + webSocket.begin(); + webSocket.onEvent(webSocketEvent); + serverWebSocketToggleDeveloperMode(); +} + /** * @brief * @@ -156,10 +189,10 @@ void serverSlotSet() { uint8_t slotIndex = lowByte(server.arg(String(F("index"))).toInt()); if(!slotLoad(slotIndex)) { - _DBGF(PSTR("unable to read %s\n"), FPSTR(slotsFile)); + _DBGF(PSTR("unable to read: %s\n"), FPSTR(slotsFile)); goto server_slot_set_failure; } - _WSF(PSTR("slot change success: %d\n"), uopt->slotID); + _WSF(PSTR("active slotID: %d\n"), uopt->slotID); server.send(200, mimeAppJson, F("[\"ok\"]")); // begin loading new preset on the next loop userCommand = '3'; @@ -261,7 +294,7 @@ void serverSlotRemove() } // reset parameters setResetParameters(); - _DBGN(F("slot has been removed, parameters now reset to defaults")); + _DBGN(F("(!) slot has been removed, parameters now reset to defaults")); // also reset resolution uopt->resolutionID = Output240p; @@ -904,1705 +937,1715 @@ void handleSerialCommand() serialCommand = ' '; } - if (serialCommand != '@') + if (serialCommand == '@') + return; + + _WSF(commandDescr, + "serial", + serialCommand, + serialCommand, + // uopt->presetPreference, + uopt->slotID, + // rto->presetID + uopt->resolutionID, + uopt->resolutionID); + + // multistage with bad characters? + if (inputStage > 0) + { + // need 's', 't' or 'g' + if (serialCommand != 's' && serialCommand != 't' && serialCommand != 'g') + { + discardSerialRxData(); + _WSN(F(" abort")); + serialCommand = ' '; + } + } + + switch (serialCommand) { - _WSF(commandDescr, - "serial", - serialCommand, - serialCommand, - // uopt->presetPreference, - uopt->slotID, - // rto->presetID - uopt->resolutionID, - uopt->resolutionID); - // multistage with bad characters? - if (inputStage > 0) + case ' ': + // skip spaces + inputStage = segmentCurrent = registerCurrent = 0; // and reset these + break; + case 'd': + { + // don't store scanlines + if (GBS::GBS_OPTION_SCANLINES_ENABLED::read() == 1) { - // need 's', 't' or 'g' - if (serialCommand != 's' && serialCommand != 't' && serialCommand != 'g') - { - discardSerialRxData(); - _WSN(F(" abort")); - serialCommand = ' '; - } + disableScanlines(); } + // pal forced 60hz: no need to revert here. let the voffset function handle it - switch (serialCommand) + if (uopt->enableFrameTimeLock && FrameSync::getSyncLastCorrection() != 0) { - case ' ': - // skip spaces - inputStage = segmentCurrent = registerCurrent = 0; // and reset these - break; - case 'd': + FrameSync::reset(uopt->frameTimeLockMethod); + } + // dump + for (int segment = 0; segment <= 5; segment++) { - // don't store scanlines - if (GBS::GBS_OPTION_SCANLINES_ENABLED::read() == 1) + dumpRegisters(segment); + } + _WSN(F("};")); + } + break; + case '+': + _WSN(F("hor. +")); + shiftHorizontalRight(); + // shiftHorizontalRightIF(4); + break; + case '-': + _WSN(F("hor. -")); + shiftHorizontalLeft(); + // shiftHorizontalLeftIF(4); + break; + case '*': + shiftVerticalUpIF(); + break; + case '/': + shiftVerticalDownIF(); + break; + case 'z': + _WSN(F("scale+")); + scaleHorizontal(2, true); + break; + case 'h': + _WSN(F("scale-")); + scaleHorizontal(2, false); + break; + case 'q': + resetDigital(); + delay(2); + ResetSDRAM(); + delay(2); + togglePhaseAdjustUnits(); + // enableVDS(); + break; + case 'D': // toggle debug mode + _WS(F("debug view: ")); + if (GBS::ADC_UNUSED_62::read() == 0x00) + { + // if (uopt->wantPeaking == 0) { GBS::VDS_PK_Y_H_BYPS::write(0); } // 3_4e 0 // enable peaking but don't store + GBS::VDS_PK_LB_GAIN::write(0x3f); // 3_45 + GBS::VDS_PK_LH_GAIN::write(0x3f); // 3_47 + GBS::ADC_UNUSED_60::write(GBS::VDS_Y_OFST::read()); // backup + GBS::ADC_UNUSED_61::write(GBS::HD_Y_OFFSET::read()); + GBS::ADC_UNUSED_62::write(1); // remember to remove on restore + GBS::VDS_Y_OFST::write(GBS::VDS_Y_OFST::read() + 0x24); + GBS::HD_Y_OFFSET::write(GBS::HD_Y_OFFSET::read() + 0x24); + if (!rto->inputIsYPbPr) + { + // RGB input that has HD_DYN bypassed, use it now + GBS::HD_DYN_BYPS::write(0); + GBS::HD_U_OFFSET::write(GBS::HD_U_OFFSET::read() + 0x24); + GBS::HD_V_OFFSET::write(GBS::HD_V_OFFSET::read() + 0x24); + } + // GBS::IF_IN_DREG_BYPS::write(1); // enhances noise from not delaying IF processing properly + rto->debugView = true; + _WSN(F("on")); + } + else + { + // if (uopt->wantPeaking == 0) { GBS::VDS_PK_Y_H_BYPS::write(1); } // 3_4e 0 + // if (rto->presetID == 0x05) { + if (uopt->resolutionID == Output1080p) { - disableScanlines(); + GBS::VDS_PK_LB_GAIN::write(0x16); // 3_45 + GBS::VDS_PK_LH_GAIN::write(0x0A); // 3_47 } - // pal forced 60hz: no need to revert here. let the voffset function handle it - - if (uopt->enableFrameTimeLock && FrameSync::getSyncLastCorrection() != 0) + else { - FrameSync::reset(uopt->frameTimeLockMethod); + GBS::VDS_PK_LB_GAIN::write(0x16); // 3_45 + GBS::VDS_PK_LH_GAIN::write(0x18); // 3_47 } - // dump - for (int segment = 0; segment <= 5; segment++) + GBS::VDS_Y_OFST::write(GBS::ADC_UNUSED_60::read()); // restore + GBS::HD_Y_OFFSET::write(GBS::ADC_UNUSED_61::read()); + if (!rto->inputIsYPbPr) { - dumpRegisters(segment); + // RGB input, HD_DYN_BYPS again + GBS::HD_DYN_BYPS::write(1); + GBS::HD_U_OFFSET::write(0); // probably just 0 by default + GBS::HD_V_OFFSET::write(0); // probably just 0 by default } - _WSN(F("};")); + // GBS::IF_IN_DREG_BYPS::write(0); + GBS::ADC_UNUSED_60::write(0); // .. and clear + GBS::ADC_UNUSED_61::write(0); + GBS::ADC_UNUSED_62::write(0); + rto->debugView = false; + _WSN(F("off")); } + // serialCommand = '@'; break; - case '+': - _WSN(F("hor. +")); - shiftHorizontalRight(); - // shiftHorizontalRightIF(4); - break; - case '-': - _WSN(F("hor. -")); - shiftHorizontalLeft(); - // shiftHorizontalLeftIF(4); - break; - case '*': - shiftVerticalUpIF(); - break; - case '/': - shiftVerticalDownIF(); - break; - case 'z': - _WSN(F("scale+")); - scaleHorizontal(2, true); - break; - case 'h': - _WSN(F("scale-")); - scaleHorizontal(2, false); - break; - case 'q': - resetDigital(); - delay(2); - ResetSDRAM(); - delay(2); - togglePhaseAdjustUnits(); - // enableVDS(); - break; - case 'D': // toggle debug mode - _WS(F("debug view: ")); - if (GBS::ADC_UNUSED_62::read() == 0x00) - { - // if (uopt->wantPeaking == 0) { GBS::VDS_PK_Y_H_BYPS::write(0); } // 3_4e 0 // enable peaking but don't store - GBS::VDS_PK_LB_GAIN::write(0x3f); // 3_45 - GBS::VDS_PK_LH_GAIN::write(0x3f); // 3_47 - GBS::ADC_UNUSED_60::write(GBS::VDS_Y_OFST::read()); // backup - GBS::ADC_UNUSED_61::write(GBS::HD_Y_OFFSET::read()); - GBS::ADC_UNUSED_62::write(1); // remember to remove on restore - GBS::VDS_Y_OFST::write(GBS::VDS_Y_OFST::read() + 0x24); - GBS::HD_Y_OFFSET::write(GBS::HD_Y_OFFSET::read() + 0x24); - if (!rto->inputIsYPbPr) - { - // RGB input that has HD_DYN bypassed, use it now - GBS::HD_DYN_BYPS::write(0); - GBS::HD_U_OFFSET::write(GBS::HD_U_OFFSET::read() + 0x24); - GBS::HD_V_OFFSET::write(GBS::HD_V_OFFSET::read() + 0x24); - } - // GBS::IF_IN_DREG_BYPS::write(1); // enhances noise from not delaying IF processing properly - rto->debugView = true; - _WSN(F("on")); - } - else - { - // if (uopt->wantPeaking == 0) { GBS::VDS_PK_Y_H_BYPS::write(1); } // 3_4e 0 - // if (rto->presetID == 0x05) { - if (uopt->resolutionID == Output1080p) - { - GBS::VDS_PK_LB_GAIN::write(0x16); // 3_45 - GBS::VDS_PK_LH_GAIN::write(0x0A); // 3_47 - } - else - { - GBS::VDS_PK_LB_GAIN::write(0x16); // 3_45 - GBS::VDS_PK_LH_GAIN::write(0x18); // 3_47 - } - GBS::VDS_Y_OFST::write(GBS::ADC_UNUSED_60::read()); // restore - GBS::HD_Y_OFFSET::write(GBS::ADC_UNUSED_61::read()); - if (!rto->inputIsYPbPr) - { - // RGB input, HD_DYN_BYPS again - GBS::HD_DYN_BYPS::write(1); - GBS::HD_U_OFFSET::write(0); // probably just 0 by default - GBS::HD_V_OFFSET::write(0); // probably just 0 by default - } - // GBS::IF_IN_DREG_BYPS::write(0); - GBS::ADC_UNUSED_60::write(0); // .. and clear - GBS::ADC_UNUSED_61::write(0); - GBS::ADC_UNUSED_62::write(0); - rto->debugView = false; - _WSN(F("off")); + case 'C': + _WSN(F("PLL: ICLK")); + // display clock in last test best at 0x85 + GBS::PLL648_CONTROL_01::write(0x85); + GBS::PLL_CKIS::write(1); // PLL use ICLK (instead of oscillator) + latchPLLAD(); + // GBS::VDS_HSCALE::write(512); + rto->syncLockFailIgnore = 16; + FrameSync::reset(uopt->frameTimeLockMethod); // adjust htotal to new display clock + rto->forceRetime = true; + // applyBestHTotal(FrameSync::init()); // adjust htotal to new display clock + // applyBestHTotal(FrameSync::init()); // twice + // GBS::VDS_FLOCK_EN::write(1); //risky + delay(200); + break; + case 'Y': + writeProgramArrayNew(ntsc_1280x720, false); + doPostPresetLoadSteps(); + break; + case 'y': + writeProgramArrayNew(pal_1280x720, false); + doPostPresetLoadSteps(); + break; + case 'P': + _WS(F("auto deinterlace: ")); + rto->deinterlaceAutoEnabled = !rto->deinterlaceAutoEnabled; + if (rto->deinterlaceAutoEnabled) + { + _WSN(F("on")); + } + else + { + _WSN(F("off")); + } + break; + case 'p': + if (!rto->motionAdaptiveDeinterlaceActive) + { + if (GBS::GBS_OPTION_SCANLINES_ENABLED::read() == 1) + { // don't rely on rto->scanlinesEnabled + disableScanlines(); } - serialCommand = '@'; - break; - case 'C': - _WSN(F("PLL: ICLK")); - // display clock in last test best at 0x85 - GBS::PLL648_CONTROL_01::write(0x85); - GBS::PLL_CKIS::write(1); // PLL use ICLK (instead of oscillator) - latchPLLAD(); - // GBS::VDS_HSCALE::write(512); + enableMotionAdaptDeinterlace(); + } + else + { + disableMotionAdaptDeinterlace(); + } + break; + case 'k': + bypassModeSwitch_RGBHV(); + break; + case 'K': // set outputBypass + setOutModeHdBypass(false); + // uopt->presetPreference = OutputBypass; + uopt->resolutionID = OutputBypass; + _WSN(F("OutputBypass mode is now active")); + slotFlush(); + // saveUserPrefs(); + break; + case 'T': // toggle auto gain + _WS(F("auto gain ")); + if (uopt->enableAutoGain == 0) + { + uopt->enableAutoGain = 1; + setAdcGain(AUTO_GAIN_INIT); + GBS::DEC_TEST_ENABLE::write(1); + _WSN(F("on")); + } + else + { + uopt->enableAutoGain = 0; + GBS::DEC_TEST_ENABLE::write(0); + _WSN(F("off")); + } + slotFlush(); + // saveUserPrefs(); + break; + case 'e': + writeProgramArrayNew(ntsc_240p, false); + doPostPresetLoadSteps(); + break; + case 'r': + writeProgramArrayNew(pal_240p, false); + doPostPresetLoadSteps(); + break; + case '.': // resync HTotal + { + if (!rto->outModeHdBypass) + { + // bestHtotal recalc + rto->autoBestHtotalEnabled = true; rto->syncLockFailIgnore = 16; - FrameSync::reset(uopt->frameTimeLockMethod); // adjust htotal to new display clock rto->forceRetime = true; - // applyBestHTotal(FrameSync::init()); // adjust htotal to new display clock - // applyBestHTotal(FrameSync::init()); // twice - // GBS::VDS_FLOCK_EN::write(1); //risky - delay(200); - break; - case 'Y': - writeProgramArrayNew(ntsc_1280x720, false); - doPostPresetLoadSteps(); - break; - case 'y': - writeProgramArrayNew(pal_1280x720, false); - doPostPresetLoadSteps(); - break; - case 'P': - _WS(F("auto deinterlace: ")); - rto->deinterlaceAutoEnabled = !rto->deinterlaceAutoEnabled; - if (rto->deinterlaceAutoEnabled) - { - _WSN(F("on")); - } - else - { - _WSN(F("off")); - } - break; - case 'p': - if (!rto->motionAdaptiveDeinterlaceActive) + FrameSync::reset(uopt->frameTimeLockMethod); + + if (!rto->syncWatcherEnabled) { - if (GBS::GBS_OPTION_SCANLINES_ENABLED::read() == 1) - { // don't rely on rto->scanlinesEnabled - disableScanlines(); - } - enableMotionAdaptDeinterlace(); + bool autoBestHtotalSuccess = 0; + delay(30); + autoBestHtotalSuccess = runAutoBestHTotal(); + if (!autoBestHtotalSuccess) + { + _WSN(F("HTotal is unchanged")); + } else + _WSN(F("resync HTotal success")); } - else + } + } + break; + case '!': + // fastGetBestHtotal(); + // readEeprom(); + _WSF(PSTR("sfr: %.02f\n pll: %l\n"), getSourceFieldRate(1), getPllRate()); + break; + case '$': + { + // EEPROM write protect pin (7, next to Vcc) is under original MCU control + // MCU drags to Vcc to write, EEPROM drags to Gnd normally + // This routine only works with that "WP" pin disconnected + // 0x17 = input selector? // 0x18 = input selector related? + // 0x54 = coast start 0x55 = coast end + uint16_t writeAddr = 0x54; + const uint8_t eepromAddr = 0x50; + for (; writeAddr < 0x56; writeAddr++) + { + Wire.beginTransmission(eepromAddr); + Wire.write(writeAddr >> 8); // high addr byte, 4 bits + + Wire.write((uint8_t)writeAddr); // mlow addr byte, 8 bits = 12 bits (0xfff max) + Wire.write(0x10); // coast end value ? + Wire.endTransmission(); + delay(5); + } + + // Wire.beginTransmission(eepromAddr); + // Wire.write((uint8_t)0); Wire.write((uint8_t)0); + // Wire.write(0xff); // force eeprom clear probably + // Wire.endTransmission(); + // delay(5); + + _WSN(F("done")); + } + break; + case 'j': + // resetPLL(); + latchPLLAD(); + break; + case 'J': + resetPLLAD(); + break; + case 'v': + rto->phaseSP += 1; + rto->phaseSP &= 0x1f; + _WSF(PSTR("SP: %d\n"), rto->phaseSP); + setAndLatchPhaseSP(); + // setAndLatchPhaseADC(); + break; + case 'b': + advancePhase(); + latchPLLAD(); + _WSF(PSTR("ADC: %d\n"), rto->phaseADC); + break; + case '#': + rto->videoStandardInput = 13; + applyPresets(13); + // _WSN(getStatus00IfHsVsStable()); + // globalDelay++; + // _WSN(globalDelay); + break; + case 'n': + { + uint16_t pll_divider = GBS::PLLAD_MD::read(); + pll_divider += 1; + GBS::PLLAD_MD::write(pll_divider); + GBS::IF_HSYNC_RST::write((pll_divider / 2)); + GBS::IF_LINE_SP::write(((pll_divider / 2) + 1) + 0x40); + _WSF(PSTR("PLL div: 0x%04X %d\n"), pll_divider, pll_divider); + // set IF before latching + // setIfHblankParameters(); + latchPLLAD(); + delay(1); + // applyBestHTotal(GBS::VDS_HSYNC_RST::read()); + updateClampPosition(); + updateCoastPosition(0); + } + break; + case 'N': + { + // if (GBS::RFF_ENABLE::read()) { + // disableMotionAdaptDeinterlace(); + // } + // else { + // enableMotionAdaptDeinterlace(); + // } + + // GBS::RFF_ENABLE::write(!GBS::RFF_ENABLE::read()); + + if (rto->scanlinesEnabled) + { + rto->scanlinesEnabled = false; + disableScanlines(); + } + else + { + rto->scanlinesEnabled = true; + enableScanlines(); + } + } + break; + case 'a': + _WSF(PSTR("HTotal++: %d\n"), GBS::VDS_HSYNC_RST::read() + 1); + if (GBS::VDS_HSYNC_RST::read() < 4095) + { + if (uopt->enableFrameTimeLock) { - disableMotionAdaptDeinterlace(); + // syncLastCorrection != 0 check is included + FrameSync::reset(uopt->frameTimeLockMethod); } - break; - case 'k': - bypassModeSwitch_RGBHV(); - break; - case 'K': // set outputBypass - setOutModeHdBypass(false); - // uopt->presetPreference = OutputBypass; - uopt->resolutionID = OutputBypass; - _WSN(F("OutputBypass mode is now active")); - saveUserPrefs(); - break; - case 'T': - _WS(F("auto gain ")); - if (uopt->enableAutoGain == 0) + rto->forceRetime = 1; + applyBestHTotal(GBS::VDS_HSYNC_RST::read() + 1); + } + break; + case 'A': + _WSF(PSTR("HTotal--: %d\n"), GBS::VDS_HSYNC_RST::read() - 1); + if (GBS::VDS_HSYNC_RST::read() > 0) + { + if (uopt->enableFrameTimeLock) { - uopt->enableAutoGain = 1; - setAdcGain(AUTO_GAIN_INIT); - GBS::DEC_TEST_ENABLE::write(1); - _WSN(F("on")); + // syncLastCorrection != 0 check is included + FrameSync::reset(uopt->frameTimeLockMethod); } - else + rto->forceRetime = 1; + applyBestHTotal(GBS::VDS_HSYNC_RST::read() - 1); + } + break; + case 'M': + { + } + break; + case 'm': + _WS(F("syncwatcher ")); + if (rto->syncWatcherEnabled == true) + { + rto->syncWatcherEnabled = false; + if (rto->videoIsFrozen) { - uopt->enableAutoGain = 0; - GBS::DEC_TEST_ENABLE::write(0); - _WSN(F("off")); + unfreezeVideo(); } - saveUserPrefs(); - break; - case 'e': - writeProgramArrayNew(ntsc_240p, false); - doPostPresetLoadSteps(); + _WSN(F("off")); + } + else + { + rto->syncWatcherEnabled = true; + _WSN(F("on")); + } + break; + case ',': + printVideoTimings(); + break; + case 'i': + rto->printInfos = !rto->printInfos; + break; + case 'c': // enableOTA mode + initUpdateOTA(); + rto->allowUpdatesOTA = true; + _WSN(F("OTA Updates on")); + break; + case 'G': + _WS(F("Debug Pings ")); + if (!rto->enableDebugPings) + { + _WSN(F("on")); + rto->enableDebugPings = 1; + } + else + { + _WSN(F("off")); + rto->enableDebugPings = 0; + } + break; + case 'u': + ResetSDRAM(); + break; + case 'f': // toggle peaking + _WS(F("peaking ")); + if (uopt->wantPeaking == 0) + { + uopt->wantPeaking = 1; + GBS::VDS_PK_Y_H_BYPS::write(0); + _WSN(F("on")); + } + else + { + uopt->wantPeaking = 0; + GBS::VDS_PK_Y_H_BYPS::write(1); + _WSN(F("off")); + } + slotFlush(); + // saveUserPrefs(); + break; + case 'F': // switch ADC filter button (dev.mode) + _WS(F("ADC filter ")); + if (GBS::ADC_FLTR::read() > 0) + { + GBS::ADC_FLTR::write(0); + rto->adcFilter = false; + _WSN(F("off")); + } + else + { + GBS::ADC_FLTR::write(3); + rto->adcFilter = true; + _WSN(F("on")); + } + break; + case 'L': // force outut component + { + // Component / VGA Output + uopt->wantOutputComponent = !uopt->wantOutputComponent; + OutputComponentOrVGA(); + // apply 1280x720 preset now, otherwise a reboot would be required + uint8_t videoMode = getVideoMode(); + if (videoMode == 0) + videoMode = rto->videoStandardInput; + // OutputResolution backup = uopt->resolutionID; + // uopt->presetPreference = Output720P; + // uopt->resolutionID = Output720p; + rto->videoStandardInput = 0; // force hard reset + applyPresets(videoMode); + // uopt->resolutionID = backup; + saveUserPrefs(); + } + break; + case 'l': + _WSN(F("resetSyncProcessor")); + // freezeVideo(); + resetSyncProcessor(); + // delay(10); + // unfreezeVideo(); + break; + // case 'Z': + // { + // uopt->matchPresetSource = !uopt->matchPresetSource; + // // saveUserPrefs(); + // slotFlush(); + // uint8_t vidMode = getVideoMode(); + // // if (uopt->presetPreference == 0 && GBS::GBS_PRESET_ID::read() == 0x11) { + // if (uopt->resolutionID == Output240p && GBS::GBS_PRESET_ID::read() == Output960p50) + // { + // applyPresets(vidMode); + // // } else if (uopt->presetPreference == 4 && GBS::GBS_PRESET_ID::read() == 0x02) { + // } + // else if (uopt->resolutionID == Output1024p && GBS::GBS_PRESET_ID::read() == Output1024p) + // { + // applyPresets(vidMode); + // } + // } + // break; + case 'W': + uopt->enableFrameTimeLock = !uopt->enableFrameTimeLock; + break; + case 'E': + writeProgramArrayNew(ntsc_1280x1024, false); + _WSN(F("ntsc_1280x1024 is now active")); + doPostPresetLoadSteps(); + break; + case 'R': + writeProgramArrayNew(pal_1280x1024, false); + _WSN(F("pal_1280x1024 is now active")); + doPostPresetLoadSteps(); + break; + case '0': + moveHS(4, true); + break; + case '1': + moveHS(4, false); + break; + case '2': + writeProgramArrayNew(pal_768x576, false); // ModeLine "720x576@50" 27 720 732 795 864 576 581 586 625 -hsync -vsync + _WSN(F("pal_768x576 is now active")); + doPostPresetLoadSteps(); + break; + // case '3': + // break; + case '4': + { + // scale vertical + + if (GBS::VDS_VSCALE::read() <= 256) + { + _WSN(F("limit")); break; - case 'r': - writeProgramArrayNew(pal_240p, false); - doPostPresetLoadSteps(); + } + scaleVertical(2, true); + // actually requires full vertical mask + position offset calculation + } + break; + case '5': + { + // scale vertical - + if (GBS::VDS_VSCALE::read() == 1023) + { + _WSN(F("limit")); break; - case '.': // resync HTotal + } + scaleVertical(2, false); + // actually requires full vertical mask + position offset calculation + } + break; + case '6': + if (videoStandardInputIsPalNtscSd() && !rto->outModeHdBypass) { - if (!rto->outModeHdBypass) - { - // bestHtotal recalc - rto->autoBestHtotalEnabled = true; - rto->syncLockFailIgnore = 16; - rto->forceRetime = true; - FrameSync::reset(uopt->frameTimeLockMethod); - - if (!rto->syncWatcherEnabled) + if (GBS::IF_HBIN_SP::read() >= 10) + { // IF_HBIN_SP: min 2 + GBS::IF_HBIN_SP::write(GBS::IF_HBIN_SP::read() - 8); // canvas move right + if ((GBS::IF_HSYNC_RST::read() - 4) > ((GBS::PLLAD_MD::read() >> 1) + 5)) { - bool autoBestHtotalSuccess = 0; - delay(30); - autoBestHtotalSuccess = runAutoBestHTotal(); - if (!autoBestHtotalSuccess) - { - _WSN(F("HTotal is unchanged")); - } else - _WSN(F("resync HTotal success")); + GBS::IF_HSYNC_RST::write(GBS::IF_HSYNC_RST::read() - 4); // shrink 1_0e + GBS::IF_LINE_SP::write(GBS::IF_LINE_SP::read() - 4); // and 1_22 to go with it } } - } - break; - case '!': - // fastGetBestHtotal(); - // readEeprom(); - _WSF(PSTR("sfr: %.02f\n pll: %l\n"), getSourceFieldRate(1), getPllRate()); - break; - case '$': - { - // EEPROM write protect pin (7, next to Vcc) is under original MCU control - // MCU drags to Vcc to write, EEPROM drags to Gnd normally - // This routine only works with that "WP" pin disconnected - // 0x17 = input selector? // 0x18 = input selector related? - // 0x54 = coast start 0x55 = coast end - uint16_t writeAddr = 0x54; - const uint8_t eepromAddr = 0x50; - for (; writeAddr < 0x56; writeAddr++) + else { - Wire.beginTransmission(eepromAddr); - Wire.write(writeAddr >> 8); // high addr byte, 4 bits + - Wire.write((uint8_t)writeAddr); // mlow addr byte, 8 bits = 12 bits (0xfff max) - Wire.write(0x10); // coast end value ? - Wire.endTransmission(); - delay(5); + _WSN(F("limit")); } - - // Wire.beginTransmission(eepromAddr); - // Wire.write((uint8_t)0); Wire.write((uint8_t)0); - // Wire.write(0xff); // force eeprom clear probably - // Wire.endTransmission(); - // delay(5); - - _WSN(F("done")); + } + else if (!rto->outModeHdBypass) + { + if (GBS::IF_HB_SP2::read() >= 4) + GBS::IF_HB_SP2::write(GBS::IF_HB_SP2::read() - 4); // 1_1a + else + GBS::IF_HB_SP2::write(GBS::IF_HSYNC_RST::read() - 0x30); + if (GBS::IF_HB_ST2::read() >= 4) + GBS::IF_HB_ST2::write(GBS::IF_HB_ST2::read() - 4); // 1_18 + else + GBS::IF_HB_ST2::write(GBS::IF_HSYNC_RST::read() - 0x30); + _WSF(PSTR("IF_HB_ST2: 0x%04X IF_HB_SP2: 0x%04X\n"), + GBS::IF_HB_ST2::read(), GBS::IF_HB_SP2::read()); } break; - case 'j': - // resetPLL(); - latchPLLAD(); - break; - case 'J': - resetPLLAD(); - break; - case 'v': - rto->phaseSP += 1; - rto->phaseSP &= 0x1f; - _WSF(PSTR("SP: %d\n"), rto->phaseSP); - setAndLatchPhaseSP(); - // setAndLatchPhaseADC(); - break; - case 'b': - advancePhase(); - latchPLLAD(); - _WSF(PSTR("ADC: %d\n"), rto->phaseADC); - break; - case '#': - rto->videoStandardInput = 13; - applyPresets(13); - // _WSN(getStatus00IfHsVsStable()); - // globalDelay++; - // _WSN(globalDelay); - break; - case 'n': - { - uint16_t pll_divider = GBS::PLLAD_MD::read(); - pll_divider += 1; - GBS::PLLAD_MD::write(pll_divider); - GBS::IF_HSYNC_RST::write((pll_divider / 2)); - GBS::IF_LINE_SP::write(((pll_divider / 2) + 1) + 0x40); - _WSF(PSTR("PLL div: 0x%04X %d\n"), pll_divider, pll_divider); - // set IF before latching - // setIfHblankParameters(); - latchPLLAD(); - delay(1); - // applyBestHTotal(GBS::VDS_HSYNC_RST::read()); - updateClampPosition(); - updateCoastPosition(0); - } - break; - case 'N': - { - // if (GBS::RFF_ENABLE::read()) { - // disableMotionAdaptDeinterlace(); - // } - // else { - // enableMotionAdaptDeinterlace(); - // } - - // GBS::RFF_ENABLE::write(!GBS::RFF_ENABLE::read()); - - if (rto->scanlinesEnabled) - { - rto->scanlinesEnabled = false; - disableScanlines(); + case '7': + if (videoStandardInputIsPalNtscSd() && !rto->outModeHdBypass) + { + if (GBS::IF_HBIN_SP::read() < 0x150) + { // (arbitrary) max limit + GBS::IF_HBIN_SP::write(GBS::IF_HBIN_SP::read() + 8); // canvas move left } else { - rto->scanlinesEnabled = true; - enableScanlines(); + _WSN(F("limit")); } } + else if (!rto->outModeHdBypass) + { + if (GBS::IF_HB_SP2::read() < (GBS::IF_HSYNC_RST::read() - 0x30)) + GBS::IF_HB_SP2::write(GBS::IF_HB_SP2::read() + 4); // 1_1a + else + GBS::IF_HB_SP2::write(0); + if (GBS::IF_HB_ST2::read() < (GBS::IF_HSYNC_RST::read() - 0x30)) + GBS::IF_HB_ST2::write(GBS::IF_HB_ST2::read() + 4); // 1_18 + else + GBS::IF_HB_ST2::write(0); + _WSF(PSTR("IF_HB_ST2: 0x%04X IF_HB_SP2: 0x%04X\n"), + GBS::IF_HB_ST2::read(), GBS::IF_HB_SP2::read()); + } + break; + case '8': // invert sync button (dev. mode) + // _WSN("invert sync"); + rto->invertSync = !rto->invertSync; + invertHS(); + invertVS(); + // optimizePhaseSP(); + break; + case '9': + writeProgramArrayNew(ntsc_720x480, false); + doPostPresetLoadSteps(); break; - case 'a': - _WSF(PSTR("HTotal++: %d\n"), GBS::VDS_HSYNC_RST::read() + 1); - if (GBS::VDS_HSYNC_RST::read() < 4095) + case 'o': + { // oversampring button (dev.mode) + if (rto->osr == 1) + { + setOverSampleRatio(2, false); + } + else if (rto->osr == 2) + { + setOverSampleRatio(4, false); + } + else if (rto->osr == 4) + { + setOverSampleRatio(1, false); + } + delay(4); + optimizePhaseSP(); + _WSF(PSTR("OSR 0x%02X\n"), rto->osr); + rto->phaseIsSet = 0; // do it again in modes applicable + } + break; + case 'g': + inputStage++; + // we have a multibyte command + if (inputStage > 0) + { + if (inputStage == 1) { - if (uopt->enableFrameTimeLock) - { - // syncLastCorrection != 0 check is included - FrameSync::reset(uopt->frameTimeLockMethod); - } - rto->forceRetime = 1; - applyBestHTotal(GBS::VDS_HSYNC_RST::read() + 1); + segmentCurrent = Serial.parseInt(); + _WSF(PSTR("G%d"), segmentCurrent); } - break; - case 'A': - _WSF(PSTR("HTotal--: %d\n"), GBS::VDS_HSYNC_RST::read() - 1); - if (GBS::VDS_HSYNC_RST::read() > 0) + else if (inputStage == 2) { - if (uopt->enableFrameTimeLock) + char szNumbers[3]; + szNumbers[0] = Serial.read(); + szNumbers[1] = Serial.read(); + szNumbers[2] = '\0'; + // char * pEnd; + registerCurrent = strtol(szNumbers, NULL, 16); + _WSF(PSTR("R 0x%04X"), registerCurrent); + if (segmentCurrent <= 5) + { + writeOneByte(0xF0, segmentCurrent); + readFromRegister(registerCurrent, 1, &readout); + _WSF(PSTR(" value: 0x%02X\n"), readout); + } + else { - // syncLastCorrection != 0 check is included - FrameSync::reset(uopt->frameTimeLockMethod); + discardSerialRxData(); + _WSN(F("abort")); } - rto->forceRetime = 1; - applyBestHTotal(GBS::VDS_HSYNC_RST::read() - 1); + inputStage = 0; } - break; - case 'M': - { } break; - case 'm': - _WS(F("syncwatcher ")); - if (rto->syncWatcherEnabled == true) + case 's': + inputStage++; + // we have a multibyte command + if (inputStage > 0) + { + if (inputStage == 1) { - rto->syncWatcherEnabled = false; - if (rto->videoIsFrozen) - { - unfreezeVideo(); - } - _WSN(F("off")); + segmentCurrent = Serial.parseInt(); + _WSF(PSTR("S%d"), segmentCurrent); } - else - { - rto->syncWatcherEnabled = true; - _WSN(F("on")); - } - break; - case ',': - printVideoTimings(); - break; - case 'i': - rto->printInfos = !rto->printInfos; - break; - case 'c': // enableOTA mode - initUpdateOTA(); - rto->allowUpdatesOTA = true; - _WSN(F("OTA Updates on")); - break; - case 'G': - _WS(F("Debug Pings ")); - if (!rto->enableDebugPings) - { - _WSN(F("on")); - rto->enableDebugPings = 1; - } - else + else if (inputStage == 2) { - _WSN(F("off")); - rto->enableDebugPings = 0; + char szNumbers[3]; + for (uint8_t a = 0; a <= 1; a++) + { + // ascii 0x30 to 0x39 for '0' to '9' + if ((Serial.peek() >= '0' && Serial.peek() <= '9') || + (Serial.peek() >= 'a' && Serial.peek() <= 'f') || + (Serial.peek() >= 'A' && Serial.peek() <= 'F')) + { + szNumbers[a] = Serial.read(); + } + else + { + szNumbers[a] = 0; // NUL char + Serial.read(); // but consume the char + } + } + szNumbers[2] = '\0'; + // char * pEnd; + registerCurrent = strtol(szNumbers, NULL, 16); + _WSF(PSTR("R 0x%04X"), registerCurrent); } - break; - case 'u': - ResetSDRAM(); - break; - case 'f': - _WS(F("peaking ")); - if (uopt->wantPeaking == 0) + else if (inputStage == 3) { - uopt->wantPeaking = 1; - GBS::VDS_PK_Y_H_BYPS::write(0); - _WSN(F("on")); + char szNumbers[3]; + for (uint8_t a = 0; a <= 1; a++) + { + if ((Serial.peek() >= '0' && Serial.peek() <= '9') || + (Serial.peek() >= 'a' && Serial.peek() <= 'f') || + (Serial.peek() >= 'A' && Serial.peek() <= 'F')) + { + szNumbers[a] = Serial.read(); + } + else + { + szNumbers[a] = 0; // NUL char + Serial.read(); // but consume the char + } + } + szNumbers[2] = '\0'; + // char * pEnd; + inputToogleBit = strtol(szNumbers, NULL, 16); + if (segmentCurrent <= 5) + { + writeOneByte(0xF0, segmentCurrent); + readFromRegister(registerCurrent, 1, &readout); + _WSF(PSTR(" (was 0x%04X)"), readout); + writeOneByte(registerCurrent, inputToogleBit); + readFromRegister(registerCurrent, 1, &readout); + _WSF(PSTR(" is now: 0x%02X\n"), readout); + } + else + { + discardSerialRxData(); + _WSN(F("abort")); + } + inputStage = 0; } - else + } + break; + case 't': + inputStage++; + // we have a multibyte command + if (inputStage > 0) + { + if (inputStage == 1) { - uopt->wantPeaking = 0; - GBS::VDS_PK_Y_H_BYPS::write(1); - _WSN(F("off")); + segmentCurrent = Serial.parseInt(); + _WSF(PSTR("T%d"), segmentCurrent); } - saveUserPrefs(); - break; - case 'F': // switch ADC filter button (dev.mode) - _WS(F("ADC filter ")); - if (GBS::ADC_FLTR::read() > 0) + else if (inputStage == 2) { - GBS::ADC_FLTR::write(0); - _WSN(F("off")); + char szNumbers[3]; + for (uint8_t a = 0; a <= 1; a++) + { + // ascii 0x30 to 0x39 for '0' to '9' + if ((Serial.peek() >= '0' && Serial.peek() <= '9') || + (Serial.peek() >= 'a' && Serial.peek() <= 'f') || + (Serial.peek() >= 'A' && Serial.peek() <= 'F')) + { + szNumbers[a] = Serial.read(); + } + else + { + szNumbers[a] = 0; // NUL char + Serial.read(); // but consume the char + } + } + szNumbers[2] = '\0'; + // char * pEnd; + registerCurrent = strtol(szNumbers, NULL, 16); + _WSF(PSTR("R 0x%04X"), registerCurrent); } - else + else if (inputStage == 3) { - GBS::ADC_FLTR::write(3); - _WSN(F("on")); + if (Serial.peek() >= '0' && Serial.peek() <= '7') + { + inputToogleBit = Serial.parseInt(); + } + else + { + inputToogleBit = 255; // will get discarded next step + } + _WSF(PSTR(" Bit: %d"), inputToogleBit); + inputStage = 0; + if ((segmentCurrent <= 5) && (inputToogleBit <= 7)) + { + writeOneByte(0xF0, segmentCurrent); + readFromRegister(registerCurrent, 1, &readout); + _WSF(PSTR(" (was 0x%04X)"), readout); + writeOneByte(registerCurrent, readout ^ (1 << inputToogleBit)); + readFromRegister(registerCurrent, 1, &readout); + _WSF(PSTR(" is now: 0x%02X\n"), readout); + } + else + { + discardSerialRxData(); + inputToogleBit = registerCurrent = 0; + _WSN(F("abort")); + } } - break; - case 'L': // force outut component - { - // Component / VGA Output - uopt->wantOutputComponent = !uopt->wantOutputComponent; - OutputComponentOrVGA(); - // apply 1280x720 preset now, otherwise a reboot would be required - uint8_t videoMode = getVideoMode(); - if (videoMode == 0) - videoMode = rto->videoStandardInput; - // OutputResolution backup = uopt->resolutionID; - // uopt->presetPreference = Output720P; - // uopt->resolutionID = Output720p; - rto->videoStandardInput = 0; // force hard reset - applyPresets(videoMode); - // uopt->resolutionID = backup; - saveUserPrefs(); } break; - case 'l': - _WSN(F("resetSyncProcessor")); - // freezeVideo(); - resetSyncProcessor(); - // delay(10); - // unfreezeVideo(); - break; - case 'Z': + case '<': + { + if (segmentCurrent != 255 && registerCurrent != 255) { - uopt->matchPresetSource = !uopt->matchPresetSource; - // saveUserPrefs(); - slotFlush(); - uint8_t vidMode = getVideoMode(); - // if (uopt->presetPreference == 0 && GBS::GBS_PRESET_ID::read() == 0x11) { - if (uopt->resolutionID == Output240p && GBS::GBS_PRESET_ID::read() == Output960p50) - { - applyPresets(vidMode); - // } else if (uopt->presetPreference == 4 && GBS::GBS_PRESET_ID::read() == 0x02) { - } - else if (uopt->resolutionID == Output1024p && GBS::GBS_PRESET_ID::read() == Output1024p) - { - applyPresets(vidMode); - } + writeOneByte(0xF0, segmentCurrent); + readFromRegister(registerCurrent, 1, &readout); + writeOneByte(registerCurrent, readout - 1); // also allow wrapping + _WSF(PSTR("S%d_0x%04X"), segmentCurrent, registerCurrent); + readFromRegister(registerCurrent, 1, &readout); + _WSF(PSTR(" : 0x%04X\n"), readout); } - break; - case 'W': - uopt->enableFrameTimeLock = !uopt->enableFrameTimeLock; - break; - case 'E': - writeProgramArrayNew(ntsc_1280x1024, false); - _WSN(F("ntsc_1280x1024 is now active")); - doPostPresetLoadSteps(); - break; - case 'R': - writeProgramArrayNew(pal_1280x1024, false); - _WSN(F("pal_1280x1024 is now active")); - doPostPresetLoadSteps(); - break; - case '0': - moveHS(4, true); - break; - case '1': - moveHS(4, false); - break; - case '2': - writeProgramArrayNew(pal_768x576, false); // ModeLine "720x576@50" 27 720 732 795 864 576 581 586 625 -hsync -vsync - _WSN(F("pal_768x576 is now active")); - doPostPresetLoadSteps(); - break; - case '3': - // - break; - case '4': + } + break; + case '>': + { + if (segmentCurrent != 255 && registerCurrent != 255) { - // scale vertical + - if (GBS::VDS_VSCALE::read() <= 256) - { - _WSN(F("limit")); - break; - } - scaleVertical(2, true); - // actually requires full vertical mask + position offset calculation + writeOneByte(0xF0, segmentCurrent); + readFromRegister(registerCurrent, 1, &readout); + writeOneByte(registerCurrent, readout + 1); + _WSF(PSTR("S%d_0x%04X"), segmentCurrent, registerCurrent); + readFromRegister(registerCurrent, 1, &readout); + _WSF(PSTR(" : 0x%04X\n"), readout); } + } + break; + case '_': + { + uint32_t ticks = FrameSync::getPulseTicks(); + _WSN(ticks); + } + break; + case '~': + goLowPowerWithInputDetection(); // test reset + input detect break; - case '5': + case 'w': + { + // Serial.flush(); + uint16_t value = 0; + String what = Serial.readStringUntil(' '); + + if (what.length() > 5) + { + _WSN(F("abort")); + inputStage = 0; + break; + } + if (what.equals("f")) { - // scale vertical - - if (GBS::VDS_VSCALE::read() == 1023) + if (rto->extClockGenDetected) { - _WSN(F("limit")); - break; + _WSF(PSTR("old freqExtClockGen: %l\n"), rto->freqExtClockGen); + rto->freqExtClockGen = Serial.parseInt(); + // safety range: 1 - 250 MHz + if (rto->freqExtClockGen >= 1000000 && rto->freqExtClockGen <= 250000000) + { + Si.setFreq(0, rto->freqExtClockGen); + rto->clampPositionIsSet = 0; + rto->coastPositionIsSet = 0; + } + _WSF(PSTR("set freqExtClockGen: %l\n"), rto->freqExtClockGen); } - scaleVertical(2, false); - // actually requires full vertical mask + position offset calculation + break; } - break; - case '6': - if (videoStandardInputIsPalNtscSd() && !rto->outModeHdBypass) + + value = Serial.parseInt(); + if (value < 4096) + { + _WSF(PSTR("set %s %d\n"), what.c_str(), value); + if (what.equals("ht")) { - if (GBS::IF_HBIN_SP::read() >= 10) - { // IF_HBIN_SP: min 2 - GBS::IF_HBIN_SP::write(GBS::IF_HBIN_SP::read() - 8); // canvas move right - if ((GBS::IF_HSYNC_RST::read() - 4) > ((GBS::PLLAD_MD::read() >> 1) + 5)) - { - GBS::IF_HSYNC_RST::write(GBS::IF_HSYNC_RST::read() - 4); // shrink 1_0e - GBS::IF_LINE_SP::write(GBS::IF_LINE_SP::read() - 4); // and 1_22 to go with it - } + // set_htotal(value); + if (!rto->outModeHdBypass) + { + rto->forceRetime = 1; + applyBestHTotal(value); } else { - _WSN(F("limit")); + GBS::VDS_HSYNC_RST::write(value); } } - else if (!rto->outModeHdBypass) + else if (what.equals("vt")) { - if (GBS::IF_HB_SP2::read() >= 4) - GBS::IF_HB_SP2::write(GBS::IF_HB_SP2::read() - 4); // 1_1a - else - GBS::IF_HB_SP2::write(GBS::IF_HSYNC_RST::read() - 0x30); - if (GBS::IF_HB_ST2::read() >= 4) - GBS::IF_HB_ST2::write(GBS::IF_HB_ST2::read() - 4); // 1_18 - else - GBS::IF_HB_ST2::write(GBS::IF_HSYNC_RST::read() - 0x30); - _WSF(PSTR("IF_HB_ST2: 0x%04X IF_HB_SP2: 0x%04X\n"), - GBS::IF_HB_ST2::read(), GBS::IF_HB_SP2::read()); + set_vtotal(value); } - break; - case '7': - if (videoStandardInputIsPalNtscSd() && !rto->outModeHdBypass) + else if (what.equals("hsst")) { - if (GBS::IF_HBIN_SP::read() < 0x150) - { // (arbitrary) max limit - GBS::IF_HBIN_SP::write(GBS::IF_HBIN_SP::read() + 8); // canvas move left - } - else - { - _WSN(F("limit")); - } + setHSyncStartPosition(value); } - else if (!rto->outModeHdBypass) + else if (what.equals("hssp")) { - if (GBS::IF_HB_SP2::read() < (GBS::IF_HSYNC_RST::read() - 0x30)) - GBS::IF_HB_SP2::write(GBS::IF_HB_SP2::read() + 4); // 1_1a - else - GBS::IF_HB_SP2::write(0); - if (GBS::IF_HB_ST2::read() < (GBS::IF_HSYNC_RST::read() - 0x30)) - GBS::IF_HB_ST2::write(GBS::IF_HB_ST2::read() + 4); // 1_18 - else - GBS::IF_HB_ST2::write(0); - _WSF(PSTR("IF_HB_ST2: 0x%04X IF_HB_SP2: 0x%04X\n"), - GBS::IF_HB_ST2::read(), GBS::IF_HB_SP2::read()); + setHSyncStopPosition(value); } - break; - case '8': // invert sync button (dev. mode) - // _WSN("invert sync"); - rto->invertSync = !rto->invertSync; - invertHS(); - invertVS(); - // optimizePhaseSP(); - break; - case '9': - writeProgramArrayNew(ntsc_720x480, false); - doPostPresetLoadSteps(); - break; - case 'o': - { // oversampring button (dev.mode) - if (rto->osr == 1) + else if (what.equals("hbst")) { - setOverSampleRatio(2, false); + setMemoryHblankStartPosition(value); } - else if (rto->osr == 2) + else if (what.equals("hbsp")) { - setOverSampleRatio(4, false); + setMemoryHblankStopPosition(value); } - else if (rto->osr == 4) + else if (what.equals("hbstd")) { - setOverSampleRatio(1, false); + setDisplayHblankStartPosition(value); } - delay(4); - optimizePhaseSP(); - _WSF(PSTR("OSR 0x%02X\n"), rto->osr); - rto->phaseIsSet = 0; // do it again in modes applicable - } - break; - case 'g': - inputStage++; - // we have a multibyte command - if (inputStage > 0) + else if (what.equals("hbspd")) { - if (inputStage == 1) - { - segmentCurrent = Serial.parseInt(); - _WSF(PSTR("G%d"), segmentCurrent); - } - else if (inputStage == 2) - { - char szNumbers[3]; - szNumbers[0] = Serial.read(); - szNumbers[1] = Serial.read(); - szNumbers[2] = '\0'; - // char * pEnd; - registerCurrent = strtol(szNumbers, NULL, 16); - _WSF(PSTR("R 0x%04X"), registerCurrent); - if (segmentCurrent <= 5) - { - writeOneByte(0xF0, segmentCurrent); - readFromRegister(registerCurrent, 1, &readout); - _WSF(PSTR(" value: 0x%02X\n"), readout); - } - else - { - discardSerialRxData(); - _WSN(F("abort")); - } - inputStage = 0; - } + setDisplayHblankStopPosition(value); } - break; - case 's': - inputStage++; - // we have a multibyte command - if (inputStage > 0) + else if (what.equals("vsst")) { - if (inputStage == 1) - { - segmentCurrent = Serial.parseInt(); - _WSF(PSTR("S%d"), segmentCurrent); - } - else if (inputStage == 2) - { - char szNumbers[3]; - for (uint8_t a = 0; a <= 1; a++) - { - // ascii 0x30 to 0x39 for '0' to '9' - if ((Serial.peek() >= '0' && Serial.peek() <= '9') || - (Serial.peek() >= 'a' && Serial.peek() <= 'f') || - (Serial.peek() >= 'A' && Serial.peek() <= 'F')) - { - szNumbers[a] = Serial.read(); - } - else - { - szNumbers[a] = 0; // NUL char - Serial.read(); // but consume the char - } - } - szNumbers[2] = '\0'; - // char * pEnd; - registerCurrent = strtol(szNumbers, NULL, 16); - _WSF(PSTR("R 0x%04X"), registerCurrent); - } - else if (inputStage == 3) - { - char szNumbers[3]; - for (uint8_t a = 0; a <= 1; a++) - { - if ((Serial.peek() >= '0' && Serial.peek() <= '9') || - (Serial.peek() >= 'a' && Serial.peek() <= 'f') || - (Serial.peek() >= 'A' && Serial.peek() <= 'F')) - { - szNumbers[a] = Serial.read(); - } - else - { - szNumbers[a] = 0; // NUL char - Serial.read(); // but consume the char - } - } - szNumbers[2] = '\0'; - // char * pEnd; - inputToogleBit = strtol(szNumbers, NULL, 16); - if (segmentCurrent <= 5) - { - writeOneByte(0xF0, segmentCurrent); - readFromRegister(registerCurrent, 1, &readout); - _WSF(PSTR(" (was 0x%04X)"), readout); - writeOneByte(registerCurrent, inputToogleBit); - readFromRegister(registerCurrent, 1, &readout); - _WSF(PSTR(" is now: 0x%02X\n"), readout); - } - else - { - discardSerialRxData(); - _WSN(F("abort")); - } - inputStage = 0; - } + setVSyncStartPosition(value); } - break; - case 't': - inputStage++; - // we have a multibyte command - if (inputStage > 0) + else if (what.equals("vssp")) { - if (inputStage == 1) - { - segmentCurrent = Serial.parseInt(); - _WSF(PSTR("T%d"), segmentCurrent); - } - else if (inputStage == 2) - { - char szNumbers[3]; - for (uint8_t a = 0; a <= 1; a++) - { - // ascii 0x30 to 0x39 for '0' to '9' - if ((Serial.peek() >= '0' && Serial.peek() <= '9') || - (Serial.peek() >= 'a' && Serial.peek() <= 'f') || - (Serial.peek() >= 'A' && Serial.peek() <= 'F')) - { - szNumbers[a] = Serial.read(); - } - else - { - szNumbers[a] = 0; // NUL char - Serial.read(); // but consume the char - } - } - szNumbers[2] = '\0'; - // char * pEnd; - registerCurrent = strtol(szNumbers, NULL, 16); - _WSF(PSTR("R 0x%04X"), registerCurrent); - } - else if (inputStage == 3) - { - if (Serial.peek() >= '0' && Serial.peek() <= '7') - { - inputToogleBit = Serial.parseInt(); - } - else - { - inputToogleBit = 255; // will get discarded next step - } - _WSF(PSTR(" Bit: %d"), inputToogleBit); - inputStage = 0; - if ((segmentCurrent <= 5) && (inputToogleBit <= 7)) - { - writeOneByte(0xF0, segmentCurrent); - readFromRegister(registerCurrent, 1, &readout); - _WSF(PSTR(" (was 0x%04X)"), readout); - writeOneByte(registerCurrent, readout ^ (1 << inputToogleBit)); - readFromRegister(registerCurrent, 1, &readout); - _WSF(PSTR(" is now: 0x%02X\n"), readout); - } - else - { - discardSerialRxData(); - inputToogleBit = registerCurrent = 0; - _WSN(F("abort")); - } - } + setVSyncStopPosition(value); } - break; - case '<': - { - if (segmentCurrent != 255 && registerCurrent != 255) + else if (what.equals("vbst")) { - writeOneByte(0xF0, segmentCurrent); - readFromRegister(registerCurrent, 1, &readout); - writeOneByte(registerCurrent, readout - 1); // also allow wrapping - _WSF(PSTR("S%d_0x%04X"), segmentCurrent, registerCurrent); - readFromRegister(registerCurrent, 1, &readout); - _WSF(PSTR(" : 0x%04X\n"), readout); + setMemoryVblankStartPosition(value); } - } - break; - case '>': - { - if (segmentCurrent != 255 && registerCurrent != 255) + else if (what.equals("vbsp")) { - writeOneByte(0xF0, segmentCurrent); - readFromRegister(registerCurrent, 1, &readout); - writeOneByte(registerCurrent, readout + 1); - _WSF(PSTR("S%d_0x%04X"), segmentCurrent, registerCurrent); - readFromRegister(registerCurrent, 1, &readout); - _WSF(PSTR(" : 0x%04X\n"), readout); + setMemoryVblankStopPosition(value); } - } - break; - case '_': - { - uint32_t ticks = FrameSync::getPulseTicks(); - _WSN(ticks); - } - break; - case '~': - goLowPowerWithInputDetection(); // test reset + input detect - break; - case 'w': - { - // Serial.flush(); - uint16_t value = 0; - String what = Serial.readStringUntil(' '); - - if (what.length() > 5) + else if (what.equals("vbstd")) { - _WSN(F("abort")); - inputStage = 0; - break; + setDisplayVblankStartPosition(value); } - if (what.equals("f")) + else if (what.equals("vbspd")) { - if (rto->extClockGenDetected) - { - _WSF(PSTR("old freqExtClockGen: %l\n"), rto->freqExtClockGen); - rto->freqExtClockGen = Serial.parseInt(); - // safety range: 1 - 250 MHz - if (rto->freqExtClockGen >= 1000000 && rto->freqExtClockGen <= 250000000) - { - Si.setFreq(0, rto->freqExtClockGen); - rto->clampPositionIsSet = 0; - rto->coastPositionIsSet = 0; - } - _WSF(PSTR("set freqExtClockGen: %l\n"), rto->freqExtClockGen); - } - break; + setDisplayVblankStopPosition(value); } - - value = Serial.parseInt(); - if (value < 4096) + else if (what.equals("sog")) { - _WSF(PSTR("set %s %d\n"), what.c_str(), value); - if (what.equals("ht")) - { - // set_htotal(value); - if (!rto->outModeHdBypass) - { - rto->forceRetime = 1; - applyBestHTotal(value); - } - else - { - GBS::VDS_HSYNC_RST::write(value); - } - } - else if (what.equals("vt")) - { - set_vtotal(value); - } - else if (what.equals("hsst")) - { - setHSyncStartPosition(value); - } - else if (what.equals("hssp")) - { - setHSyncStopPosition(value); - } - else if (what.equals("hbst")) - { - setMemoryHblankStartPosition(value); - } - else if (what.equals("hbsp")) - { - setMemoryHblankStopPosition(value); - } - else if (what.equals("hbstd")) - { - setDisplayHblankStartPosition(value); - } - else if (what.equals("hbspd")) - { - setDisplayHblankStopPosition(value); - } - else if (what.equals("vsst")) - { - setVSyncStartPosition(value); - } - else if (what.equals("vssp")) - { - setVSyncStopPosition(value); - } - else if (what.equals("vbst")) - { - setMemoryVblankStartPosition(value); - } - else if (what.equals("vbsp")) - { - setMemoryVblankStopPosition(value); - } - else if (what.equals("vbstd")) - { - setDisplayVblankStartPosition(value); - } - else if (what.equals("vbspd")) - { - setDisplayVblankStopPosition(value); - } - else if (what.equals("sog")) - { - setAndUpdateSogLevel(value); - } - else if (what.equals("ifini")) - { - GBS::IF_INI_ST::write(value); - // _WSN(GBS::IF_INI_ST::read()); - } - else if (what.equals("ifvst")) - { - GBS::IF_VB_ST::write(value); - // _WSN(GBS::IF_VB_ST::read()); - } - else if (what.equals("ifvsp")) - { - GBS::IF_VB_SP::write(value); - // _WSN(GBS::IF_VB_ST::read()); - } - else if (what.equals("vsstc")) - { - setCsVsStart(value); - } - else if (what.equals("vsspc")) - { - setCsVsStop(value); - } + setAndUpdateSogLevel(value); } - else + else if (what.equals("ifini")) + { + GBS::IF_INI_ST::write(value); + // _WSN(GBS::IF_INI_ST::read()); + } + else if (what.equals("ifvst")) + { + GBS::IF_VB_ST::write(value); + // _WSN(GBS::IF_VB_ST::read()); + } + else if (what.equals("ifvsp")) + { + GBS::IF_VB_SP::write(value); + // _WSN(GBS::IF_VB_ST::read()); + } + else if (what.equals("vsstc")) + { + setCsVsStart(value); + } + else if (what.equals("vsspc")) { - _WSN(F("abort")); + setCsVsStop(value); } } + else + { + _WSN(F("abort")); + } + } + break; + case 'x': + { + uint16_t if_hblank_scale_stop = GBS::IF_HBIN_SP::read(); + GBS::IF_HBIN_SP::write(if_hblank_scale_stop + 1); + _WSF(PSTR("1_26: 0x%04X\n"), (if_hblank_scale_stop + 1)); + } + break; + case 'X': + { + uint16_t if_hblank_scale_stop = GBS::IF_HBIN_SP::read(); + GBS::IF_HBIN_SP::write(if_hblank_scale_stop - 1); + _WSF(PSTR("1_26: 0x%04X\n"), (if_hblank_scale_stop - 1)); + } + break; + case '(': + { + writeProgramArrayNew(ntsc_1920x1080, false); + doPostPresetLoadSteps(); + } + break; + case ')': + { + writeProgramArrayNew(pal_1920x1080, false); + doPostPresetLoadSteps(); + } + break; + case 'V': // toggle step response + { + _WS(F("step response ")); + uopt->wantStepResponse = !uopt->wantStepResponse; + if (uopt->wantStepResponse) + { + GBS::VDS_UV_STEP_BYPS::write(0); + _WSN(F("on")); + } + else + { + GBS::VDS_UV_STEP_BYPS::write(1); + _WSN(F("off")); + } + slotFlush(); + // saveUserPrefs(); + } + break; + case 'S': + { + snapToIntegralFrameRate(); + break; + } + case ':': + externalClockGenSyncInOutRate(); break; - case 'x': + case ';': + externalClockGenResetClock(); + if (rto->extClockGenDetected) + { + rto->extClockGenDetected = 0; + _WSN(F("ext clock gen bypass")); + } + else { - uint16_t if_hblank_scale_stop = GBS::IF_HBIN_SP::read(); - GBS::IF_HBIN_SP::write(if_hblank_scale_stop + 1); - _WSF(PSTR("1_26: 0x%04X\n"), (if_hblank_scale_stop + 1)); + rto->extClockGenDetected = 1; + _WSN(F("ext clock gen active")); + externalClockGenSyncInOutRate(); } + //{ + // float bla = 0; + // double esp8266_clock_freq = ESP.getCpuFreqMHz() * 1000000; + // bla = esp8266_clock_freq / (double)FrameSync::getPulseTicks(); + // _WSN(bla, 5); + //} + break; + default: + _WSF(PSTR("unknown command: 0x%04X\n"), serialCommand); break; - case 'X': + } + + delay(1); // give some time to read in eventual next chars + + // a web ui or terminal command has finished. good idea to reset sync lock timer + // important if the command was to change presets, possibly others + lastVsyncLock = millis(); + + // if (!Serial.available()) + // { + // // in case we handled a Serial or web server command and there's no more extra commands + // // but keep debug view command (resets once called) + // // TODO: we have rto->debugView + // if (serialCommand != 'D') + // { + // serialCommand = '@'; + // } + // wifiLoop(1); + // } + serialCommand = '@'; +} + +/** + * @brief initially was Type2 command + * + */ +void handleUserCommand() +{ + if (userCommand == '@') + return; + + _WSF( + commandDescr, + "user", + userCommand, + userCommand, + uopt->slotID, + uopt->resolutionID, + uopt->resolutionID); + + switch (userCommand) + { + case '0': + _WS(F("pal force 60hz ")); + uopt->PalForce60 = !uopt->PalForce60; + if (uopt->PalForce60 == 1) + _WSN(F("on")); + else + _WSN(F("off")); + slotFlush(); + break; + case '1': // reset to defaults button + // active slot settings reset + fsToFactory(); + // common parameters reset + loadDefaultUserOptions(); + saveUserPrefs(); + _WSN(F("options set to defaults, restarting")); + resetInMSec(2000); + break; + // case '2': + // + // break; + case '3': // load new preset on slot change + { + // TODO: unknown logic + if (rto->videoStandardInput == 14) { + // vga upscale path: let synwatcher handle it + rto->videoStandardInput = 15; + } else { + // also does loadPresetFromLFS() + applyPresets(rto->videoStandardInput); + } + } + break; + // case '4': + // break; + case '5': // Frame Time Lock toggle + uopt->enableFrameTimeLock = !uopt->enableFrameTimeLock; + // saveUserPrefs(); + if (uopt->enableFrameTimeLock) + { + _WSN(F("FTL on")); + } + else + { + _WSN(F("FTL off")); + } + if (!rto->extClockGenDetected) { - uint16_t if_hblank_scale_stop = GBS::IF_HBIN_SP::read(); - GBS::IF_HBIN_SP::write(if_hblank_scale_stop - 1); - _WSF(PSTR("1_26: 0x%04X\n"), (if_hblank_scale_stop - 1)); + FrameSync::reset(uopt->frameTimeLockMethod); + } + if (uopt->enableFrameTimeLock) + { + activeFrameTimeLockInitialSteps(); } + slotFlush(); break; - case '(': + // case '6': + // break; + case '7': // toggle scanlines + uopt->wantScanlines = !uopt->wantScanlines; + _WS(F("scanlines: ")); + if (uopt->wantScanlines) { - writeProgramArrayNew(ntsc_1920x1080, false); - doPostPresetLoadSteps(); + _WSN(F("on (Line Filter recommended)")); } + else + { + disableScanlines(); + _WSN(F("off")); + } + slotFlush(); + // saveUserPrefs(); break; - case ')': + // case '9': + // break; + case 'a': // device restart + resetInMSec(2000); + break; + case 'd': // toggle developer mode + rto->developerMode = !rto->developerMode; + serverWebSocketToggleDeveloperMode(); + serialDevModeToggle(); + break; + case 'e': // print files on data + { + _WSN(F("file system:")); + fs::Dir dir = LittleFS.openDir("/"); + while (dir.next()) + { + _WSF( + PSTR(" %s %ld\n"), + dir.fileName().c_str(), + dir.fileSize()); + } + // + fs::File f = LittleFS.open(FPSTR(preferencesFile), "r"); + if (!f) + { + _WSN(F("\nfailed to read system preferences")); + } + else + { + _WSF( + PSTR("\nsystem preferences:\n active slotID = %d\n component out. = %d\n scaling RGBHV = %d\n" \ + " ADC calibration = %d\n ext. clock gen. = %d\n\n"), + f.read(), + f.read(), + f.read(), + f.read(), + f.read() + ); + + f.close(); + } + } + break; + /** + * 1. change resolution + * 2. update registers if videoStandardInput != 14 + * 3. update/save preset data + */ + case 's': // 1080p + case 'p': // 1024p + case 'f': // 960p + case 'g': // 720p + case 'k': // 576p + case 'h': // 480p + case 'j': // 240p + case 'L': // 15kHz/downscale + { + // load preset via webui + uint8_t videoMode = getVideoMode(); + if (videoMode == 0 && GBS::STATUS_SYNC_PROC_HSACT::read()) + videoMode = rto->videoStandardInput; // last known good as fallback + // else videoMode stays 0 and we'll apply via some assumptions + if (userCommand == 'f') + // uopt->presetPreference = Output960P; // 1280x960 + uopt->resolutionID = Output960p; // 1280x960 + if (userCommand == 'g') + // uopt->presetPreference = Output720P; // 1280x720 + uopt->resolutionID = Output720p; // 1280x720 + if (userCommand == 'h') + // uopt->presetPreference = Output480P; // 720x480/768x576 + uopt->resolutionID = Output480p; // 720x480/768x576 + if (userCommand == 'p') + // uopt->presetPreference = Output1024P; // 1280x1024 + uopt->resolutionID = Output1024p; // 1280x1024 + if (userCommand == 's') + // uopt->presetPreference = Output1080P; // 1920x1080 + uopt->resolutionID = Output1080p; // 1920x1080 + if (userCommand == 'L') + // uopt->presetPreference = OutputDownscale; // downscale + uopt->resolutionID = Output15kHz; // downscale + if (userCommand == 'k') + uopt->resolutionID = Output576p50; // PAL + if (userCommand == 'j') + uopt->resolutionID = Output240p; // 240p + + rto->useHdmiSyncFix = 1; // disables sync out when programming preset + if (rto->videoStandardInput == 14) + { + // vga upscale path: let synwatcher handle it + _DBGN(F("video input is #14, switch to #15")); + rto->videoStandardInput = 15; + } + else + { + // normal path + _DBGF(PSTR("apply preset of videoMode: %d resolution: %d\n"), videoMode, uopt->resolutionID); + applyPresets(videoMode); + } + slotFlush(); + // saveUserPrefs(); + } + break; + case 'i': // toggle active frametime lock method + if (!rto->extClockGenDetected) { - writeProgramArrayNew(pal_1920x1080, false); - doPostPresetLoadSteps(); + FrameSync::reset(uopt->frameTimeLockMethod); } + + if (uopt->frameTimeLockMethod == 0) + { + uopt->frameTimeLockMethod = 1; + } + else if (uopt->frameTimeLockMethod == 1) + { + uopt->frameTimeLockMethod = 0; + } + // saveUserPrefs(); + activeFrameTimeLockInitialSteps(); + slotFlush(); break; - case 'V': + case 'l': + // cycle through available SDRAM clocks { - _WS(F("step response ")); - uopt->wantStepResponse = !uopt->wantStepResponse; - if (uopt->wantStepResponse) + uint8_t PLL_MS = GBS::PLL_MS::read(); + uint8_t memClock = 0; + + if (PLL_MS == 0) + PLL_MS = 2; + else if (PLL_MS == 2) + PLL_MS = 7; + else if (PLL_MS == 7) + PLL_MS = 4; + else if (PLL_MS == 4) + PLL_MS = 3; + else if (PLL_MS == 3) + PLL_MS = 5; + else if (PLL_MS == 5) + PLL_MS = 0; + + switch (PLL_MS) + { + case 0: + memClock = 108; + break; + case 1: + memClock = 81; + break; // goes well with 4_2C = 0x14, 4_2D = 0x27 + case 2: + memClock = 10; + break; // feedback clock + case 3: + memClock = 162; + break; + case 4: + memClock = 144; + break; + case 5: + memClock = 185; + break; // slight OC + case 6: + memClock = 216; + break; // !OC! + case 7: + memClock = 129; + break; + default: + break; + } + GBS::PLL_MS::write(PLL_MS); + ResetSDRAM(); + if (memClock != 10) { - GBS::VDS_UV_STEP_BYPS::write(0); - _WSN(F("on")); + _WSF(PSTR("SDRAM clock: %dMhz\n"), memClock); } else { - GBS::VDS_UV_STEP_BYPS::write(1); - _WSN(F("off")); + _WSN(F("SDRAM clock: feedback clock")); } - saveUserPrefs(); } break; - case 'S': + case 'm': // toggle line filter + _WS(F("Line Filter: ")); + if (uopt->wantVdsLineFilter) { - snapToIntegralFrameRate(); - break; + uopt->wantVdsLineFilter = 0; + GBS::VDS_D_RAM_BYPS::write(1); + _WSN(F("off")); } - case ':': - externalClockGenSyncInOutRate(); - break; - case ';': - externalClockGenResetClock(); - if (rto->extClockGenDetected) - { - rto->extClockGenDetected = 0; - _WSN(F("ext clock gen bypass")); - } - else - { - rto->extClockGenDetected = 1; - _WSN(F("ext clock gen active")); - externalClockGenSyncInOutRate(); - } - //{ - // float bla = 0; - // double esp8266_clock_freq = ESP.getCpuFreqMHz() * 1000000; - // bla = esp8266_clock_freq / (double)FrameSync::getPulseTicks(); - // _WSN(bla, 5); - //} - break; - default: - _WSF(PSTR("unknown command: 0x%04X\n"), serialCommand); - break; + else + { + uopt->wantVdsLineFilter = 1; + GBS::VDS_D_RAM_BYPS::write(0); + _WSN(F("on")); } - - delay(1); // give some time to read in eventual next chars - - // a web ui or terminal command has finished. good idea to reset sync lock timer - // important if the command was to change presets, possibly others - lastVsyncLock = millis(); - - if (!Serial.available()) + slotFlush(); + // saveUserPrefs(); + break; + case 'n': + uopt->enableAutoGain = 0; + setAdcGain(GBS::ADC_RGCTRL::read() - 1); + _WSF(PSTR("ADC gain++ : 0x%04X\n"), GBS::ADC_RGCTRL::read()); + break; + case 'o': + uopt->enableAutoGain = 0; + setAdcGain(GBS::ADC_RGCTRL::read() + 1); + _WSF(PSTR("ADC gain-- : 0x%04X\n"), GBS::ADC_RGCTRL::read()); + break; + case 'A': + { + uint16_t htotal = GBS::VDS_HSYNC_RST::read(); + uint16_t hbstd = GBS::VDS_DIS_HB_ST::read(); + uint16_t hbspd = GBS::VDS_DIS_HB_SP::read(); + if ((hbstd > 4) && (hbspd < (htotal - 4))) + { + GBS::VDS_DIS_HB_ST::write(GBS::VDS_DIS_HB_ST::read() - 4); + GBS::VDS_DIS_HB_SP::write(GBS::VDS_DIS_HB_SP::read() + 4); + } + else { - // in case we handled a Serial or web server command and there's no more extra commands - // but keep debug view command (resets once called) - // @sk: we have rto->debugView - // if (serialCommand != 'D') - // { - // serialCommand = '@'; - // } - wifiLoop(1); + _WSN(F("limit")); } } -} - -/** - * @brief initially was Type2 command - * - */ -void handleUserCommand() -{ - if (userCommand != '@') + break; + case 'B': { - _WSF( - commandDescr, - "user", - userCommand, - userCommand, - uopt->slotID, - uopt->resolutionID, - uopt->resolutionID); - switch (userCommand) - { - case '0': - _WS(F("pal force 60hz ")); - uopt->PalForce60 = !uopt->PalForce60; - if (uopt->PalForce60 == 1) - _WSN(F("on")); - else - _WSN(F("off")); - slotFlush(); - break; - case '1': // reset to defaults button - // active slot settings reset - slotResetFlush(uopt->slotID); - // common parameters reset - loadDefaultUserOptions(); - saveUserPrefs(); - _WSN(F("options set to defaults, restarting")); - resetInMSec(2000); - break; - case '2': - // - break; - case '3': // before: load custom preset, now: slot change - { - // @sk: see serverSlotSet() - // TODO: unknown logic - if (rto->videoStandardInput == 14) { - // vga upscale path: let synwatcher handle it - rto->videoStandardInput = 15; - } else { - // also does loadPresetFromLFS() - applyPresets(rto->videoStandardInput); - } - // save new slotID into preferences - saveUserPrefs(); + uint16_t htotal = GBS::VDS_HSYNC_RST::read(); + uint16_t hbstd = GBS::VDS_DIS_HB_ST::read(); + uint16_t hbspd = GBS::VDS_DIS_HB_SP::read(); + if ((hbstd < (htotal - 4)) && (hbspd > 4)) + { + GBS::VDS_DIS_HB_ST::write(GBS::VDS_DIS_HB_ST::read() + 4); + GBS::VDS_DIS_HB_SP::write(GBS::VDS_DIS_HB_SP::read() - 4); + } + else + { + _WSN(F("limit")); + } + } + break; + case 'C': + { + // vert mask + + uint16_t vtotal = GBS::VDS_VSYNC_RST::read(); + uint16_t vbstd = GBS::VDS_DIS_VB_ST::read(); + uint16_t vbspd = GBS::VDS_DIS_VB_SP::read(); + if ((vbstd > 6) && (vbspd < (vtotal - 4))) + { + GBS::VDS_DIS_VB_ST::write(vbstd - 2); + GBS::VDS_DIS_VB_SP::write(vbspd + 2); + } + else + { + _WSN(F("limit")); + } + } + break; + case 'D': + { + // vert mask - + uint16_t vtotal = GBS::VDS_VSYNC_RST::read(); + uint16_t vbstd = GBS::VDS_DIS_VB_ST::read(); + uint16_t vbspd = GBS::VDS_DIS_VB_SP::read(); + if ((vbstd < (vtotal - 4)) && (vbspd > 6)) + { + GBS::VDS_DIS_VB_ST::write(vbstd + 2); + GBS::VDS_DIS_VB_SP::write(vbspd - 2); + } + else + { + _WSN(F("limit")); } - break; - // case '4': - // break; - case '5': - // Frame Time Lock toggle - uopt->enableFrameTimeLock = !uopt->enableFrameTimeLock; - // saveUserPrefs(); - if (uopt->enableFrameTimeLock) - { - _WSN(F("FTL on")); - } - else - { - _WSN(F("FTL off")); - } - if (!rto->extClockGenDetected) - { - FrameSync::reset(uopt->frameTimeLockMethod); - } - if (uopt->enableFrameTimeLock) - { - activeFrameTimeLockInitialSteps(); - } - slotFlush(); - break; - case '6': - // - break; - case '7': - uopt->wantScanlines = !uopt->wantScanlines; - _WS(F("scanlines: ")); - if (uopt->wantScanlines) - { - _WSN(F("on (Line Filter recommended)")); - } - else + } + break; + case 'q': // bob / motionAdaptive + if (uopt->deintMode != 1) + { + // switch to bob mode + uopt->deintMode = 1; + disableMotionAdaptDeinterlace(); + if (GBS::GBS_OPTION_SCANLINES_ENABLED::read()) { disableScanlines(); - _WSN(F("off")); } - saveUserPrefs(); - break; - // case '9': - // - // break; - case 'a': // device restart - resetInMSec(2000); - break; - case 'e': // print files on data + // saveUserPrefs(); + _WSN(F("Deinterlacer: Bob")); + } else { + // switch to motion adaptive mode + uopt->deintMode = 0; + // saveUserPrefs(); + _WSN(F("Deinterlacer: Motion Adaptive")); + } + slotFlush(); + break; + // case 'r': + // break; + case 't': + // unused now + _WS(F("6-tap: ")); + if (uopt->wantTap6 == 0) { - _WSN(F("file system:")); - fs::Dir dir = LittleFS.openDir("/"); - while (dir.next()) - { - _WSF( - PSTR(" %s %ld\n"), - dir.fileName().c_str(), - dir.fileSize()); - } - // - fs::File f = LittleFS.open(FPSTR(preferencesFile), "r"); - if (!f) - { - _WSN(F("\nfailed to read system preferences")); - } - else - { - _WSF( - PSTR("\nsystem preferences:\n active slotID = %d\n component out. = %d\n scaling RGBHV = %d\n" \ - " ADC calibration = %d\n ext. clock gen. = %d\n"), - f.read(), - f.read(), - f.read(), - f.read(), - f.read() - ); - - f.close(); - } + uopt->wantTap6 = 1; + GBS::VDS_TAP6_BYPS::write(0); + GBS::MADPT_Y_DELAY_UV_DELAY::write(GBS::MADPT_Y_DELAY_UV_DELAY::read() - 1); + _WSN(F("on")); } + else + { + uopt->wantTap6 = 0; + GBS::VDS_TAP6_BYPS::write(1); + GBS::MADPT_Y_DELAY_UV_DELAY::write(GBS::MADPT_Y_DELAY_UV_DELAY::read() + 1); + _WSN(F("off")); + } + // saveUserPrefs(); + slotFlush(); break; - /** - * 1. change resolution - * 2. update registers if videoStandardInput != 14 - * 3. update/save preset data - */ - case 's': // 1080p - case 'p': // 1024p - case 'f': // 960p - case 'g': // 720p - case 'k': // 576p - case 'h': // 480p - case 'j': // 240p - case 'L': // 15kHz/downscale - { - // load preset via webui - uint8_t videoMode = getVideoMode(); - if (videoMode == 0 && GBS::STATUS_SYNC_PROC_HSACT::read()) - videoMode = rto->videoStandardInput; // last known good as fallback - // else videoMode stays 0 and we'll apply via some assumptions - if (userCommand == 'f') - // uopt->presetPreference = Output960P; // 1280x960 - uopt->resolutionID = Output960p; // 1280x960 - if (userCommand == 'g') - // uopt->presetPreference = Output720P; // 1280x720 - uopt->resolutionID = Output720p; // 1280x720 - if (userCommand == 'h') - // uopt->presetPreference = Output480P; // 720x480/768x576 - uopt->resolutionID = Output480p; // 720x480/768x576 - if (userCommand == 'p') - // uopt->presetPreference = Output1024P; // 1280x1024 - uopt->resolutionID = Output1024p; // 1280x1024 - if (userCommand == 's') - // uopt->presetPreference = Output1080P; // 1920x1080 - uopt->resolutionID = Output1080p; // 1920x1080 - if (userCommand == 'L') - // uopt->presetPreference = OutputDownscale; // downscale - uopt->resolutionID = Output15kHz; // downscale - if (userCommand == 'k') - uopt->resolutionID = Output576p50; // PAL - if (userCommand == 'j') - uopt->resolutionID = Output240p; // 240p - - rto->useHdmiSyncFix = 1; // disables sync out when programming preset - if (rto->videoStandardInput == 14) - { - // vga upscale path: let synwatcher handle it - _DBGN(F("video input is #14, switch to #15")); - rto->videoStandardInput = 15; - } - else + case 'u': // extract backup + extractBackup(); + // reset active slot + uopt->slotID = 0; + saveUserPrefs(); + // reset device to apply new configuration + resetInMSec(2000); + break; + case 'v': + { + uopt->wantFullHeight = !uopt->wantFullHeight; + // saveUserPrefs(); + slotFlush(); + uint8_t vidMode = getVideoMode(); + // if (uopt->presetPreference == 5) { + if (uopt->resolutionID == Output1080p) + { + if (GBS::GBS_PRESET_ID::read() == 0x05 || GBS::GBS_PRESET_ID::read() == 0x15) { - // normal path - _DBGF(PSTR("apply preset of videoMode: %d resolution: %d\n"), videoMode, uopt->resolutionID); - applyPresets(videoMode); + applyPresets(vidMode); } - saveUserPrefs(); } + } + break; + case 'w': // ADC calibration + _WS(F("ADC calibration: ")); + uopt->enableCalibrationADC = !uopt->enableCalibrationADC; + if(uopt->enableCalibrationADC) + _WSN(F("on")); + else + _WSN(F("off")); + saveUserPrefs(); break; - case 'i': - // toggle active frametime lock method - if (!rto->extClockGenDetected) - { - FrameSync::reset(uopt->frameTimeLockMethod); - } - - if (uopt->frameTimeLockMethod == 0) - { - uopt->frameTimeLockMethod = 1; - } - else if (uopt->frameTimeLockMethod == 1) - { - uopt->frameTimeLockMethod = 0; - } - // saveUserPrefs(); - activeFrameTimeLockInitialSteps(); - slotFlush(); - break; - case 'l': - // cycle through available SDRAM clocks - { - uint8_t PLL_MS = GBS::PLL_MS::read(); - uint8_t memClock = 0; - - if (PLL_MS == 0) - PLL_MS = 2; - else if (PLL_MS == 2) - PLL_MS = 7; - else if (PLL_MS == 7) - PLL_MS = 4; - else if (PLL_MS == 4) - PLL_MS = 3; - else if (PLL_MS == 3) - PLL_MS = 5; - else if (PLL_MS == 5) - PLL_MS = 0; - - switch (PLL_MS) - { - case 0: - memClock = 108; - break; - case 1: - memClock = 81; - break; // goes well with 4_2C = 0x14, 4_2D = 0x27 - case 2: - memClock = 10; - break; // feedback clock - case 3: - memClock = 162; - break; - case 4: - memClock = 144; - break; - case 5: - memClock = 185; - break; // slight OC - case 6: - memClock = 216; - break; // !OC! - case 7: - memClock = 129; - break; - default: - break; - } - GBS::PLL_MS::write(PLL_MS); - ResetSDRAM(); - if (memClock != 10) - { - _WSF(PSTR("SDRAM clock: %dMhz\n"), memClock); - } - else - { - _WSN(F("SDRAM clock: feedback clock")); - } - } - break; - case 'm': - _WS(F("Line Filter: ")); - if (uopt->wantVdsLineFilter) - { - uopt->wantVdsLineFilter = 0; - GBS::VDS_D_RAM_BYPS::write(1); - _WSN(F("off")); - } - else - { - uopt->wantVdsLineFilter = 1; - GBS::VDS_D_RAM_BYPS::write(0); - _WSN(F("on")); - } - saveUserPrefs(); - break; - case 'n': - uopt->enableAutoGain = 0; - setAdcGain(GBS::ADC_RGCTRL::read() - 1); - _WSF(PSTR("ADC gain++ : 0x%04X\n"), GBS::ADC_RGCTRL::read()); - break; - case 'o': - uopt->enableAutoGain = 0; - setAdcGain(GBS::ADC_RGCTRL::read() + 1); - _WSF(PSTR("ADC gain-- : 0x%04X\n"), GBS::ADC_RGCTRL::read()); - break; - case 'A': + case 'x': // do upscaling on low resolutions (preferScalingRgbhv) + uopt->preferScalingRgbhv = !uopt->preferScalingRgbhv; + _WS(F("preferScalingRgbhv: ")); + if (uopt->preferScalingRgbhv) { - uint16_t htotal = GBS::VDS_HSYNC_RST::read(); - uint16_t hbstd = GBS::VDS_DIS_HB_ST::read(); - uint16_t hbspd = GBS::VDS_DIS_HB_SP::read(); - if ((hbstd > 4) && (hbspd < (htotal - 4))) - { - GBS::VDS_DIS_HB_ST::write(GBS::VDS_DIS_HB_ST::read() - 4); - GBS::VDS_DIS_HB_SP::write(GBS::VDS_DIS_HB_SP::read() + 4); - } - else - { - _WSN(F("limit")); - } + _WSN(F("on")); + } + else + { + _WSN(F("off")); } + saveUserPrefs(); break; - case 'B': + case 'X': // enable/disable ext. clock generator + _WS(F("ExternalClockGenerator: ")); + if (uopt->disableExternalClockGenerator == 0) { - uint16_t htotal = GBS::VDS_HSYNC_RST::read(); - uint16_t hbstd = GBS::VDS_DIS_HB_ST::read(); - uint16_t hbspd = GBS::VDS_DIS_HB_SP::read(); - if ((hbstd < (htotal - 4)) && (hbspd > 4)) - { - GBS::VDS_DIS_HB_ST::write(GBS::VDS_DIS_HB_ST::read() + 4); - GBS::VDS_DIS_HB_SP::write(GBS::VDS_DIS_HB_SP::read() - 4); - } - else - { - _WSN(F("limit")); - } + uopt->disableExternalClockGenerator = 1; + _WSN(F("off")); + } + else + { + uopt->disableExternalClockGenerator = 0; + _WSN("on"); } + saveUserPrefs(); break; - case 'C': + case 'z': + // sog slicer level + if (rto->currentLevelSOG > 0) { - // vert mask + - uint16_t vtotal = GBS::VDS_VSYNC_RST::read(); - uint16_t vbstd = GBS::VDS_DIS_VB_ST::read(); - uint16_t vbspd = GBS::VDS_DIS_VB_SP::read(); - if ((vbstd > 6) && (vbspd < (vtotal - 4))) - { - GBS::VDS_DIS_VB_ST::write(vbstd - 2); - GBS::VDS_DIS_VB_SP::write(vbspd + 2); - } - else - { - _WSN(F("limit")); - } + rto->currentLevelSOG -= 1; + } + else + { + rto->currentLevelSOG = 16; } + setAndUpdateSogLevel(rto->currentLevelSOG); + optimizePhaseSP(); + _WSF( + PSTR("Phase: %d SOG: %d\n"), + rto->phaseSP, + rto->currentLevelSOG); break; - case 'D': + case 'E': + // test option for now + _WS(F("IF Auto Offset: ")); + toggleIfAutoOffset(); + if (GBS::IF_AUTO_OFST_EN::read()) { - // vert mask - - uint16_t vtotal = GBS::VDS_VSYNC_RST::read(); - uint16_t vbstd = GBS::VDS_DIS_VB_ST::read(); - uint16_t vbspd = GBS::VDS_DIS_VB_SP::read(); - if ((vbstd < (vtotal - 4)) && (vbspd > 6)) - { - GBS::VDS_DIS_VB_ST::write(vbstd + 2); - GBS::VDS_DIS_VB_SP::write(vbspd - 2); - } - else - { - _WSN(F("limit")); - } + _WSN(F("on")); + } + else + { + _WSN(F("off")); } break; - case 'q': // bob / motionAdaptive - if (uopt->deintMode != 1) - { - // switch to bob mode - uopt->deintMode = 1; - disableMotionAdaptDeinterlace(); - if (GBS::GBS_OPTION_SCANLINES_ENABLED::read()) - { - disableScanlines(); - } - // saveUserPrefs(); - _WSN(F("Deinterlacer: Bob")); - } else { - // switch to motion adaptive mode - uopt->deintMode = 0; - // saveUserPrefs(); - _WSN(F("Deinterlacer: Motion Adaptive")); - } - slotFlush(); - break; - // case 'r': - // break; - case 't': - // unused now - _WS(F("6-tap: ")); - if (uopt->wantTap6 == 0) - { - uopt->wantTap6 = 1; - GBS::VDS_TAP6_BYPS::write(0); - GBS::MADPT_Y_DELAY_UV_DELAY::write(GBS::MADPT_Y_DELAY_UV_DELAY::read() - 1); - _WSN(F("on")); - } - else - { - uopt->wantTap6 = 0; - GBS::VDS_TAP6_BYPS::write(1); - GBS::MADPT_Y_DELAY_UV_DELAY::write(GBS::MADPT_Y_DELAY_UV_DELAY::read() + 1); - _WSN(F("off")); - } - saveUserPrefs(); - break; - case 'u': // extract backup - extractBackup(); - // reset device to apply new configuration - resetInMSec(2000); - case 'v': + case 'F': // freeze pic + if (GBS::CAPTURE_ENABLE::read()) { - uopt->wantFullHeight = !uopt->wantFullHeight; - // saveUserPrefs(); - slotFlush(); - uint8_t vidMode = getVideoMode(); - // if (uopt->presetPreference == 5) { - if (uopt->resolutionID == Output1080p) - { - if (GBS::GBS_PRESET_ID::read() == 0x05 || GBS::GBS_PRESET_ID::read() == 0x15) - { - applyPresets(vidMode); - } - } + rto->freezeCapture = false; + GBS::CAPTURE_ENABLE::write(0); + _WSN(F("capture: disabled")); + } + else + { + rto->freezeCapture = true; + GBS::CAPTURE_ENABLE::write(1); + _WSN(F("capture: enabled")); } break; - case 'w': // ADC calibration - uopt->enableCalibrationADC = !uopt->enableCalibrationADC; - saveUserPrefs(); - break; - case 'x': // preferScalingRgbhv - uopt->preferScalingRgbhv = !uopt->preferScalingRgbhv; - _WS(F("preferScalingRgbhv: ")); - if (uopt->preferScalingRgbhv) - { - _WSN(F("on")); - } - else - { - _WSN(F("off")); - } - saveUserPrefs(); - break; - case 'X': // enable/disable ext. clock generator - _WS(F("ExternalClockGenerator ")); - if (uopt->disableExternalClockGenerator == 0) - { - uopt->disableExternalClockGenerator = 1; - _WSN(F("disabled")); - } - else - { - uopt->disableExternalClockGenerator = 0; - _WSN("enabled"); - } - saveUserPrefs(); - break; - case 'z': - // sog slicer level - if (rto->currentLevelSOG > 0) - { - rto->currentLevelSOG -= 1; - } - else - { - rto->currentLevelSOG = 16; - } - setAndUpdateSogLevel(rto->currentLevelSOG); - optimizePhaseSP(); - _WSF( - PSTR("Phase: %d SOG: %d\n"), - rto->phaseSP, - rto->currentLevelSOG); - break; - case 'E': - // test option for now - _WS(F("IF Auto Offset: ")); - toggleIfAutoOffset(); - if (GBS::IF_AUTO_OFST_EN::read()) - { - _WSN(F("on")); - } - else - { - _WSN(F("off")); - } - break; - case 'F': // freeze pic - if (GBS::CAPTURE_ENABLE::read()) - { - _WSN(F("capture: disabled")); - GBS::CAPTURE_ENABLE::write(0); - } - else - { - _WSN(F("capture: enabled")); - GBS::CAPTURE_ENABLE::write(1); - } - break; - case 'K': - // scanline strength - if (uopt->scanlineStrength >= 0x10) - { - uopt->scanlineStrength -= 0x10; - } - else - { - uopt->scanlineStrength = 0x50; - } - if (rto->scanlinesEnabled) - { - GBS::MADPT_Y_MI_OFFSET::write(uopt->scanlineStrength); - GBS::MADPT_UV_MI_OFFSET::write(uopt->scanlineStrength); - } - saveUserPrefs(); - _WSF(PSTR("Scanline Brightness: 0x%04X\n"), uopt->scanlineStrength); - break; - case 'Z': - // brightness++ - GBS::VDS_Y_OFST::write(GBS::VDS_Y_OFST::read() + 1); - _WSF( - PSTR("Brightness++ : %d\n"), - GBS::VDS_Y_OFST::read()); - break; - case 'T': - // brightness-- - GBS::VDS_Y_OFST::write(GBS::VDS_Y_OFST::read() - 1); - _WSF( - PSTR("Brightness-- : %d\n"), - GBS::VDS_Y_OFST::read()); - break; - case 'N': - // contrast++ - GBS::VDS_Y_GAIN::write(GBS::VDS_Y_GAIN::read() + 1); - _WSF( - PSTR("Contrast++ : %d\n"), - GBS::VDS_Y_GAIN::read()); - break; - case 'M': - // contrast-- - GBS::VDS_Y_GAIN::write(GBS::VDS_Y_GAIN::read() - 1); - _WSF( - PSTR("Contrast-- : %d\n"), - GBS::VDS_Y_GAIN::read()); - break; - case 'Q': - // pb/u gain++ - GBS::VDS_UCOS_GAIN::write(GBS::VDS_UCOS_GAIN::read() + 1); - _WSF( - PSTR("Pb/U gain++ : %d\n"), - GBS::VDS_UCOS_GAIN::read()); - break; - case 'H': - // pb/u gain-- - GBS::VDS_UCOS_GAIN::write(GBS::VDS_UCOS_GAIN::read() - 1); + case 'K': // scanline strength + if (uopt->scanlineStrength >= 0x10) + { + uopt->scanlineStrength -= 0x10; + } + else + { + uopt->scanlineStrength = 0x50; + } + if (rto->scanlinesEnabled) + { + GBS::MADPT_Y_MI_OFFSET::write(uopt->scanlineStrength); + GBS::MADPT_UV_MI_OFFSET::write(uopt->scanlineStrength); + } + slotFlush(); + // saveUserPrefs(); + _WSF(PSTR("Scanline Brightness: 0x%04X\n"), uopt->scanlineStrength); + break; + case 'Z': // brightness++ + GBS::VDS_Y_OFST::write(GBS::VDS_Y_OFST::read() + 1); + _WSF( + PSTR("Brightness++ : %d\n"), + GBS::VDS_Y_OFST::read()); + break; + case 'T': // brightness-- + GBS::VDS_Y_OFST::write(GBS::VDS_Y_OFST::read() - 1); + _WSF( + PSTR("Brightness-- : %d\n"), + GBS::VDS_Y_OFST::read()); + break; + case 'N': // contrast++ + GBS::VDS_Y_GAIN::write(GBS::VDS_Y_GAIN::read() + 1); + _WSF( + PSTR("Contrast++ : %d\n"), + GBS::VDS_Y_GAIN::read()); + break; + case 'M': // contrast-- + GBS::VDS_Y_GAIN::write(GBS::VDS_Y_GAIN::read() - 1); + _WSF( + PSTR("Contrast-- : %d\n"), + GBS::VDS_Y_GAIN::read()); + break; + case 'Q': // pb/u gain++ + GBS::VDS_UCOS_GAIN::write(GBS::VDS_UCOS_GAIN::read() + 1); + _WSF( + PSTR("Pb/U gain++ : %d\n"), + GBS::VDS_UCOS_GAIN::read()); + break; + case 'H': // pb/u gain-- + GBS::VDS_UCOS_GAIN::write(GBS::VDS_UCOS_GAIN::read() - 1); + _WSF( + PSTR("Pb/U gain-- : %d\n"), + GBS::VDS_UCOS_GAIN::read()); + break; + break; + case 'P': // pr/v gain++ + GBS::VDS_VCOS_GAIN::write(GBS::VDS_VCOS_GAIN::read() + 1); + _WSF( + PSTR("Pr/V gain++ : %d\n"), + GBS::VDS_VCOS_GAIN::read()); + break; + case 'S': // pr/v gain-- + GBS::VDS_VCOS_GAIN::write(GBS::VDS_VCOS_GAIN::read() - 1); + _WSF( + PSTR("Pr/V gain-- : %d\n"), + GBS::VDS_VCOS_GAIN::read()); + break; + case 'O': // info + if (GBS::ADC_INPUT_SEL::read() == 1) + { _WSF( - PSTR("Pb/U gain-- : %d\n"), + PSTR("RGB reg\n------------\nY_OFFSET: %d\n" \ + "U_OFFSET: %d\nV_OFFSET: %d\nY_GAIN: %d\n" \ + "USIN_GAIN: %d\nUCOS_GAIN: %d\n"), + GBS::VDS_Y_OFST::read(), + GBS::VDS_U_OFST::read(), + GBS::VDS_V_OFST::read(), + GBS::VDS_Y_GAIN::read(), + GBS::VDS_USIN_GAIN::read(), GBS::VDS_UCOS_GAIN::read()); - break; - break; - case 'P': - // pr/v gain++ - GBS::VDS_VCOS_GAIN::write(GBS::VDS_VCOS_GAIN::read() + 1); - _WSF( - PSTR("Pr/V gain++ : %d\n"), - GBS::VDS_VCOS_GAIN::read()); - break; - case 'S': - // pr/v gain-- - GBS::VDS_VCOS_GAIN::write(GBS::VDS_VCOS_GAIN::read() - 1); + } + else + { _WSF( - PSTR("Pr/V gain-- : %d\n"), - GBS::VDS_VCOS_GAIN::read()); - break; - case 'O': - // info - if (GBS::ADC_INPUT_SEL::read() == 1) - { - _WSF( - PSTR("RGB reg\n------------\nY_OFFSET: %d\n" \ - "U_OFFSET: %d\nV_OFFSET: %d\nY_GAIN: %d\n" \ + PSTR("YPbPr reg\n------------\nY_OFFSET: %d\n" + "U_OFFSET: %d\nV_OFFSET: %d\nY_GAIN: %d\n" "USIN_GAIN: %d\nUCOS_GAIN: %d\n"), - GBS::VDS_Y_OFST::read(), - GBS::VDS_U_OFST::read(), - GBS::VDS_V_OFST::read(), - GBS::VDS_Y_GAIN::read(), - GBS::VDS_USIN_GAIN::read(), - GBS::VDS_UCOS_GAIN::read()); - } - else - { - _WSF( - PSTR("YPbPr reg\n------------\nY_OFFSET: %d\n" - "U_OFFSET: %d\nV_OFFSET: %d\nY_GAIN: %d\n" - "USIN_GAIN: %d\nUCOS_GAIN: %d\n"), - GBS::VDS_Y_OFST::read(), - GBS::VDS_U_OFST::read(), - GBS::VDS_V_OFST::read(), - GBS::VDS_Y_GAIN::read(), - GBS::VDS_USIN_GAIN::read(), - GBS::VDS_UCOS_GAIN::read()); - } - break; - case 'U': - // default - if (GBS::ADC_INPUT_SEL::read() == 1) - { - GBS::VDS_Y_GAIN::write(128); - GBS::VDS_UCOS_GAIN::write(28); - GBS::VDS_VCOS_GAIN::write(41); - GBS::VDS_Y_OFST::write(0); - GBS::VDS_U_OFST::write(0); - GBS::VDS_V_OFST::write(0); - GBS::ADC_ROFCTRL::write(adco->r_off); - GBS::ADC_GOFCTRL::write(adco->g_off); - GBS::ADC_BOFCTRL::write(adco->b_off); - _WSN(F("RGB:defauit")); - } - else - { - GBS::VDS_Y_GAIN::write(128); - GBS::VDS_UCOS_GAIN::write(28); - GBS::VDS_VCOS_GAIN::write(41); - GBS::VDS_Y_OFST::write(254); - GBS::VDS_U_OFST::write(3); - GBS::VDS_V_OFST::write(3); - GBS::ADC_ROFCTRL::write(adco->r_off); - GBS::ADC_GOFCTRL::write(adco->g_off); - GBS::ADC_BOFCTRL::write(adco->b_off); - _WSN(F("YPbPr:defauit")); - } - break; - default: - break; + GBS::VDS_Y_OFST::read(), + GBS::VDS_U_OFST::read(), + GBS::VDS_V_OFST::read(), + GBS::VDS_Y_GAIN::read(), + GBS::VDS_USIN_GAIN::read(), + GBS::VDS_UCOS_GAIN::read()); } - - userCommand = '@'; // in case we handled web server command - lastVsyncLock = millis(); - wifiLoop(1); + break; + case 'U': // default + if (GBS::ADC_INPUT_SEL::read() == 1) + { + GBS::VDS_Y_GAIN::write(128); + GBS::VDS_UCOS_GAIN::write(28); + GBS::VDS_VCOS_GAIN::write(41); + GBS::VDS_Y_OFST::write(0); + GBS::VDS_U_OFST::write(0); + GBS::VDS_V_OFST::write(0); + GBS::ADC_ROFCTRL::write(adco->r_off); + GBS::ADC_GOFCTRL::write(adco->g_off); + GBS::ADC_BOFCTRL::write(adco->b_off); + _WSN(F("RGB:defauit")); + } + else + { + GBS::VDS_Y_GAIN::write(128); + GBS::VDS_UCOS_GAIN::write(28); + GBS::VDS_VCOS_GAIN::write(41); + GBS::VDS_Y_OFST::write(254); + GBS::VDS_U_OFST::write(3); + GBS::VDS_V_OFST::write(3); + GBS::ADC_ROFCTRL::write(adco->r_off); + GBS::ADC_GOFCTRL::write(adco->g_off); + GBS::ADC_BOFCTRL::write(adco->b_off); + _WSN(F("YPbPr:defauit")); + } + break; + default: + break; } + + userCommand = '@'; // in case we handled web server command + lastVsyncLock = millis(); } /** @@ -2675,6 +2718,108 @@ void fsToFactory() { LittleFS.remove(fn); delay(50); } + // set default slot ID + uopt->slotID = 0; +} + +/** + * @brief webSocket events handler + * + * @param num + * @param type + * @param payload + * @param length + */ +void webSocketEvent(uint8_t num, uint8_t type, uint8_t * payload, size_t length) { + switch (type) { + case WStype_DISCONNECTED: + case WStype_CONNECTED: + _DBGF(PSTR("(ws) %d client(s) connected\n"), num+1); + break; + case WStype_PONG: + if (webSocket.connectedClients() > 0) { + // debug output + if(rto->developerMode) { + const char * buff = serialGetBuffer(); + if(*buff != '\0') { + // sending to all clients + webSocket.broadcastBIN((uint8_t *)buff, SERIAL_BUFFER_MAX_LEN); + serialBufferClean(); + return; + } + } + // system status values for an ordinary heartbeat + constexpr size_t MESSAGE_LEN = 8; + uint8_t toSend[MESSAGE_LEN]; + memset(toSend, 0, MESSAGE_LEN); + // special character # used for message filtering in WebUI + toSend[0] = '#'; + toSend[1] = uopt->slotID + '0'; + // TODO: resolutionID must be INTEGER too? + toSend[2] = (char)uopt->resolutionID; + // + if (uopt->wantScanlines) + toSend[3] |= (1 << 0); + if (uopt->wantVdsLineFilter) + toSend[3] |= (1 << 1); + if (uopt->wantStepResponse) + toSend[3] |= (1 << 2); + if (uopt->wantPeaking) + toSend[3] |= (1 << 3); + if (uopt->enableAutoGain) + toSend[3] |= (1 << 4); + if (uopt->enableFrameTimeLock) + toSend[3] |= (1 << 5); + + // + if (uopt->deintMode == 0) // motion adaptive if == 0 + toSend[4] |= (1 << 0); + if (uopt->deintMode == 1) // bob if == 1 + toSend[4] |= (1 << 1); + // if (uopt->wantTap6) { + // toSend[4] |= (1 << 1); + // } + if (uopt->wantFullHeight) + toSend[4] |= (1 << 2); + // if (uopt->matchPresetSource) + // toSend[4] |= (1 << 3); + if (uopt->PalForce60 == 1) + toSend[4] |= (1 << 3); + + // system preferences + if (uopt->wantOutputComponent) + toSend[5] |= (1 << 0); + if (uopt->enableCalibrationADC) + toSend[5] |= (1 << 1); + if (uopt->preferScalingRgbhv) + toSend[5] |= (1 << 2); + if (uopt->disableExternalClockGenerator) + toSend[5] |= (1 << 3); + + // developer panel controls status + if(rto->developerMode) + toSend[6] |= (1 << 0); + if(rto->printInfos) + toSend[6] |= (1 << 1); + if(rto->invertSync) + toSend[6] |= (1 << 2); + if(rto->osr != 0) + toSend[6] |= (1 << 3); + if(rto->adcFilter) + toSend[6] |= (1 << 4); + if(rto->debugView) + toSend[6] |= (1 << 5); + if(rto->freezeCapture) + toSend[6] |= (1 << 6); + + // system tab controls + if(rto->allowUpdatesOTA) + toSend[7] |= (1 << 0); + + webSocket.sendBIN(num, toSend, MESSAGE_LEN); + } + break; + } } #endif // defined(ESP8266) \ No newline at end of file diff --git a/src/wserver.h b/src/wserver.h index 9f35865..b2953c0 100644 --- a/src/wserver.h +++ b/src/wserver.h @@ -3,7 +3,7 @@ # File: wserver.h # # File Created: Friday, 19th April 2024 3:11:47 pm # # Author: Sergey Ko # -# Last Modified: Friday, 7th June 2024 3:55:49 pm # +# Last Modified: Monday, 10th June 2024 12:27:21 pm # # Modified By: Sergey Ko # ##################################################################################### # CHANGELOG: # @@ -48,6 +48,8 @@ const char mimeOctetStream[] PROGMEM = "application/octet-stream"; const char mimeAppJson[] PROGMEM = "application/json"; void serverInit(); +void serverWebSocketToggleDeveloperMode(); +void serverWebSocketInit(); void serverHome(); void serverSC(); void serverUC(); @@ -79,6 +81,7 @@ void handleSerialCommand(); void handleUserCommand(); void initUpdateOTA(); void fsToFactory(); +void webSocketEvent(uint8_t num, uint8_t type, uint8_t * payload, size_t length); #endif // defined(ESP8266) #endif // _ESPWSERVER_H_ \ No newline at end of file diff --git a/translation.hdwui.json b/translation.hdwui.json index d5a972f..4a9a14f 100644 --- a/translation.hdwui.json +++ b/translation.hdwui.json @@ -38,19 +38,19 @@ "ru": "Профили настроек" }, "OM_RESET_RESTORE": { - "en": "Reset/Restore", - "es": "Resetear/Restaurar", - "ru": "Сброс/Восстановление" + "en": "Reset / Restore", + "es": "Resetear / Restaurar", + "ru": "Сброс / Восстановление" }, "OM_RESET_GBS": { - "en": "Reset GBS", - "es": "Resetear ajustes", - "ru": "Сбросить настройки" + "en": "Restart device", + "es": "Reiniciar dispositivo", + "ru": "Перезагрузить" }, "OM_RESET_WIFI": { "en": "Reset WiFi settings", - "es": "Borrar ajustes WiFi", - "ru": "Сброс настройек WiFi" + "es": "Borrar ajustes de WiFi", + "ru": "Сброс настроек WiFi" }, "OM_RESTORE_FACTORY": { "en": "Restore Factory", diff --git a/translation.webui.json b/translation.webui.json index 9c87d55..89c8842 100644 --- a/translation.webui.json +++ b/translation.webui.json @@ -190,16 +190,17 @@ "ru": "Соответствать Профилю", "zh": "" }, - - - - "MATCH_PRESETS_SWITCH_HELP_1": { "en": "If enabled, default to 1280x960 for NTSC 60 and 1280x1024 for PAL 50 (does not apply for 720p / 1080p presets).", "es": "Si está habilitado, el valor predeterminado es 1280x960 para NTSC 60 y 1280x1024 para PAL 50 (no se aplica a los ajustes preestablecidos de 720p/1080p).", "ru": "Если этот параметр включен, по умолчанию используется разрешение 1280x960 для NTSC 60 и 1280x1024 для PAL 50 (не применяется к предустановкам 720p/1080p).", "zh": "" }, + + + + + "FULL_HEIGHT_SWITCH": { "en": "Full Height", "es": "Estirar en altura", @@ -411,9 +412,9 @@ "zh": "" }, "DEVELOPER_MODE_SWITCH_HELP_1": { - "en": "Enables the developer menu which contains various debugging tools", - "es": "Habilita el menú de desarrollador que contiene varias herramientas adicionales.", - "ru": "Включает меню разработчика, содержащее различные инструменты отладки.", + "en": "Enables the developer menu and real time debug console which contains various debugging tools", + "es": "Habilita el menú de desarrollador y consola de registro que contiene varias herramientas adicionales.", + "ru": "Включает меню разработчика и консоль отладки, содержащее различные инструменты отладки.", "zh": "" }, "DEVELOPER_LEGEND": { @@ -422,6 +423,12 @@ "ru": "Разработчик", "zh": "" }, + "DEVELOPER_JS_ALERT_TEXTDECODER": { + "en": "This browser does not support TextDecoder feature...", + "es": "Este navegador no es compatible con la función TextDecoder...", + "ru": "Этот браузер не поддерживает функцию TextDecoder...", + "zh": "" + }, "TOGGLE_CONSOLE_BUTTON": { "en": "Toggle Console", "es": "Ocultar/Mostrar consola", @@ -591,9 +598,9 @@ "zh": "" }, "RESET_BUTTON_JS_ALERT_MESSAGE": { - "en": "

    This action will reset:

    • Active Slot parameters,
    • Common parameters

    Are you ready to continue?

    ", - "es": "

    Esta por resetear:

    • Parámetros del Perfil activo,
    • Configuración común

    Quiere seguir reiniciando?

    ", - "ru": "

    Следующие параметры будут сброшены:

    • Параметры активного Профиля,
    • Общесистемные параметры

    Желаете продолжить?

    ", + "en": "

    This action will reset your device to factory state. The data to be deleted:

    • All Slots and Presets data,
    • Common parameters

    Are you ready to continue?

    ", + "es": "

    Restablecer el estado de fábrica. Los siguientes datos serán eliminados:

    • Todos Perfiles y sus ajustes,
    • Configuración común

    Quiere seguir reiniciando?

    ", + "ru": "

    Сброс устройства к заводскому состоянию. Следующие данные будут удалены:

    • Все Профили и предустановки,
    • Общесистемные параметры

    Желаете продолжить?

    ", "zh": "" }, "BACKUP_LEGEND": {