Add facility to receive config via sysex
This commit is contained in:
parent
075905f7ea
commit
72b305830f
2 changed files with 107 additions and 19 deletions
|
@ -12,7 +12,7 @@
|
||||||
#include "led.h"
|
#include "led.h"
|
||||||
|
|
||||||
//Read settings from eeprom. Returns wether or not anything was written (due to factory reset or upgrade)
|
//Read settings from eeprom. Returns wether or not anything was written (due to factory reset or upgrade)
|
||||||
void readEEPROM(bool factoryReset) {
|
void readEEPROM(const bool factoryReset) {
|
||||||
|
|
||||||
// if stored settings are not for current version, or Enter+Menu are pressed at startup, they are replaced by factory settings
|
// if stored settings are not for current version, or Enter+Menu are pressed at startup, they are replaced by factory settings
|
||||||
uint16_t settingsVersion = readSetting(VERSION_ADDR);
|
uint16_t settingsVersion = readSetting(VERSION_ADDR);
|
||||||
|
@ -174,7 +174,7 @@ void setBit(uint16_t &bitfield, const uint8_t pos, const uint16_t value) {
|
||||||
|
|
||||||
|
|
||||||
//Read and write EEPROM data
|
//Read and write EEPROM data
|
||||||
void writeSetting(uint16_t address, uint16_t value) {
|
void writeSetting(const uint16_t address, const uint16_t value) {
|
||||||
union {
|
union {
|
||||||
uint8_t v[2];
|
uint8_t v[2];
|
||||||
uint16_t val;
|
uint16_t val;
|
||||||
|
@ -184,7 +184,7 @@ void writeSetting(uint16_t address, uint16_t value) {
|
||||||
EEPROM.update(address+1, data.v[1]);
|
EEPROM.update(address+1, data.v[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t readSetting(uint16_t address) {
|
uint16_t readSetting(const uint16_t address) {
|
||||||
union {
|
union {
|
||||||
uint8_t v[2];
|
uint8_t v[2];
|
||||||
uint16_t val;
|
uint16_t val;
|
||||||
|
@ -194,7 +194,7 @@ uint16_t readSetting(uint16_t address) {
|
||||||
return data.val;
|
return data.val;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t readSettingBounded(uint16_t address, uint16_t min, uint16_t max, uint16_t defaultValue) {
|
uint16_t readSettingBounded(const uint16_t address, const uint16_t min, const uint16_t max, const uint16_t defaultValue) {
|
||||||
uint16_t val = readSetting(address);
|
uint16_t val = readSetting(address);
|
||||||
if(val < min || val > max) {
|
if(val < min || val > max) {
|
||||||
val = defaultValue;
|
val = defaultValue;
|
||||||
|
@ -207,7 +207,7 @@ uint16_t readSettingBounded(uint16_t address, uint16_t min, uint16_t max, uint16
|
||||||
|
|
||||||
|
|
||||||
//Functions to send and receive config (and other things) via USB MIDI SysEx messages
|
//Functions to send and receive config (and other things) via USB MIDI SysEx messages
|
||||||
uint32_t crc32(uint8_t *message, size_t length) {
|
uint32_t crc32(const uint8_t *message, const size_t length) {
|
||||||
size_t pos=0;
|
size_t pos=0;
|
||||||
uint32_t crc=0xFFFFFFFF;
|
uint32_t crc=0xFFFFFFFF;
|
||||||
|
|
||||||
|
@ -290,6 +290,86 @@ void sendSysexMessage(const char* messageCode) {
|
||||||
usbMIDI.sendSysEx(11, (const uint8_t *)sysexMessage);
|
usbMIDI.sendSysEx(11, (const uint8_t *)sysexMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool receiveSysexSettings(const uint8_t* data, const uint16_t length) {
|
||||||
|
|
||||||
|
//Expected size of data (vendor+NuEVIc02+len+payload+crc32)
|
||||||
|
uint16_t expected_size = 3 + 8 + 2 + EEPROM_SIZE + 4;
|
||||||
|
|
||||||
|
|
||||||
|
//Positions (offsets) of parts in buffer
|
||||||
|
int size_pos = 11;
|
||||||
|
int payload_pos = size_pos + 2;
|
||||||
|
int checksum_pos = payload_pos + EEPROM_SIZE;
|
||||||
|
|
||||||
|
//Make sure length of receive buffer is enough to read all we need to. We can accept extra junk at the end though.
|
||||||
|
if(length<expected_size) {
|
||||||
|
configShowMessage("Invalid config format");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//No need to verify vendor or header/command, already done before we get here.
|
||||||
|
|
||||||
|
//Calculate checksum of stuff received (everything before checksum), transform to midi format
|
||||||
|
//(being a one-way operation, we can't do the reverse anyway)
|
||||||
|
uint32_t crc=midi32to28(crc32(data, checksum_pos));
|
||||||
|
uint32_t crc_rcv;
|
||||||
|
memcpy(&crc_rcv, data+checksum_pos, 4);
|
||||||
|
if(crc != crc_rcv) {
|
||||||
|
configShowMessage("Invalid checksum");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Verify that payload size matches the size of our EEPROM config
|
||||||
|
uint16_t payload_size = midi14to16(data[payload_pos]);
|
||||||
|
if(payload_size != EEPROM_SIZE) {
|
||||||
|
configShowMessage("Invalid config size");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint16_t eeprom_version_rcv = midi14to16(data[payload_pos+VERSION_ADDR]);
|
||||||
|
if(eeprom_version_rcv != EEPROM_VERSION) {
|
||||||
|
configShowMessage("Invalid config version");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Grab all the items in payload and save to EEPROM
|
||||||
|
for(uint16_t i=0; i<payload_size/2; i++) {
|
||||||
|
uint16_t addr = i*2;
|
||||||
|
uint16_t val;
|
||||||
|
memcpy(&val, data+payload_pos+addr, 2);
|
||||||
|
val = midi14to16(val);
|
||||||
|
|
||||||
|
//Skip sensor calibration values if they are "out of bounds". This makes it possible to send a config that does
|
||||||
|
//not overwrite sensor calibration.
|
||||||
|
if(addr == BREATH_THR_ADDR || addr == BREATH_MAX_ADDR) {
|
||||||
|
if(val<breathLoLimit || val>breathHiLimit) continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(addr == PORTAM_THR_ADDR || addr == PORTAM_MAX_ADDR) {
|
||||||
|
if(val<portamLoLimit || val>portamHiLimit) continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(addr == PITCHB_THR_ADDR || addr == PITCHB_MAX_ADDR) {
|
||||||
|
if(val<pitchbLoLimit || val>pitchbHiLimit) continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(addr == EXTRAC_THR_ADDR || addr == EXTRAC_MAX_ADDR) {
|
||||||
|
if(val<extracLoLimit || val>extracHiLimit) continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(addr == CTOUCH_THR_ADDR) {
|
||||||
|
if(val<ctouchLoLimit || val>ctouchHiLimit) continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
writeSetting(i, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
//All went well
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
//Send EEPROM and firmware versions
|
//Send EEPROM and firmware versions
|
||||||
void sendSysexVersion() {
|
void sendSysexVersion() {
|
||||||
char sysexMessage[] = "vvvNuEVIc04eevvvvvvvv"; //Placeholders for vendor and code
|
char sysexMessage[] = "vvvNuEVIc04eevvvvvvvv"; //Placeholders for vendor and code
|
||||||
|
@ -318,7 +398,7 @@ void configShowMessage(const char* message) {
|
||||||
|
|
||||||
uint8_t* sysex_rcv_buffer = NULL;
|
uint8_t* sysex_rcv_buffer = NULL;
|
||||||
|
|
||||||
void handleSysexChunk(const uint8_t *data, uint16_t length, bool last) {
|
void handleSysexChunk(const uint8_t *data, const uint16_t length, const bool last) {
|
||||||
size_t pos = 0;
|
size_t pos = 0;
|
||||||
|
|
||||||
if(!sysex_rcv_buffer) {
|
if(!sysex_rcv_buffer) {
|
||||||
|
@ -342,24 +422,25 @@ void handleSysexChunk(const uint8_t *data, uint16_t length, bool last) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void handleSysex(const uint8_t *data, uint8_t length) {
|
void handleSysex(const uint8_t *data, const unsigned int length) {
|
||||||
|
//Note: Sysex data as received here contains sysex start and end markers (0xF0 and 0xF7)
|
||||||
|
|
||||||
//Too short to even contain a 3-byte vendor id is not for us.
|
//Too short to even contain a 3-byte vendor id is not for us.
|
||||||
if(length<3) return;
|
if(length<4) return;
|
||||||
|
|
||||||
//Verify vendor
|
//Verify vendor
|
||||||
if(strncmp((char*)data, sysex_id, 3)) return; //Silently ignore different vendor id
|
if(strncmp((char*)(data+1), sysex_id, 3)) return; //Silently ignore different vendor id
|
||||||
|
|
||||||
//Verify header. Min length is 3+5+3 bytes (vendor+header+message code)
|
//Verify header. Min length is 3+5+3 bytes (vendor+header+message code)
|
||||||
if(length<11 || strncmp((char*)(data+3), "NuEVI", 3)) {
|
if(length<12 || strncmp((char*)(data+4), "NuEVI", 5)) {
|
||||||
configShowMessage("Invalid message received.");
|
configShowMessage("Invalid message.");
|
||||||
sendSysexMessage("e00");
|
sendSysexMessage("e00");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Get message code
|
//Get message code
|
||||||
char messageCode[3];
|
char messageCode[3];
|
||||||
strncpy(messageCode, (char*)(data+8), 3);
|
strncpy(messageCode, (char*)(data+9), 3);
|
||||||
|
|
||||||
if(!strncmp(messageCode, "c00", 3)) { //Config dump request
|
if(!strncmp(messageCode, "c00", 3)) { //Config dump request
|
||||||
configShowMessage("Sending config...");
|
configShowMessage("Sending config...");
|
||||||
|
@ -368,6 +449,11 @@ void handleSysex(const uint8_t *data, uint8_t length) {
|
||||||
} else if(!strncmp(messageCode, "c03", 3)) { //Version info request
|
} else if(!strncmp(messageCode, "c03", 3)) { //Version info request
|
||||||
configShowMessage("Sending version.");
|
configShowMessage("Sending version.");
|
||||||
sendSysexVersion();
|
sendSysexVersion();
|
||||||
|
} else if(!strncmp(messageCode, "c02", 3)) { //New config incoming
|
||||||
|
configShowMessage("Receiving config...");
|
||||||
|
|
||||||
|
//Tell receiveSysexSettings about what's between sysex start and end markers
|
||||||
|
if(receiveSysexSettings(data+1, length-2)) configShowMessage("New config saved.");
|
||||||
} else {
|
} else {
|
||||||
configShowMessage("Unknown message.");
|
configShowMessage("Unknown message.");
|
||||||
sendSysexMessage("e01"); //Unimplemented message code
|
sendSysexMessage("e01"); //Unimplemented message code
|
||||||
|
@ -391,6 +477,8 @@ void configModeSetup() {
|
||||||
|
|
||||||
statusLedFlash(500);
|
statusLedFlash(500);
|
||||||
|
|
||||||
|
sendSysexVersion(); //Friendly hello
|
||||||
|
|
||||||
configShowMessage("Ready.");
|
configShowMessage("Ready.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -122,20 +122,20 @@
|
||||||
#define DAC_MODE_FACTORY DAC_MODE_BREATH
|
#define DAC_MODE_FACTORY DAC_MODE_BREATH
|
||||||
|
|
||||||
|
|
||||||
void readEEPROM(bool factoryReset);
|
void readEEPROM(const bool factoryReset);
|
||||||
void setBit(uint16_t &bitfield, const uint8_t pos, const uint16_t value);
|
void setBit(uint16_t &bitfield, const uint8_t pos, const uint16_t value);
|
||||||
uint16_t readSetting(uint16_t address);
|
uint16_t readSetting(const uint16_t address);
|
||||||
void writeSetting(uint16_t address, uint16_t value);
|
void writeSetting(const uint16_t address, const uint16_t value);
|
||||||
uint16_t readSettingBounded(uint16_t address, uint16_t min, uint16_t max, uint16_t defaultValue);
|
uint16_t readSettingBounded(const uint16_t address, const uint16_t min, const uint16_t max, const uint16_t defaultValue);
|
||||||
|
|
||||||
//Functions for config management mode
|
//Functions for config management mode
|
||||||
void sendSysexSettings();
|
void sendSysexSettings();
|
||||||
void sendSysexMessage(const char* messageCode);
|
void sendSysexMessage(const char* messageCode);
|
||||||
void sendSysexVersion();
|
void sendSysexVersion();
|
||||||
|
|
||||||
void handleSysex(const uint8_t *data, uint8_t length);
|
void handleSysex(const uint8_t *data, const unsigned int length);
|
||||||
void handleSysexChunk(const uint8_t *data, uint16_t length, bool last);
|
void handleSysexChunk(const uint8_t *data, const uint16_t length, const bool last);
|
||||||
uint32_t crc32(uint8_t *message, size_t length);
|
uint32_t crc32(const uint8_t *message, const size_t length);
|
||||||
|
|
||||||
void configInitScreen();
|
void configInitScreen();
|
||||||
void configShowMessage(const char* message);
|
void configShowMessage(const char* message);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue