/*
  xdrv_127_debug.ino - debug support for Tasmota

  Copyright (C) 2021  Theo Arends

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

//#define USE_DEBUG_DRIVER

#ifdef DEBUG_THEO
#ifndef USE_DEBUG_DRIVER
#define USE_DEBUG_DRIVER
#endif  // USE_DEBUG_DRIVER
#endif  // DEBUG_THEO

#ifdef USE_DEBUG_DRIVER
/*********************************************************************************************\
 * Virtual debugging support - Part1
 *
 * Needs file zzzz_debug.ino due to DEFINE processing
\*********************************************************************************************/

#define XDRV_127            127

#ifndef CPU_LOAD_CHECK
#define CPU_LOAD_CHECK      1                 // Seconds between each CPU_LOAD log
#endif

/*********************************************************************************************\
 * Debug commands
\*********************************************************************************************/

#define D_CMND_MEMDUMP   "MemDump"
#define D_CMND_CFGDUMP   "CfgDump"
#define D_CMND_CFGPEEK   "CfgPeek"
#define D_CMND_CFGPOKE   "CfgPoke"
#define D_CMND_SHOWHEAP  "ShowHeap"
#define D_CMND_CFGXOR    "CfgXor"
#define D_CMND_CPUCHECK  "CpuChk"
#define D_CMND_EXCEPTION "Exception"
#define D_CMND_FLASHDUMP "FlashDump"
#define D_CMND_FLASHMODE "FlashMode"
#define D_CMND_FREEMEM   "FreeMem"
#define D_CMND_HELP      "Help"
#define D_CMND_RTCDUMP   "RtcDump"
#define D_CMND_I2CWRITE  "I2CWrite"
#define D_CMND_I2CREAD   "I2CRead"
#define D_CMND_I2CSTRETCH "I2CStretch"
#define D_CMND_I2CCLOCK  "I2CClock"
#define D_CMND_SERBUFF   "SerBufSize"
#define D_CMND_SOSET     "SOSet"

const char kDebugCommands[] PROGMEM = "|"  // No prefix
  D_CMND_MEMDUMP "|" D_CMND_CFGDUMP "|" D_CMND_CFGPEEK "|" D_CMND_CFGPOKE "|"
#ifdef ESP8266
#ifdef UMM_INLINE_METRICS
  D_CMND_SHOWHEAP "|"
#endif
#endif
#ifdef USE_WEBSERVER
  D_CMND_CFGXOR "|"
#endif
  D_CMND_CPUCHECK "|" D_CMND_SERBUFF "|"
#ifdef DEBUG_THEO
  D_CMND_EXCEPTION "|"
#endif
  D_CMND_FLASHDUMP "|" D_CMND_FLASHMODE "|" D_CMND_FREEMEM"|" D_CMND_HELP "|" D_CMND_RTCDUMP "|"
#ifdef USE_I2C
  D_CMND_I2CWRITE "|" D_CMND_I2CREAD "|" D_CMND_I2CSTRETCH "|" D_CMND_I2CCLOCK "|"
#endif
  D_CMND_SOSET
  ;

void (* const DebugCommand[])(void) PROGMEM = {
  &CmndMemDump, &CmndCfgDump, &CmndCfgPeek, &CmndCfgPoke,
#ifdef ESP8266
#ifdef UMM_INLINE_METRICS
  &CmndShowHeap,
#endif
#endif
#ifdef USE_WEBSERVER
  &CmndCfgXor,
#endif
  &CmndCpuCheck, &CmndSerBufSize,
#ifdef DEBUG_THEO
  &CmndException,
#endif
  &CmndFlashDump, &CmndFlashMode, &CmndFreemem, &CmndHelp, &CmndRtcDump,
#ifdef USE_I2C
  &CmndI2cWrite, &CmndI2cRead, &CmndI2cStretch, &CmndI2cClock,
#endif
  &CmndSoSet
  };

uint32_t CPU_loops = 0;
uint32_t CPU_last_millis = 0;
uint32_t CPU_last_loop_time = 0;
uint8_t CPU_load_check = 0;
uint8_t CPU_show_freemem = 0;

/*******************************************************************************************/

#ifdef DEBUG_THEO
void ExceptionTest(uint8_t type)
{
/*
Exception (28):
epc1=0x4000bf64 epc2=0x00000000 epc3=0x00000000 excvaddr=0x00000007 depc=0x00000000

ctx: cont
sp: 3fff1f30 end: 3fff2840 offset: 01a0

>>>stack>>>
3fff20d0:  202c3573 756f7247 2c302070 646e4920
3fff20e0:  40236a6e 7954202c 45206570 00454358
3fff20f0:  00000010 00000007 00000000 3fff2180
3fff2100:  3fff2190 40107bfc 3fff3e4c 3fff22c0
3fff2110:  40261934 000000f0 3fff22c0 401004d8
3fff2120:  40238fcf 00000050 3fff2100 4021fc10
3fff2130:  3fff32bc 4021680c 3ffeade1 4021ff7d
3fff2140:  3fff2190 3fff2180 0000000c 7fffffff
3fff2150:  00000019 00000000 00000000 3fff21c0
3fff2160:  3fff23f3 3ffe8e08 00000000 4021ffb4
3fff2170:  3fff2190 3fff2180 0000000c 40201118
3fff2180:  3fff21c0 0000003c 3ffef840 00000007
3fff2190:  00000000 00000000 00000000 40201128
3fff21a0:  3fff23f3 000000f1 3fff23ec 4020fafb
3fff21b0:  3fff23f3 3fff21c0 3fff21d0 3fff23f6
3fff21c0:  00000000 3fff23fb 4022321b 00000000

Exception 28: LoadProhibited: A load referenced a page mapped with an attribute that does not permit loads
Decoding 14 results
0x40236a6e: ets_vsnprintf at ?? line ?
0x40107bfc: vsnprintf at C:\Data2\Arduino\arduino-1.8.1-esp-2.3.0\portable\packages\esp8266\hardware\esp8266\2.3.0\cores\esp8266/libc_replacements.c line 387
0x40261934: bignum_exptmod at ?? line ?
0x401004d8: malloc at C:\Data2\Arduino\arduino-1.8.1-esp-2.3.0\portable\packages\esp8266\hardware\esp8266\2.3.0\cores\esp8266\umm_malloc/umm_malloc.c line 1664
0x40238fcf: wifi_station_get_connect_status at ?? line ?
0x4021fc10: operator new[](unsigned int) at C:\Data2\Arduino\arduino-1.8.1-esp-2.3.0\portable\packages\esp8266\hardware\esp8266\2.3.0\cores\esp8266/abi.cpp line 57
0x4021680c: ESP8266WiFiSTAClass::status() at C:\Data2\Arduino\arduino-1.8.1-esp-2.3.0\portable\packages\esp8266\hardware\esp8266\2.3.0\libraries\ESP8266WiFi\src/ESP8266WiFiSTA.cpp line 569
0x4021ff7d: vsnprintf_P(char*, unsigned int, char const*, __va_list_tag) at C:\Data2\Arduino\arduino-1.8.1-esp-2.3.0\portable\packages\esp8266\hardware\esp8266\2.3.0\cores\esp8266/pgmspace.cpp line 146
0x4021ffb4: snprintf_P(char*, unsigned int, char const*, ...) at C:\Data2\Arduino\arduino-1.8.1-esp-2.3.0\portable\packages\esp8266\hardware\esp8266\2.3.0\cores\esp8266/pgmspace.cpp line 146
0x40201118: atol at C:\Data2\Arduino\arduino-1.8.1-esp-2.3.0\portable\packages\esp8266\hardware\esp8266\2.3.0\cores\esp8266/core_esp8266_noniso.c line 45
0x40201128: atoi at C:\Data2\Arduino\arduino-1.8.1-esp-2.3.0\portable\packages\esp8266\hardware\esp8266\2.3.0\cores\esp8266/core_esp8266_noniso.c line 45

00:00:08 MQTT: tele/tasmota/INFO3 = {"Started":"Fatal exception:28 flag:2 (EXCEPTION) epc1:0x4000bf64 epc2:0x00000000 epc3:0x00000000 excvaddr:0x00000007 depc:0x00000000"}
*/
  if (1 == type) {
    char svalue[10];
    snprintf_P(svalue, sizeof(svalue), PSTR("%s"), 7);  // Exception 28 as number in string (7 in excvaddr)
  }
/*
14:50:52 osWatch: FreeRam 25896, rssi 68, last_run 0
14:51:02 osWatch: FreeRam 25896, rssi 58, last_run 0
14:51:03 CMND: exception 2
14:51:12 osWatch: FreeRam 25360, rssi 60, last_run 8771
14:51:22 osWatch: FreeRam 25360, rssi 62, last_run 18771
14:51:32 osWatch: FreeRam 25360, rssi 62, last_run 28771
14:51:42 osWatch: FreeRam 25360, rssi 62, last_run 38771
14:51:42 osWatch: Warning, loop blocked. Restart now
*/
  if (2 == type) {
    while(1) delay(1000);  // this will trigger the os watch
  }
}

#endif  // DEBUG_THEO

/*******************************************************************************************/

void CpuLoadLoop(void)
{
  CPU_last_loop_time = millis();
  if (CPU_load_check && CPU_last_millis) {
    CPU_loops ++;
    if ((CPU_last_millis + (CPU_load_check *1000)) <= CPU_last_loop_time) {
#if defined(F_CPU) && (F_CPU == 160000000L)
      int CPU_load = 100 - ( (CPU_loops*(1 + 30*TasmotaGlobal.sleep)) / (CPU_load_check *800) );
      CPU_loops = CPU_loops / CPU_load_check;
      AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "FreeRam %d, CPU %d%%(160MHz), Loops/sec %d"), ESP.getFreeHeap(), CPU_load, CPU_loops);
#else
      int CPU_load = 100 - ( (CPU_loops*(1 + 30*TasmotaGlobal.sleep)) / (CPU_load_check *400) );
      CPU_loops = CPU_loops / CPU_load_check;
      AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "FreeRam %d, CPU %d%%(80MHz), Loops/sec %d"), ESP.getFreeHeap(), CPU_load, CPU_loops);
#endif
      CPU_last_millis = CPU_last_loop_time;
      CPU_loops = 0;
    }
  }
}

/*******************************************************************************************/

#ifdef ESP8266
// All version from core 2.4.2
// https://github.com/esp8266/Arduino/pull/5018
// https://github.com/esp8266/Arduino/pull/4553

extern "C" {
#include <cont.h>
  extern cont_t* g_pcont;
}

void DebugFreeMem(void) {
  register uint32_t *sp asm("a1");

  AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "FreeRam %d, FreeStack %d (%s)"), ESP.getFreeHeap(), 4 * (sp - g_pcont->stack), XdrvMailbox.data);
}

uint32_t FreeStack(void) {
  register uint32_t *sp asm("a1");
  return 4 * (sp - g_pcont->stack);
}

void AddLogMem(const char* function) {
  register uint32_t *sp asm("a1");
  AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "== %s FreeRam %d, FreeStack %d"), function, ESP.getFreeHeap(), 4 * (sp - g_pcont->stack));
}

#endif  // ESP8266
#ifdef ESP32

void DebugFreeMem(void) {
  register uint8_t *sp asm("a1");

  AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "FreeRam %d, FreeStack %d (%s)"), ESP.getFreeHeap(), sp - pxTaskGetStackStart(NULL), XdrvMailbox.data);
}

uint32_t FreeStack(void) {
  register uint8_t *sp asm("a1");
  return sp - pxTaskGetStackStart(NULL);
}

void AddLogMem(const char* function) {
  register uint8_t *sp asm("a1");
  AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "== %s FreeRam %d, FreeStack %d"), function, ESP.getFreeHeap(), sp - pxTaskGetStackStart(NULL));
}

#endif  // ESP8266 - ESP32

/*******************************************************************************************/
/*
  Usage:

  DebugTimer(1);

  Code snippit to time

  DebugTimer(0);
*/

void DebugTimer(bool start) {
  static uint32_t timer = 0;

  if (start) {
    timer = micros();
  } else {
    timer = ((int32_t)(micros() - timer));
    AddLog(LOG_LEVEL_DEBUG, PSTR("DBG: ExecTime %d uS"), timer);
  }
}

/*******************************************************************************************/

void DebugRtcDump(char* parms)
{
#ifdef ESP8266
  uint32_t CFG_COLS = 16;

  uint16_t idx;
  uint16_t maxrow;
  uint16_t row;
  uint16_t col;
  char *p;

  // |<--SDK data (256 bytes)-->|<--User data (512 bytes)-->|
  // 000 - 0FF: SDK
  //  000 - 01B: SDK rst_info
  // 100 - 2FF: User
  //  280 - 283: Tasmota RtcReboot   (Offset 100 (x 4bytes) - sizeof(RTCRBT) (x 4bytes))
  //  290 - 2EB: Tasmota RtcSettings (Offset 100 (x 4bytes))

  uint8_t buffer[768];
//  ESP.rtcUserMemoryRead(0, (uint32_t*)&buffer, sizeof(buffer));
  system_rtc_mem_read(0, (uint32_t*)&buffer, sizeof(buffer));

  maxrow = ((sizeof(buffer)+CFG_COLS)/CFG_COLS);

  uint16_t srow = strtol(parms, &p, 16) / CFG_COLS;
  uint16_t mrow = strtol(p, &p, 10);

//  AddLog(LOG_LEVEL_DEBUG, PSTR("Cnfg: Parms %s, Start row %d, rows %d"), parms, srow, mrow);

  if (0 == mrow) {  // Default only 8 lines
    mrow = 8;
  }
  if (srow > maxrow) {
    srow = maxrow - mrow;
  }
  if (mrow < (maxrow - srow)) {
    maxrow = srow + mrow;
  }

  char log_data[100];  // 020:  C7 2B 2E AB  70 E8 09 AE  C8 88 3D EA  7C FF 48 2F | +. p     = | H/|
  for (row = srow; row < maxrow; row++) {
    idx = row * CFG_COLS;
    snprintf_P(log_data, sizeof(log_data), PSTR("%03X:"), idx);
    for (col = 0; col < CFG_COLS; col++) {
      if (!(col%4)) {
        snprintf_P(log_data, sizeof(log_data), PSTR("%s "), log_data);
      }
      snprintf_P(log_data, sizeof(log_data), PSTR("%s %02X"), log_data, buffer[idx + col]);
    }
    snprintf_P(log_data, sizeof(log_data), PSTR("%s |"), log_data);
    for (col = 0; col < CFG_COLS; col++) {
//      if (!(col%4)) {
//        snprintf_P(log_data, sizeof(log_data), PSTR("%s "), log_data);
//      }
      snprintf_P(log_data, sizeof(log_data), PSTR("%s%c"), log_data, ((buffer[idx + col] > 0x20) && (buffer[idx + col] < 0x7F)) ? (char)buffer[idx + col] : ' ');
    }
    snprintf_P(log_data, sizeof(log_data), PSTR("%s|"), log_data);
    AddLogData(LOG_LEVEL_INFO, log_data);
  }
#endif  // ESP8266
}

/*******************************************************************************************/

void DebugDump(uint32_t start, uint32_t size) {
  uint32_t CFG_COLS = 32;

  uint32_t idx;
  uint32_t maxrow;
  uint32_t row;
  uint32_t col;
  char *p;

  uint8_t *buffer = (uint8_t *)(start);
  maxrow = ((size + CFG_COLS) / CFG_COLS);

  uint32_t srow = 0;
  uint32_t mrow = maxrow;

//  AddLog(LOG_LEVEL_DEBUG, PSTR("Cnfg: Parms %s, Start row %d, rows %d"), parms, srow, mrow);

  if (0 == mrow) {  // Default only 8 lines
    mrow = 8;
  }
  if (srow > maxrow) {
    srow = maxrow - mrow;
  }
  if (mrow < (maxrow - srow)) {
    maxrow = srow + mrow;
  }

  char log_data[150];  // 020:  C7 2B 2E AB  70 E8 09 AE  C8 88 3D EA  7C FF 48 2F  0E A7 D7 BF  02 0E D7 7D  C9 6F B9 3A  1D 01 3F 28 | +. p     = | H/       } o :  ?(|
  for (row = srow; row < maxrow; row++) {
    idx = row * CFG_COLS;
    snprintf_P(log_data, sizeof(log_data), PSTR("%08X:"), start + idx);
    for (col = 0; col < CFG_COLS; col++) {
      if (!(col%4)) {
        snprintf_P(log_data, sizeof(log_data), PSTR("%s "), log_data);
      }
      snprintf_P(log_data, sizeof(log_data), PSTR("%s %02X"), log_data, buffer[idx + col]);
    }
    snprintf_P(log_data, sizeof(log_data), PSTR("%s |"), log_data);
    for (col = 0; col < CFG_COLS; col++) {
//      if (!(col%4)) {
//        snprintf_P(log_data, sizeof(log_data), PSTR("%s "), log_data);
//      }
      snprintf_P(log_data, sizeof(log_data), PSTR("%s%c"), log_data, ((buffer[idx + col] > 0x20) && (buffer[idx + col] < 0x7F)) ? (char)buffer[idx + col] : ' ');
    }
    snprintf_P(log_data, sizeof(log_data), PSTR("%s|"), log_data);
    AddLogData(LOG_LEVEL_INFO, log_data);
    delay(1);
  }
}

void DebugCfgDump(char* parms)
{
  uint32_t CFG_COLS = 16;

  uint16_t idx;
  uint16_t maxrow;
  uint16_t row;
  uint16_t col;
  char *p;

  uint8_t *buffer = (uint8_t *) Settings;
  maxrow = ((sizeof(TSettings)+CFG_COLS)/CFG_COLS);

  uint16_t srow = strtol(parms, &p, 16) / CFG_COLS;
  uint16_t mrow = strtol(p, &p, 10);

//  AddLog(LOG_LEVEL_DEBUG, PSTR("Cnfg: Parms %s, Start row %d, rows %d"), parms, srow, mrow);

  if (0 == mrow) {  // Default only 8 lines
    mrow = 8;
  }
  if (srow > maxrow) {
    srow = maxrow - mrow;
  }
  if (mrow < (maxrow - srow)) {
    maxrow = srow + mrow;
  }

  char log_data[100];  // 020:  6D 75 73 31  3A 38 30 2F  61 70 69 2F  61 72 64 75 |mus1:80/api/ardu|
  for (row = srow; row < maxrow; row++) {
    idx = row * CFG_COLS;
    snprintf_P(log_data, sizeof(log_data), PSTR("%03X:"), idx);
    for (col = 0; col < CFG_COLS; col++) {
      if (!(col%4)) {
        snprintf_P(log_data, sizeof(log_data), PSTR("%s "), log_data);
      }
      snprintf_P(log_data, sizeof(log_data), PSTR("%s %02X"), log_data, buffer[idx + col]);
    }
    snprintf_P(log_data, sizeof(log_data), PSTR("%s |"), log_data);
    for (col = 0; col < CFG_COLS; col++) {
//      if (!(col%4)) {
//        snprintf_P(log_data, sizeof(log_data), PSTR("%s "), log_data);
//      }
      snprintf_P(log_data, sizeof(log_data), PSTR("%s%c"), log_data, ((buffer[idx + col] > 0x20) && (buffer[idx + col] < 0x7F)) ? (char)buffer[idx + col] : ' ');
    }
    snprintf_P(log_data, sizeof(log_data), PSTR("%s|"), log_data);
    AddLogData(LOG_LEVEL_INFO, log_data);
    delay(1);
  }
}

void DebugCfgPeek(char* parms)
{
  char *p;

  uint16_t address = strtol(parms, &p, 16);
  if (address > sizeof(TSettings)) address = sizeof(TSettings) -4;
  address = (address >> 2) << 2;

  uint8_t *buffer = (uint8_t *) Settings;
  uint8_t data8 = buffer[address];
  uint16_t data16 = (buffer[address +1] << 8) + buffer[address];
  uint32_t data32 = (buffer[address +3] << 24) + (buffer[address +2] << 16) + data16;

  char log_data[100];  // 000: 09 12 00 10 |    | 0x09 (9), 0x1209 (4617), 0x10001209 (268440073)
  snprintf_P(log_data, sizeof(log_data), PSTR("%03X:"), address);
  for (uint32_t i = 0; i < 4; i++) {
    snprintf_P(log_data, sizeof(log_data), PSTR("%s %02X"), log_data, buffer[address +i]);
  }
  snprintf_P(log_data, sizeof(log_data), PSTR("%s |"), log_data);
  for (uint32_t i = 0; i < 4; i++) {
    snprintf_P(log_data, sizeof(log_data), PSTR("%s%c"), log_data, ((buffer[address +i] > 0x20) && (buffer[address +i] < 0x7F)) ? (char)buffer[address +i] : ' ');
  }
  snprintf_P(log_data, sizeof(log_data), PSTR("%s| 0x%02X (%d), 0x%04X (%d), 0x%0LX (%lu)"), log_data, data8, data8, data16, data16, data32, data32);
  AddLogData(LOG_LEVEL_INFO, log_data);
}

void DebugCfgPoke(char* parms)
{
  char *p;

  uint16_t address = strtol(parms, &p, 16);
  if (address > sizeof(TSettings)) address = sizeof(TSettings) -4;
  address = (address >> 2) << 2;

  uint32_t data = strtol(p, &p, 16);

  uint8_t *buffer = (uint8_t *) Settings;
  uint32_t data32 = (buffer[address +3] << 24) + (buffer[address +2] << 16) + (buffer[address +1] << 8) + buffer[address];

  uint8_t *nbuffer = (uint8_t *) &data;
  for (uint32_t i = 0; i < 4; i++) { buffer[address +i] = nbuffer[+i]; }

  uint32_t ndata32 = (buffer[address +3] << 24) + (buffer[address +2] << 16) + (buffer[address +1] << 8) + buffer[address];

  AddLog(LOG_LEVEL_INFO, PSTR("%03X: 0x%0LX (%lu) poked to 0x%0LX (%lu)"), address, data32, data32, ndata32, ndata32);
}

void SetFlashMode(uint8_t mode)
{
#ifdef ESP8266
  uint8_t *_buffer;
  uint32_t address;

  address = 0;
  _buffer = new uint8_t[FLASH_SECTOR_SIZE];

  if (ESP.flashRead(address, (uint32_t*)_buffer, FLASH_SECTOR_SIZE)) {
    if (_buffer[2] != mode) {  // DOUT
      _buffer[2] = mode;
      if (ESP.flashEraseSector(address / FLASH_SECTOR_SIZE)) {
        ESP.flashWrite(address, (uint32_t*)_buffer, FLASH_SECTOR_SIZE);
      }
    }
  }
  delete[] _buffer;
#endif  // ESP8266
}

/*********************************************************************************************\
 * Commands
\*********************************************************************************************/

void CmndHelp(void)
{
  AddLog(LOG_LEVEL_INFO, PSTR("HLP: %s"), kDebugCommands);
  ResponseCmndDone();
}

void CmndRtcDump(void)
{
  DebugRtcDump(XdrvMailbox.data);
  ResponseCmndDone();
}

void CmndMemDump(void) {
  // MemDump 0x3fff1aa8 200
  if (XdrvMailbox.payload >= 0) {
    char *p;

    uint32_t start = strtol(XdrvMailbox.data, &p, 16);
    uint32_t size = strtol(p, &p, 10);
    if (0 == size) { size = 32; }
    DebugDump(start, size);
  }
  ResponseCmndDone();
}

void CmndCfgDump(void)
{
  DebugCfgDump(XdrvMailbox.data);
  ResponseCmndDone();
}

void CmndCfgPeek(void)
{
  DebugCfgPeek(XdrvMailbox.data);
  ResponseCmndDone();
}

void CmndCfgPoke(void)
{
  DebugCfgPoke(XdrvMailbox.data);
  ResponseCmndDone();
}

#ifdef ESP8266
#ifdef UMM_INLINE_METRICS
void CmndShowHeap(void) {
  system_show_malloc();
  ResponseCmndDone();
}
#endif
#endif

#ifdef USE_WEBSERVER
void CmndCfgXor(void)
{
  if (XdrvMailbox.data_len > 0) {
    config_xor_on_set = XdrvMailbox.payload;
  }
  char temp[10];
  snprintf_P(temp, sizeof(temp), PSTR("0x%02X"), config_xor_on_set);
  ResponseCmndChar(temp);
}
#endif  // USE_WEBSERVER

#ifdef DEBUG_THEO
void CmndException(void)
{
  if (XdrvMailbox.data_len > 0) { ExceptionTest(XdrvMailbox.payload); }
  ResponseCmndDone();
}
#endif  // DEBUG_THEO

void CmndCpuCheck(void)
{
  if (XdrvMailbox.data_len > 0) {
    CPU_load_check = XdrvMailbox.payload;
    CPU_last_millis = CPU_last_loop_time;
  }
  ResponseCmndNumber(CPU_load_check);
}

void CmndSerBufSize(void)
{
  if (XdrvMailbox.data_len > 0) {
    Serial.setRxBufferSize(XdrvMailbox.payload);
  }
#ifdef ESP8266
  ResponseCmndNumber(Serial.getRxBufferSize());
#endif  // ESP8266
#ifdef ESP32
  ResponseCmndDone();
#endif  // ESP32
}

void CmndFreemem(void)
{
  if (XdrvMailbox.data_len > 0) {
    CPU_show_freemem = XdrvMailbox.payload;
  }
  ResponseCmndNumber(CPU_show_freemem);
}

void CmndFlashMode(void)
{
  if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 3)) {
    SetFlashMode(XdrvMailbox.payload);
  }
  ResponseCmndNumber(ESP.getFlashChipMode());
}

uint32_t DebugSwap32(uint32_t x) {
	return	((x << 24) & 0xff000000 ) |
		((x <<  8) & 0x00ff0000 ) |
		((x >>  8) & 0x0000ff00 ) |
		((x >> 24) & 0x000000ff );
}

void CmndFlashDump(void)
{
#ifdef ESP8266
  // FlashDump
  // FlashDump 0xFF000
  // FlashDump 0xFC000 10
  const uint32_t flash_start = 0x40200000;  // Start address flash
  const uint8_t bytes_per_cols = 0x20;
  const uint32_t max = (EEPROM_LOCATION + 5) * SPI_FLASH_SEC_SIZE;  // 0x100000 for 1M flash, 0x400000 for 4M flash

  uint32_t start = flash_start;
  uint32_t rows = 8;

  if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= (max - bytes_per_cols))) {
    start += (XdrvMailbox.payload &0x7FFFFFFC);  // Fix exception as flash access is only allowed on 4 byte boundary

    char *p;
    uint32_t is_payload = strtol(XdrvMailbox.data, &p, 16);
    rows = strtol(p, &p, 10);
    if (0 == rows) { rows = 8; }
  }
  uint32_t end = start + (rows * bytes_per_cols);
  if ((end - flash_start) > max) {
    end = flash_start + max;
  }

  for (uint32_t pos = start; pos < end; pos += bytes_per_cols) {
    uint32_t* values = (uint32_t*)(pos);
    AddLog(LOG_LEVEL_INFO, PSTR("%06X:  %08X %08X %08X %08X  %08X %08X %08X %08X"), pos - flash_start,
      DebugSwap32(values[0]), DebugSwap32(values[1]), DebugSwap32(values[2]), DebugSwap32(values[3]),
      DebugSwap32(values[4]), DebugSwap32(values[5]), DebugSwap32(values[6]), DebugSwap32(values[7]));
  }
  ResponseCmndDone();
#endif  // ESP8266
}

#ifdef USE_I2C
void CmndI2cWrite(void)
{
  // I2cWrite <address>,<data>..
  if (TasmotaGlobal.i2c_enabled) {
    char* parms = XdrvMailbox.data;
    uint8_t buffer[100];
    uint32_t index = 0;

    char *p;
    char *data = strtok_r(parms, " ,", &p);
    while (data != NULL && index < sizeof(buffer)) {
      buffer[index++] = strtol(data, nullptr, 16);
      data = strtok_r(nullptr, " ,", &p);
    }

    if (index > 1) {
      AddLog(LOG_LEVEL_INFO, PSTR("DBG: CmndI2cWrite %*_H"), index, buffer);

      Wire.beginTransmission(buffer[0]);
      for (uint32_t i = 1; i < index; i++) {
        Wire.write(buffer[i]);
      }
      int result = Wire.endTransmission();
      AddLog(LOG_LEVEL_INFO, PSTR("I2C: Result %d"), result);
    }
  }
  ResponseCmndDone();
}

void CmndI2cRead(void)
{
  // I2cRead <address>,<size>
  if (TasmotaGlobal.i2c_enabled) {
    char* parms = XdrvMailbox.data;
    uint8_t buffer[100];
    uint32_t index = 0;

    char *p;
    char *data = strtok_r(parms, " ,", &p);
    while (data != NULL && index < sizeof(buffer)) {
      buffer[index++] = strtol(data, nullptr, 16);
      data = strtok_r(nullptr, " ,", &p);
    }

    if (index > 0) {
      uint8_t size = 1;
      if (index > 1) {
        size = buffer[1];
      }
      Wire.requestFrom(buffer[0], size);
      index = 0;
      while (Wire.available() && index < sizeof(buffer)) {
        buffer[index++] = Wire.read();
      }
      if (index > 0) {
        AddLog(LOG_LEVEL_INFO, PSTR("DBG: CmndI2cRead %*_H"), index, buffer);
      }
    }
  }
  ResponseCmndDone();
}

void CmndI2cStretch(void)
{
#ifdef ESP8266
  if (TasmotaGlobal.i2c_enabled && (XdrvMailbox.payload > 0)) {
    Wire.setClockStretchLimit(XdrvMailbox.payload);
  }
  ResponseCmndDone();
#endif  // ESP8266
}

void CmndI2cClock(void)
{
  if (TasmotaGlobal.i2c_enabled && (XdrvMailbox.payload > 0)) {
    Wire.setClock(XdrvMailbox.payload);
  }
  ResponseCmndDone();
}
#endif // USE_I2C

void CmndSoSet(void) {
  // Set option data as 32-bit hex value
  // SoSet1 0x5400401B
  if ((XdrvMailbox.index >= 1) && (XdrvMailbox.index <= 5)) {
    uint32_t option = XdrvMailbox.index;
    uint32_t data;
    switch (option) {
      case 1:
        data = Settings->flag.data;   // SetOption0 .. 31
        break;
      case 2:
        data = Settings->flag3.data;  // SetOption50 .. 81
        break;
      case 3:
        data = Settings->flag4.data;  // SetOption82 .. 113
        break;
      case 4:
        data = Settings->flag5.data;  // SetOption114 .. 145
        break;
      case 5:
        data = Settings->flag6.data;  // SetOption146 .. 177
    }
    if (XdrvMailbox.data_len > 0) {
      char *p;
      data = strtoul(XdrvMailbox.data, &p, 0);  // decimal, octal (0) or hex (0x)
      switch (option) {
        case 1:
          Settings->flag.data = data;   // SetOption0 .. 31
          break;
        case 2:
          Settings->flag3.data = data;  // SetOption50 .. 81
          break;
        case 3:
          Settings->flag4.data = data;  // SetOption82 .. 113
          break;
        case 4:
          Settings->flag5.data = data;  // SetOption114 .. 145
          break;
        case 5: Settings->flag6.data = data;  // SetOption146 .. 177
      }
//      TasmotaGlobal.restart_flag = 2;  // Activate some SetOptions
    }
    Response_P(PSTR("{\"%s%d\":\"%08X\"}"), XdrvMailbox.command, XdrvMailbox.index, data);
  }
}

/*********************************************************************************************\
 * Interface
\*********************************************************************************************/

bool Xdrv127(uint32_t function)
{
  bool result = false;

  switch (function) {
    case FUNC_LOOP:
      CpuLoadLoop();
      break;
    case FUNC_FREE_MEM:
      if (CPU_show_freemem) { DebugFreeMem(); }
      break;
    case FUNC_PRE_INIT:
      CPU_last_millis = millis();
      break;
    case FUNC_COMMAND:
      result = DecodeCommand(kDebugCommands, DebugCommand);
      break;
    case FUNC_ACTIVE:
      result = true;
      break;
  }
  return result;
}

#endif  // USE_DEBUG_DRIVER