|
@@ -356,6 +356,50 @@ static volatile bool uploadFailed = false;
|
|
|
static volatile uint32_t uploadBytes = 0;
|
|
static volatile uint32_t uploadBytes = 0;
|
|
|
static volatile uint32_t uploadCrc = 0;
|
|
static volatile uint32_t uploadCrc = 0;
|
|
|
static char uploadErrorMsg[80] = "";
|
|
static char uploadErrorMsg[80] = "";
|
|
|
|
|
+static uint8_t s_uploadFirstBytes[16];
|
|
|
|
|
+static uint8_t s_uploadFirstCount = 0;
|
|
|
|
|
+
|
|
|
|
|
+//=============================================================
|
|
|
|
|
+// Capture-to-EEPROM state
|
|
|
|
|
+// captureBuffer holds the selfboot image built during each TCP
|
|
|
|
|
+// session. It is populated by captureWrite() for every sl=0
|
|
|
|
|
+// DSP write and is committed to 24C256 by handleSaveEeprom().
|
|
|
|
|
+//=============================================================
|
|
|
|
|
+static constexpr int CAPTURE_MAX_SIZE = 28 * 1024;
|
|
|
|
|
+static uint8_t captureBuffer[CAPTURE_MAX_SIZE];
|
|
|
|
|
+static int captureLen = 0;
|
|
|
|
|
+static bool captureReady = false;
|
|
|
|
|
+
|
|
|
|
|
+// Encode one sl=0 DSP write into the selfboot EEPROM format
|
|
|
|
|
+// (Analog Devices datasheet Table 19: [0x01][len_hi][len_lo][0x00][addr_hi][addr_lo][data...])
|
|
|
|
|
+// and append it to captureBuffer. Splits large writes into 32-word chunks.
|
|
|
|
|
+static void captureWrite(uint16_t address, const uint8_t* data, int dataLen)
|
|
|
|
|
+{
|
|
|
|
|
+ int wordSize = 4; // parameter RAM
|
|
|
|
|
+ if (address >= 0x0400 && address <= 0x07FF) wordSize = 5; // program RAM
|
|
|
|
|
+ else if (address >= 0x0800) wordSize = 1; // hardware regs
|
|
|
|
|
+
|
|
|
|
|
+ int maxChunk = 32 * wordSize;
|
|
|
|
|
+ int offset = 0;
|
|
|
|
|
+ while (offset < dataLen) {
|
|
|
|
|
+ int bytes = min(dataLen - offset, maxChunk);
|
|
|
|
|
+ uint16_t addr = address + (uint16_t)(offset / wordSize);
|
|
|
|
|
+ if (captureLen + 6 + bytes + 2 > CAPTURE_MAX_SIZE) {
|
|
|
|
|
+ Serial.println("CAP: buffer full");
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ uint16_t count = (uint16_t)(bytes + 3); // chipAddr(1) + regAddr(2) + data
|
|
|
|
|
+ captureBuffer[captureLen++] = 0x01;
|
|
|
|
|
+ captureBuffer[captureLen++] = (uint8_t)(count >> 8);
|
|
|
|
|
+ captureBuffer[captureLen++] = (uint8_t)(count & 0xFF);
|
|
|
|
|
+ captureBuffer[captureLen++] = 0x00;
|
|
|
|
|
+ captureBuffer[captureLen++] = (uint8_t)(addr >> 8);
|
|
|
|
|
+ captureBuffer[captureLen++] = (uint8_t)(addr & 0xFF);
|
|
|
|
|
+ memcpy(&captureBuffer[captureLen], data + offset, bytes);
|
|
|
|
|
+ captureLen += bytes;
|
|
|
|
|
+ offset += bytes;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
//=============================================================
|
|
//=============================================================
|
|
|
// HTTP handlers
|
|
// HTTP handlers
|
|
@@ -418,7 +462,7 @@ static void handleUploadStream() {
|
|
|
|
|
|
|
|
if (up.status == UPLOAD_FILE_START) {
|
|
if (up.status == UPLOAD_FILE_START) {
|
|
|
uploadActive = true; uploadFailed = false; uploadBytes = 0; uploadCrc = 0;
|
|
uploadActive = true; uploadFailed = false; uploadBytes = 0; uploadCrc = 0;
|
|
|
- uploadErrorMsg[0] = '\0';
|
|
|
|
|
|
|
+ uploadErrorMsg[0] = '\0'; s_uploadFirstCount = 0;
|
|
|
uploadVerify = httpServer.hasArg("verify");
|
|
uploadVerify = httpServer.hasArg("verify");
|
|
|
Serial.println(); Serial.print("HTTP upload start: "); Serial.println(up.filename);
|
|
Serial.println(); Serial.print("HTTP upload start: "); Serial.println(up.filename);
|
|
|
Serial.print("Verify: "); Serial.println(uploadVerify ? "yes" : "no");
|
|
Serial.print("Verify: "); Serial.println(uploadVerify ? "yes" : "no");
|
|
@@ -442,6 +486,10 @@ static void handleUploadStream() {
|
|
|
"EEPROM I2C write failed at offset %u", (uint32_t)uploadBytes);
|
|
"EEPROM I2C write failed at offset %u", (uint32_t)uploadBytes);
|
|
|
Serial.println(uploadErrorMsg); uploadFailed = true; return;
|
|
Serial.println(uploadErrorMsg); uploadFailed = true; return;
|
|
|
}
|
|
}
|
|
|
|
|
+ if (s_uploadFirstCount == 0) {
|
|
|
|
|
+ s_uploadFirstCount = up.currentSize < 16 ? up.currentSize : 16;
|
|
|
|
|
+ memcpy(s_uploadFirstBytes, up.buf, s_uploadFirstCount);
|
|
|
|
|
+ }
|
|
|
uploadCrc = esp_rom_crc32_le(uploadCrc, up.buf, up.currentSize);
|
|
uploadCrc = esp_rom_crc32_le(uploadCrc, up.buf, up.currentSize);
|
|
|
uploadBytes += up.currentSize;
|
|
uploadBytes += up.currentSize;
|
|
|
ledMagenta();
|
|
ledMagenta();
|
|
@@ -450,6 +498,20 @@ static void handleUploadStream() {
|
|
|
Serial.print("HTTP upload end, bytes="); Serial.println((uint32_t)uploadBytes);
|
|
Serial.print("HTTP upload end, bytes="); Serial.println((uint32_t)uploadBytes);
|
|
|
if (uploadVerify && !uploadFailed) {
|
|
if (uploadVerify && !uploadFailed) {
|
|
|
Serial.println("Verify start (CRC32)...");
|
|
Serial.println("Verify start (CRC32)...");
|
|
|
|
|
+ // Diagnostic: compare first bytes uploaded vs first bytes in EEPROM
|
|
|
|
|
+ if (s_uploadFirstCount > 0) {
|
|
|
|
|
+ uint8_t eepFirst[16];
|
|
|
|
|
+ Serial.print("Uploaded[0]: ");
|
|
|
|
|
+ for (uint8_t i = 0; i < s_uploadFirstCount; i++) Serial.printf("0x%02X ", s_uploadFirstBytes[i]);
|
|
|
|
|
+ Serial.println();
|
|
|
|
|
+ if (i2cReadBlock(EEPROM_7BIT, 0, eepFirst, s_uploadFirstCount)) {
|
|
|
|
|
+ Serial.print("EEPROM [0]: ");
|
|
|
|
|
+ for (uint8_t i = 0; i < s_uploadFirstCount; i++) Serial.printf("0x%02X ", eepFirst[i]);
|
|
|
|
|
+ Serial.println();
|
|
|
|
|
+ } else {
|
|
|
|
|
+ Serial.println("EEPROM [0]: read failed");
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
uint32_t crc = 0;
|
|
uint32_t crc = 0;
|
|
|
static uint8_t tmp[256];
|
|
static uint8_t tmp[256];
|
|
|
uint32_t remaining = uploadBytes; uint16_t addr = 0;
|
|
uint32_t remaining = uploadBytes; uint16_t addr = 0;
|
|
@@ -706,6 +768,42 @@ static void handleOtaStream() {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+//=============================================================
|
|
|
|
|
+// Capture-to-EEPROM HTTP handlers
|
|
|
|
|
+//=============================================================
|
|
|
|
|
+static void handleCaptureStatus() {
|
|
|
|
|
+ char buf[64];
|
|
|
|
|
+ snprintf(buf, sizeof(buf), "{\"ready\":%s,\"bytes\":%d}",
|
|
|
|
|
+ captureReady ? "true" : "false", captureLen);
|
|
|
|
|
+ httpServer.send(200, "application/json", buf);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+static void handleSaveEeprom() {
|
|
|
|
|
+ if (!captureReady || captureLen == 0) {
|
|
|
|
|
+ httpServer.send(400, "application/json",
|
|
|
|
|
+ "{\"ok\":false,\"error\":\"No capture — connect SigmaStudio and download first.\"}");
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ Serial.printf("Save to EEPROM: %d bytes\n", captureLen);
|
|
|
|
|
+ // Append selfboot end marker
|
|
|
|
|
+ captureBuffer[captureLen] = 0x00;
|
|
|
|
|
+ captureBuffer[captureLen + 1] = 0x00;
|
|
|
|
|
+ int totalLen = captureLen + 2;
|
|
|
|
|
+ ledYellow();
|
|
|
|
|
+ bool ok = eepromWriteBlock(0, captureBuffer, (uint16_t)totalLen);
|
|
|
|
|
+ ledOff();
|
|
|
|
|
+ if (ok) {
|
|
|
|
|
+ char buf[64];
|
|
|
|
|
+ snprintf(buf, sizeof(buf), "{\"ok\":true,\"bytes\":%d}", totalLen);
|
|
|
|
|
+ httpServer.send(200, "application/json", buf);
|
|
|
|
|
+ Serial.printf("Save to EEPROM OK: %d bytes written\n", totalLen);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ httpServer.send(500, "application/json",
|
|
|
|
|
+ "{\"ok\":false,\"error\":\"EEPROM I2C write failed\"}");
|
|
|
|
|
+ Serial.println("Save to EEPROM FAILED");
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
//=============================================================
|
|
//=============================================================
|
|
|
// Setup
|
|
// Setup
|
|
|
//=============================================================
|
|
//=============================================================
|
|
@@ -782,8 +880,10 @@ void setup() {
|
|
|
httpServer.on("/gpio", HTTP_POST, handleGpioSet);
|
|
httpServer.on("/gpio", HTTP_POST, handleGpioSet);
|
|
|
httpServer.on("/wifi_reset", HTTP_POST, handleWifiReset);
|
|
httpServer.on("/wifi_reset", HTTP_POST, handleWifiReset);
|
|
|
httpServer.on("/params", HTTP_GET, handleParamsPage);
|
|
httpServer.on("/params", HTTP_GET, handleParamsPage);
|
|
|
- httpServer.on("/param", HTTP_GET, handleParamGet);
|
|
|
|
|
- httpServer.on("/param", HTTP_POST, handleParamSet);
|
|
|
|
|
|
|
+ httpServer.on("/param", HTTP_GET, handleParamGet);
|
|
|
|
|
+ httpServer.on("/param", HTTP_POST, handleParamSet);
|
|
|
|
|
+ httpServer.on("/capture_status", HTTP_GET, handleCaptureStatus);
|
|
|
|
|
+ httpServer.on("/save_eeprom", HTTP_POST, handleSaveEeprom);
|
|
|
httpServer.onNotFound([]() {
|
|
httpServer.onNotFound([]() {
|
|
|
String uri = httpServer.uri();
|
|
String uri = httpServer.uri();
|
|
|
if (streamFromFS(uri)) return;
|
|
if (streamFromFS(uri)) return;
|
|
@@ -813,6 +913,8 @@ static void handleTcpBridgeClient(WiFiClient& client)
|
|
|
{
|
|
{
|
|
|
Serial.println("TCP new connection");
|
|
Serial.println("TCP new connection");
|
|
|
DSPWriter::resetSafeload();
|
|
DSPWriter::resetSafeload();
|
|
|
|
|
+ captureLen = 0; // fresh capture for this session
|
|
|
|
|
+ captureReady = false;
|
|
|
|
|
|
|
|
int writeIndex = 0; // next free slot in dataBuffer
|
|
int writeIndex = 0; // next free slot in dataBuffer
|
|
|
int readIndex = 0; // start of current unprocessed command
|
|
int readIndex = 0; // start of current unprocessed command
|
|
@@ -947,6 +1049,19 @@ static void handleTcpBridgeClient(WiFiClient& client)
|
|
|
writeOk = DSPWriter::writeRegisterBlock(regAddress, writeHeader.dataLen,
|
|
writeOk = DSPWriter::writeRegisterBlock(regAddress, writeHeader.dataLen,
|
|
|
&dataBuffer[readIndex], registerSize);
|
|
&dataBuffer[readIndex], registerSize);
|
|
|
if (!writeOk) Serial.println("TCP DSP block write failed");
|
|
if (!writeOk) Serial.println("TCP DSP block write failed");
|
|
|
|
|
+ // Capture sl=0 writes into selfboot image (captureWrite before DSPRUN check
|
|
|
|
|
+ // so the final "start DSP" CoreRegister write is included in the image).
|
|
|
|
|
+ if (writeOk && !captureReady) {
|
|
|
|
|
+ captureWrite(writeHeader.address, &dataBuffer[readIndex], (int)writeHeader.dataLen);
|
|
|
|
|
+ }
|
|
|
|
|
+ // Detect DSPRUN bit going high = download sequence complete
|
|
|
|
|
+ if (writeHeader.address == dspRegister::CoreRegister && writeHeader.dataLen >= 2) {
|
|
|
|
|
+ bool running = (dataBuffer[readIndex + writeHeader.dataLen - 1] & 0x01) != 0;
|
|
|
|
|
+ if (running && !captureReady) {
|
|
|
|
|
+ captureReady = true;
|
|
|
|
|
+ Serial.printf("CAP: capture ready — %d bytes\n", captureLen);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
readIndex += writeHeader.dataLen;
|
|
readIndex += writeHeader.dataLen;
|