Fix terminal export import
This commit is contained in:
parent
1047d846f9
commit
14ac4401ce
62
UIThread.cpp
62
UIThread.cpp
@ -60,13 +60,15 @@ void readEncoder() {
|
|||||||
|
|
||||||
void saveGridToEEPROM() {
|
void saveGridToEEPROM() {
|
||||||
if (!globalSynth) return;
|
if (!globalSynth) return;
|
||||||
uint8_t buf[SynthEngine::SERIALIZED_GRID_SIZE];
|
uint8_t buf[SynthEngine::MAX_SERIALIZED_GRID_SIZE];
|
||||||
globalSynth->exportGrid(buf);
|
size_t size = globalSynth->exportGrid(buf);
|
||||||
|
|
||||||
EEPROM.write(0, 'N');
|
EEPROM.write(0, 'N');
|
||||||
EEPROM.write(1, 'S');
|
EEPROM.write(1, 'S');
|
||||||
for (size_t i = 0; i < sizeof(buf); i++) {
|
EEPROM.write(2, (size >> 8) & 0xFF);
|
||||||
EEPROM.write(2 + i, buf[i]);
|
EEPROM.write(3, size & 0xFF);
|
||||||
|
for (size_t i = 0; i < size; i++) {
|
||||||
|
EEPROM.write(4 + i, buf[i]);
|
||||||
}
|
}
|
||||||
EEPROM.commit();
|
EEPROM.commit();
|
||||||
}
|
}
|
||||||
@ -74,11 +76,14 @@ void saveGridToEEPROM() {
|
|||||||
void loadGridFromEEPROM() {
|
void loadGridFromEEPROM() {
|
||||||
if (!globalSynth) return;
|
if (!globalSynth) return;
|
||||||
if (EEPROM.read(0) == 'N' && EEPROM.read(1) == 'S') {
|
if (EEPROM.read(0) == 'N' && EEPROM.read(1) == 'S') {
|
||||||
uint8_t buf[SynthEngine::SERIALIZED_GRID_SIZE];
|
size_t size = (EEPROM.read(2) << 8) | EEPROM.read(3);
|
||||||
for (size_t i = 0; i < sizeof(buf); i++) {
|
if (size > SynthEngine::MAX_SERIALIZED_GRID_SIZE) size = SynthEngine::MAX_SERIALIZED_GRID_SIZE;
|
||||||
buf[i] = EEPROM.read(2 + i);
|
|
||||||
|
uint8_t buf[SynthEngine::MAX_SERIALIZED_GRID_SIZE];
|
||||||
|
for (size_t i = 0; i < size; i++) {
|
||||||
|
buf[i] = EEPROM.read(4 + i);
|
||||||
}
|
}
|
||||||
globalSynth->importGrid(buf);
|
globalSynth->importGrid(buf, size);
|
||||||
} else {
|
} else {
|
||||||
globalSynth->loadPreset(1); // Default to preset 1
|
globalSynth->loadPreset(1); // Default to preset 1
|
||||||
}
|
}
|
||||||
@ -104,7 +109,7 @@ void setupUI() {
|
|||||||
display.display();
|
display.display();
|
||||||
|
|
||||||
// Initialize EEPROM
|
// Initialize EEPROM
|
||||||
EEPROM.begin(512);
|
EEPROM.begin(1024);
|
||||||
|
|
||||||
// Check for safety clear (Button held on startup)
|
// Check for safety clear (Button held on startup)
|
||||||
if (digitalRead(PIN_ENC_SW) == LOW) {
|
if (digitalRead(PIN_ENC_SW) == LOW) {
|
||||||
@ -269,13 +274,14 @@ void drawUI() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void checkSerial() {
|
void checkSerial() {
|
||||||
static int state = 0; // 0: Header, 1: Data
|
static int state = 0; // 0: Header, 1: Count, 2: Data, 3: EndCount
|
||||||
static int headerIdx = 0;
|
static int headerIdx = 0;
|
||||||
static const char* header = "NSGRID";
|
static const char* header = "NSGRID";
|
||||||
static int loadHeaderIdx = 0;
|
static int loadHeaderIdx = 0;
|
||||||
static const char* loadHeader = "NSLOAD";
|
static const char* loadHeader = "NSLOAD";
|
||||||
static uint8_t buffer[SynthEngine::SERIALIZED_GRID_SIZE];
|
static uint8_t buffer[SynthEngine::MAX_SERIALIZED_GRID_SIZE];
|
||||||
static int bufferIdx = 0;
|
static int bufferIdx = 0;
|
||||||
|
static uint8_t elementCount = 0;
|
||||||
|
|
||||||
while (Serial.available()) {
|
while (Serial.available()) {
|
||||||
uint8_t b = Serial.read();
|
uint8_t b = Serial.read();
|
||||||
@ -283,7 +289,7 @@ void checkSerial() {
|
|||||||
if (b == header[headerIdx]) {
|
if (b == header[headerIdx]) {
|
||||||
headerIdx++;
|
headerIdx++;
|
||||||
if (headerIdx == 6) {
|
if (headerIdx == 6) {
|
||||||
state = 1;
|
state = 1; // Expect count next
|
||||||
bufferIdx = 0;
|
bufferIdx = 0;
|
||||||
headerIdx = 0;
|
headerIdx = 0;
|
||||||
loadHeaderIdx = 0;
|
loadHeaderIdx = 0;
|
||||||
@ -298,10 +304,10 @@ void checkSerial() {
|
|||||||
loadHeaderIdx++;
|
loadHeaderIdx++;
|
||||||
if (loadHeaderIdx == 6) {
|
if (loadHeaderIdx == 6) {
|
||||||
if (globalSynth) {
|
if (globalSynth) {
|
||||||
uint8_t buf[SynthEngine::SERIALIZED_GRID_SIZE];
|
static uint8_t exportBuf[SynthEngine::MAX_SERIALIZED_GRID_SIZE];
|
||||||
globalSynth->exportGrid(buf);
|
size_t size = globalSynth->exportGrid(exportBuf);
|
||||||
Serial.write("NSGRID", 6);
|
Serial.write("NSGRID", 6);
|
||||||
Serial.write(buf, sizeof(buf));
|
Serial.write(exportBuf, size);
|
||||||
Serial.flush();
|
Serial.flush();
|
||||||
}
|
}
|
||||||
loadHeaderIdx = 0;
|
loadHeaderIdx = 0;
|
||||||
@ -312,11 +318,29 @@ void checkSerial() {
|
|||||||
if (b == 'N') loadHeaderIdx = 1;
|
if (b == 'N') loadHeaderIdx = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (state == 1) {
|
} else if (state == 1) { // Count
|
||||||
|
elementCount = b;
|
||||||
|
if (1 + elementCount * 5 > sizeof(buffer)) {
|
||||||
|
state = 0;
|
||||||
|
bufferIdx = 0;
|
||||||
|
Serial.println(F("ERROR: Grid too large"));
|
||||||
|
} else {
|
||||||
|
buffer[bufferIdx++] = b;
|
||||||
|
state = 2;
|
||||||
|
}
|
||||||
|
} else if (state == 2) { // Data
|
||||||
buffer[bufferIdx++] = b;
|
buffer[bufferIdx++] = b;
|
||||||
if (bufferIdx == SynthEngine::SERIALIZED_GRID_SIZE) {
|
if (bufferIdx == 1 + elementCount * 5) {
|
||||||
if (globalSynth) {
|
state = 3;
|
||||||
globalSynth->importGrid(buffer);
|
}
|
||||||
|
} else if (state == 3) { // End Count
|
||||||
|
buffer[bufferIdx++] = b;
|
||||||
|
if (globalSynth) {
|
||||||
|
int result = globalSynth->importGrid(buffer, bufferIdx);
|
||||||
|
if (result != 0) {
|
||||||
|
Serial.print("CRC ERROR "); Serial.println(result);
|
||||||
|
globalSynth->clearGrid();
|
||||||
|
} else {
|
||||||
saveGridToEEPROM();
|
saveGridToEEPROM();
|
||||||
Serial.println(F("OK: Grid Received"));
|
Serial.println(F("OK: Grid Received"));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,6 +15,7 @@
|
|||||||
#if !defined(_WIN32)
|
#if !defined(_WIN32)
|
||||||
#include <sys/select.h>
|
#include <sys/select.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <termios.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// --- Configuration ---
|
// --- Configuration ---
|
||||||
@ -98,37 +99,60 @@ void checkSerialInput(FILE* serialPort) {
|
|||||||
return;
|
return;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
printf("Grid import maybe?\n");
|
|
||||||
|
|
||||||
static int state = 0;
|
static int state = 0;
|
||||||
static int headerIdx = 0;
|
static int headerIdx = 0;
|
||||||
static const char* header = "NSGRID";
|
static const char* header = "NSGRID";
|
||||||
static uint8_t buffer[SynthEngine::SERIALIZED_GRID_SIZE];
|
static uint8_t buffer[SynthEngine::MAX_SERIALIZED_GRID_SIZE];
|
||||||
static int bufferIdx = 0;
|
static int bufferIdx = 0;
|
||||||
|
static uint8_t elementCount = 0;
|
||||||
|
|
||||||
for (ssize_t i = 0; i < n; ++i) {
|
for (ssize_t i = 0; i < n; ++i) {
|
||||||
uint8_t b = buf[i];
|
uint8_t b = buf[i];
|
||||||
if (state == 0) {
|
if (state == 0) {
|
||||||
if (b == header[headerIdx]) {
|
if (b == (uint8_t)header[headerIdx]) {
|
||||||
headerIdx++;
|
headerIdx++;
|
||||||
if (headerIdx == 6) {
|
if (headerIdx == 6) {
|
||||||
state = 1;
|
state = 1; // Expect count
|
||||||
bufferIdx = 0;
|
bufferIdx = 0;
|
||||||
headerIdx = 0;
|
headerIdx = 0;
|
||||||
printf("Grid import starting.\n");
|
printf("Grid import starting.\n");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
if (headerIdx > 0) {
|
||||||
|
printf("Header mismatch at index %d. Received: %02X\n", headerIdx, b);
|
||||||
|
}
|
||||||
|
|
||||||
headerIdx = 0;
|
headerIdx = 0;
|
||||||
if (b == 'N') headerIdx = 1;
|
if (b == 'N') headerIdx = 1;
|
||||||
}
|
}
|
||||||
} else if (state == 1) {
|
} else if (state == 1) { // Count
|
||||||
buffer[bufferIdx++] = b;
|
elementCount = b;
|
||||||
if (bufferIdx == SynthEngine::SERIALIZED_GRID_SIZE) {
|
printf("Grid element count: %d\n", elementCount);
|
||||||
engine.importGrid(buffer);
|
if (1 + elementCount * 5 + 1 > sizeof(buffer)) {
|
||||||
printf("Grid imported from serial.\n");
|
|
||||||
state = 0;
|
state = 0;
|
||||||
bufferIdx = 0;
|
bufferIdx = 0;
|
||||||
|
printf("ERROR: Grid too large (count: %d)\n", elementCount);
|
||||||
|
} else {
|
||||||
|
buffer[bufferIdx++] = b;
|
||||||
|
state = (elementCount == 0) ? 3 : 2;
|
||||||
}
|
}
|
||||||
|
} else if (state == 2) { // Data
|
||||||
|
buffer[bufferIdx++] = b;
|
||||||
|
if (bufferIdx == 1 + elementCount * 5) {
|
||||||
|
state = 3;
|
||||||
|
}
|
||||||
|
} else if (state == 3) { // End Count
|
||||||
|
buffer[bufferIdx++] = b;
|
||||||
|
printf("Grid import finishing. Total bytes: %d. End count: %d\n", bufferIdx, b);
|
||||||
|
int result = engine.importGrid(buffer, bufferIdx);
|
||||||
|
if (result != 0) {
|
||||||
|
printf("Grid import failed: CRC ERROR %d\n", result);
|
||||||
|
engine.clearGrid();
|
||||||
|
} else {
|
||||||
|
printf("Grid imported from serial successfully.\n");
|
||||||
|
}
|
||||||
|
state = 0;
|
||||||
|
bufferIdx = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -986,7 +1010,45 @@ int main(int argc, char* argv[]) {
|
|||||||
|
|
||||||
if (argc > 1) {
|
if (argc > 1) {
|
||||||
serialPort = fopen(argv[1], "r+b");
|
serialPort = fopen(argv[1], "r+b");
|
||||||
if (serialPort) printf("Opened serial port: %s\n", argv[1]);
|
if (serialPort) {
|
||||||
|
printf("Opened serial port: %s\n", argv[1]);
|
||||||
|
#if !defined(_WIN32)
|
||||||
|
int fd = fileno(serialPort);
|
||||||
|
struct termios tty;
|
||||||
|
if (tcgetattr(fd, &tty) != 0) {
|
||||||
|
printf("Error from tcgetattr\n");
|
||||||
|
} else {
|
||||||
|
cfsetospeed(&tty, B115200);
|
||||||
|
cfsetispeed(&tty, B115200);
|
||||||
|
|
||||||
|
tty.c_cflag &= ~PARENB; // Clear parity bit, disabling parity (most common)
|
||||||
|
tty.c_cflag &= ~CSTOPB; // Clear stop field, only one stop bit used in communication (most common)
|
||||||
|
tty.c_cflag &= ~CSIZE; // Clear all bits that set the data size
|
||||||
|
tty.c_cflag |= CS8; // 8 bits per byte (most common)
|
||||||
|
tty.c_cflag &= ~CRTSCTS; // Disable RTS/CTS hardware flow control (most common)
|
||||||
|
tty.c_cflag |= CREAD | CLOCAL; // Turn on READ & ignore ctrl lines (CLOCAL = 1)
|
||||||
|
|
||||||
|
tty.c_lflag &= ~ICANON; // Disable canonical mode
|
||||||
|
tty.c_lflag &= ~ECHO; // Disable echo
|
||||||
|
tty.c_lflag &= ~ECHOE; // Disable erasure
|
||||||
|
tty.c_lflag &= ~ECHONL; // Disable new-line echo
|
||||||
|
tty.c_lflag &= ~ISIG; // Disable interpretation of INTR, QUIT and SUSP
|
||||||
|
|
||||||
|
tty.c_iflag &= ~(IXON | IXOFF | IXANY); // Turn off s/w flow ctrl
|
||||||
|
tty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL); // Disable any special handling of received bytes
|
||||||
|
|
||||||
|
tty.c_oflag &= ~OPOST; // Prevent special interpretation of output bytes (e.g. newline chars)
|
||||||
|
tty.c_oflag &= ~ONLCR; // Prevent conversion of newline to carriage return/line feed
|
||||||
|
|
||||||
|
tty.c_cc[VTIME] = 0; // No blocking with timeout
|
||||||
|
tty.c_cc[VMIN] = 0; // Non-blocking read
|
||||||
|
|
||||||
|
if (tcsetattr(fd, TCSANOW, &tty) != 0) {
|
||||||
|
printf("Error from tcsetattr\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
else printf("Failed to open serial port: %s\n", argv[1]);
|
else printf("Failed to open serial port: %s\n", argv[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1230,10 +1292,10 @@ int main(int argc, char* argv[]) {
|
|||||||
my >= exportButtonRect.y && my <= exportButtonRect.y + exportButtonRect.h) {
|
my >= exportButtonRect.y && my <= exportButtonRect.y + exportButtonRect.h) {
|
||||||
|
|
||||||
if (serialPort) {
|
if (serialPort) {
|
||||||
uint8_t buf[SynthEngine::SERIALIZED_GRID_SIZE];
|
uint8_t buf[SynthEngine::MAX_SERIALIZED_GRID_SIZE];
|
||||||
engine.exportGrid(buf);
|
size_t size = engine.exportGrid(buf);
|
||||||
fwrite("NSGRID", 1, 6, serialPort);
|
fwrite("NSGRID", 1, 6, serialPort);
|
||||||
fwrite(buf, 1, sizeof(buf), serialPort);
|
fwrite(buf, 1, size, serialPort);
|
||||||
fflush(serialPort);
|
fflush(serialPort);
|
||||||
printf("Grid exported to serial. Waiting for response...\n");
|
printf("Grid exported to serial. Waiting for response...\n");
|
||||||
|
|
||||||
|
|||||||
@ -42,37 +42,77 @@ SynthEngine::SynthEngine(uint32_t sampleRate)
|
|||||||
SynthEngine::~SynthEngine() {
|
SynthEngine::~SynthEngine() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void SynthEngine::exportGrid(uint8_t* buffer) {
|
size_t SynthEngine::exportGrid(uint8_t* buffer) {
|
||||||
SynthLockGuard<SynthMutex> lock(gridMutex);
|
SynthLockGuard<SynthMutex> lock(gridMutex);
|
||||||
size_t idx = 0;
|
uint8_t count = 0;
|
||||||
for(int y=0; y<GRID_H; ++y) {
|
for(int y=0; y<GRID_H; ++y) {
|
||||||
for(int x=0; x<GRID_W; ++x) {
|
for(int x=0; x<GRID_W; ++x) {
|
||||||
GridCell& c = grid[x][y];
|
if (grid[x][y].type != GridCell::EMPTY) count++;
|
||||||
buffer[idx++] = (uint8_t)c.type;
|
|
||||||
buffer[idx++] = (uint8_t)(c.param * 255.0f);
|
|
||||||
buffer[idx++] = (uint8_t)c.rotation;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void SynthEngine::importGrid(const uint8_t* buffer) {
|
|
||||||
SynthLockGuard<SynthMutex> lock(gridMutex);
|
|
||||||
|
|
||||||
size_t idx = 0;
|
size_t idx = 0;
|
||||||
|
buffer[idx++] = count;
|
||||||
|
|
||||||
for(int y=0; y<GRID_H; ++y) {
|
for(int y=0; y<GRID_H; ++y) {
|
||||||
for(int x=0; x<GRID_W; ++x) {
|
for(int x=0; x<GRID_W; ++x) {
|
||||||
GridCell& c = grid[x][y];
|
GridCell& c = grid[x][y];
|
||||||
uint8_t t = buffer[idx++];
|
if (c.type != GridCell::EMPTY) {
|
||||||
uint8_t p = buffer[idx++];
|
buffer[idx++] = (uint8_t)x;
|
||||||
uint8_t r = buffer[idx++];
|
buffer[idx++] = (uint8_t)y;
|
||||||
|
buffer[idx++] = (uint8_t)c.type;
|
||||||
GridCell::Type newType = (GridCell::Type)t;
|
buffer[idx++] = (uint8_t)(c.param * 255.0f);
|
||||||
c.type = newType;
|
buffer[idx++] = (uint8_t)c.rotation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buffer[idx++] = count;
|
||||||
|
return idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
int SynthEngine::importGrid(const uint8_t* buffer, size_t size) {
|
||||||
|
if (size < 2) return 1;
|
||||||
|
uint8_t countStart = buffer[0];
|
||||||
|
uint8_t countEnd = buffer[size - 1];
|
||||||
|
|
||||||
|
if (countStart != countEnd) return 2;
|
||||||
|
|
||||||
|
size_t expectedSize = 1 + countStart * 5 + 1;
|
||||||
|
if (size != expectedSize) return 3;
|
||||||
|
|
||||||
|
SynthLockGuard<SynthMutex> lock(gridMutex);
|
||||||
|
|
||||||
|
// Clear grid first
|
||||||
|
for (int x = 0; x < GRID_W; ++x) {
|
||||||
|
for (int y = 0; y < GRID_H; ++y) {
|
||||||
|
GridCell& c = grid[x][y];
|
||||||
|
if (c.type == GridCell::SINK) continue;
|
||||||
|
c.type = GridCell::EMPTY;
|
||||||
|
c.param = 0.5f;
|
||||||
|
c.rotation = 0;
|
||||||
|
c.value = 0.0f;
|
||||||
|
c.phase = 0.0f;
|
||||||
|
c.next_value = 0.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t idx = 1;
|
||||||
|
for(int i=0; i<countStart; ++i) {
|
||||||
|
uint8_t x = buffer[idx++];
|
||||||
|
uint8_t y = buffer[idx++];
|
||||||
|
uint8_t t = buffer[idx++];
|
||||||
|
uint8_t p = buffer[idx++];
|
||||||
|
uint8_t r = buffer[idx++];
|
||||||
|
|
||||||
|
if (x < GRID_W && y < GRID_H) {
|
||||||
|
GridCell& c = grid[x][y];
|
||||||
|
c.type = (GridCell::Type)t;
|
||||||
c.param = (float)p / 255.0f;
|
c.param = (float)p / 255.0f;
|
||||||
c.rotation = r;
|
c.rotation = r;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
rebuildProcessingOrder_locked();
|
rebuildProcessingOrder_locked();
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SynthEngine::clearGrid() {
|
void SynthEngine::clearGrid() {
|
||||||
|
|||||||
@ -112,9 +112,9 @@ public:
|
|||||||
static const int GRID_W = 12;
|
static const int GRID_W = 12;
|
||||||
static const int GRID_H = 12;
|
static const int GRID_H = 12;
|
||||||
|
|
||||||
static const size_t SERIALIZED_GRID_SIZE = GRID_W * GRID_H * 3;
|
static const size_t MAX_SERIALIZED_GRID_SIZE = 1024;
|
||||||
void exportGrid(uint8_t* buffer);
|
size_t exportGrid(uint8_t* buffer);
|
||||||
void importGrid(const uint8_t* buffer);
|
int importGrid(const uint8_t* buffer, size_t size);
|
||||||
void loadPreset(int preset);
|
void loadPreset(int preset);
|
||||||
void rebuildProcessingOrder();
|
void rebuildProcessingOrder();
|
||||||
void clearGrid();
|
void clearGrid();
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user