xevi/NuEVI/settings.cpp
2023-02-27 13:14:45 +01:00

618 lines
25 KiB
C++

#include <Arduino.h>
#include <EEPROM.h>
#include <Adafruit_SSD1306.h>
#include "settings.h"
#include "globals.h"
#include "menu.h"
#include "hardware.h"
#include "config.h"
#include "midi.h"
#include "led.h"
//Read settings from eeprom. Returns wether or not anything was written (due to factory reset or upgrade)
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
uint16_t settingsVersion = readSetting(VERSION_ADDR);
// blank eeprom will be 0xFFFF. For a full reset, call it "version 0" so everything gets overwritten.
if (factoryReset || settingsVersion == 0xffffu) {
settingsVersion = 0;
}
if(settingsVersion != EEPROM_VERSION) {
if(settingsVersion < 24) { //Oldest version from which any settings are recognized
writeSetting(BREATH_THR_ADDR, BREATH_THR_FACTORY);
writeSetting(BREATH_MAX_ADDR, BREATH_MAX_FACTORY);
#if defined(NURAD)
writeSetting(PORTAM_THR_ADDR, PORTPR_THR_FACTORY);
writeSetting(PORTAM_MAX_ADDR, PORTPR_MAX_FACTORY);
#else
if (digitalRead(biteJumperPin)){ //PBITE (if pulled low with jumper, pressure sensor is used instead of capacitive bite sensing)
writeSetting(PORTAM_THR_ADDR, PORTAM_THR_FACTORY);
writeSetting(PORTAM_MAX_ADDR, PORTAM_MAX_FACTORY);
} else {
writeSetting(PORTAM_THR_ADDR, PORTPR_THR_FACTORY);
writeSetting(PORTAM_MAX_ADDR, PORTPR_MAX_FACTORY);
}
#endif
writeSetting(PITCHB_THR_ADDR, PITCHB_THR_FACTORY);
writeSetting(PITCHB_MAX_ADDR, PITCHB_MAX_FACTORY);
writeSetting(EXTRAC_THR_ADDR, EXTRAC_THR_FACTORY);
writeSetting(EXTRAC_MAX_ADDR, EXTRAC_MAX_FACTORY);
writeSetting(CTOUCH_THR_ADDR, CTOUCH_THR_FACTORY);
writeSetting(TRANSP_ADDR, TRANSP_FACTORY);
writeSetting(MIDI_ADDR, MIDI_FACTORY);
writeSetting(BREATH_CC_ADDR, BREATH_CC_FACTORY);
writeSetting(BREATH_AT_ADDR, BREATH_AT_FACTORY);
writeSetting(VELOCITY_ADDR, VELOCITY_FACTORY);
writeSetting(PORTAM_ADDR, PORTAM_FACTORY);
writeSetting(PB_ADDR, PB_FACTORY);
writeSetting(EXTRA_ADDR, EXTRA_FACTORY);
writeSetting(VIBRATO_ADDR, VIBRATO_FACTORY);
writeSetting(DEGLITCH_ADDR, DEGLITCH_FACTORY);
writeSetting(PATCH_ADDR, PATCH_FACTORY);
writeSetting(OCTAVE_ADDR, OCTAVE_FACTORY);
writeSetting(BREATHCURVE_ADDR, BREATHCURVE_FACTORY);
writeSetting(VEL_SMP_DL_ADDR, VEL_SMP_DL_FACTORY);
writeSetting(VEL_BIAS_ADDR, VEL_BIAS_FACTORY);
writeSetting(PINKY_KEY_ADDR, PINKY_KEY_FACTORY);
}
if(settingsVersion < 26) {
writeSetting(FP1_ADDR, 0);
writeSetting(FP2_ADDR, 0);
writeSetting(FP3_ADDR, 0);
writeSetting(FP4_ADDR, 0);
writeSetting(FP5_ADDR, 0);
writeSetting(FP6_ADDR, 0);
writeSetting(FP7_ADDR, 0);
writeSetting(DIPSW_BITS_ADDR, DIPSW_BITS_FACTORY);
}
if(settingsVersion < 28) {
writeSetting(PARAL_ADDR, PARAL_FACTORY);
writeSetting(ROTN1_ADDR, ROTN1_FACTORY);
writeSetting(ROTN2_ADDR, ROTN2_FACTORY);
writeSetting(ROTN3_ADDR, ROTN3_FACTORY);
writeSetting(ROTN4_ADDR, ROTN4_FACTORY);
writeSetting(PRIO_ADDR, PRIO_FACTORY);
}
if(settingsVersion < 29) {
writeSetting(VIB_SENS_ADDR, VIB_SENS_FACTORY);
writeSetting(VIB_RETN_ADDR, VIB_RETN_FACTORY);
}
if(settingsVersion < 31) {
writeSetting(VIB_SQUELCH_ADDR, VIB_SQUELCH_FACTORY);
writeSetting(VIB_DIRECTION_ADDR, VIB_DIRECTION_FACTORY);
}
if(settingsVersion < 32) {
writeSetting(BREATH_CC2_ADDR, BREATH_CC2_FACTORY);
writeSetting(BREATH_CC2_RISE_ADDR, BREATH_CC2_RISE_FACTORY);
writeSetting(VIB_SENS_BITE_ADDR, VIB_SENS_BITE_FACTORY);
writeSetting(VIB_SQUELCH_BITE_ADDR, VIB_SQUELCH_BITE_FACTORY);
writeSetting(VIB_CONTROL_ADDR, VIB_CONTROL_FACTORY);
writeSetting(TRILL3_INTERVAL_ADDR, TRILL3_INTERVAL_FACTORY);
writeSetting(DAC_MODE_ADDR, DAC_MODE_FACTORY);
}
if(settingsVersion < 33) {
writeSetting(EXTRA2_ADDR, EXTRA2_FACTORY);
writeSetting(LEVEL_CC_ADDR, LEVEL_CC_FACTORY);
writeSetting(LEVEL_VAL_ADDR, LEVEL_VAL_FACTORY);
}
if(settingsVersion < 34) {
writeSetting(FINGER_ADDR, FINGER_FACTORY);
writeSetting(LPINKY3_ADDR, LPINKY3_FACTORY);
}
if(settingsVersion < 35) {
writeSetting(BATTYPE_ADDR, BATTYPE_FACTORY);
writeSetting(HARMSET_ADDR, HARMSET_FACTORY);
writeSetting(HARMSEL_ADDR, HARMSEL_FACTORY);
}
if(settingsVersion < 36) {
writeSetting(PARAB_ADDR, PARAB_FACTORY);
writeSetting(ROTB1_ADDR, ROTB1_FACTORY);
writeSetting(ROTB2_ADDR, ROTB2_FACTORY);
writeSetting(ROTB3_ADDR, ROTB3_FACTORY);
writeSetting(ROTB4_ADDR, ROTB4_FACTORY);
writeSetting(PARAC_ADDR, PARAC_FACTORY);
writeSetting(ROTC1_ADDR, ROTC1_FACTORY);
writeSetting(ROTC2_ADDR, ROTC2_FACTORY);
writeSetting(ROTC3_ADDR, ROTC3_FACTORY);
writeSetting(ROTC4_ADDR, ROTC4_FACTORY);
writeSetting(POLYSEL_ADDR, POLYSEL_FACTORY);
writeSetting(FWCTYPE_ADDR, FWCTYPE_FACTORY);
writeSetting(HMZKEY_ADDR, HMZKEY_FACTORY);
}
if(settingsVersion < 37) {
writeSetting(FWCLCH_ADDR, FWCLCH_FACTORY);
writeSetting(FWCDP2_ADDR, FWCDP2_FACTORY);
}
if(settingsVersion < 38) {
writeSetting(HMZLIMIT_ADDR, HMZLIMIT_FACTORY);
}
if(settingsVersion < 39) {
writeSetting(BRINTERV_ADDR, BRINTERV_FACTORY);
writeSetting(OTFKEY_ADDR, OTFKEY_FACTORY);
}
if(settingsVersion < 40) {
writeSetting(PORTLIMIT_ADDR, PORTLIMIT_FACTORY);
writeSetting(LEVER_THR_ADDR, LEVER_THR_FACTORY);
writeSetting(LEVER_MAX_ADDR, LEVER_MAX_FACTORY);
}
if(settingsVersion < 41) {
writeSetting(BRHARMSET_ADDR, BRHARMSET_FACTORY);
writeSetting(BRHARMSEL_ADDR, BRHARMSEL_FACTORY);
}
if(settingsVersion < 42) {
writeSetting(BITECTL_ADDR, BITECTL_FACTORY);
writeSetting(BITECC_ADDR, BITECC_FACTORY);
writeSetting(LEVERCTL_ADDR, LEVERCTL_FACTORY);
writeSetting(LEVERCC_ADDR, LEVERCC_FACTORY);
}
if(settingsVersion < 43) {
writeSetting(CVTUNE_ADDR, CVTUNE_FACTORY);
writeSetting(CVSCALE_ADDR, CVSCALE_FACTORY);
}
if(settingsVersion < 44) {
writeSetting(CVRATE_ADDR, CVRATE_FACTORY);
}
if(settingsVersion < 45) {
writeSetting(ROLLER_ADDR, ROLLER_FACTORY);
}
writeSetting(VERSION_ADDR, EEPROM_VERSION);
}
// read all settings from EEPROM
breathThrVal = readSettingBounded(BREATH_THR_ADDR, breathLoLimit, breathHiLimit, BREATH_THR_FACTORY);
breathMaxVal = readSettingBounded(BREATH_MAX_ADDR, breathLoLimit, breathHiLimit, BREATH_MAX_FACTORY);
portamThrVal = readSettingBounded(PORTAM_THR_ADDR, portamLoLimit, portamHiLimit, PORTAM_THR_FACTORY);
portamMaxVal = readSettingBounded(PORTAM_MAX_ADDR, portamLoLimit, portamHiLimit, PORTAM_MAX_FACTORY);
pitchbThrVal = readSettingBounded(PITCHB_THR_ADDR, pitchbLoLimit, pitchbHiLimit, PITCHB_THR_FACTORY);
pitchbMaxVal = readSettingBounded(PITCHB_MAX_ADDR, pitchbLoLimit, pitchbHiLimit, PITCHB_MAX_FACTORY);
transpose = readSettingBounded(TRANSP_ADDR, 0, 24, TRANSP_FACTORY);
MIDIchannel = readSettingBounded(MIDI_ADDR, 1, 16, MIDI_FACTORY);
breathCC = readSettingBounded(BREATH_CC_ADDR, 0, 10, BREATH_CC_FACTORY);
breathAT = readSettingBounded(BREATH_AT_ADDR, 0, 1, BREATH_AT_FACTORY);
velocity = readSettingBounded(VELOCITY_ADDR, 0, 127, VELOCITY_FACTORY);
portamento = readSettingBounded(PORTAM_ADDR, 0, 5, PORTAM_FACTORY);
PBdepth = readSettingBounded(PB_ADDR, 0, 12, PB_FACTORY);
extraCT = readSettingBounded(EXTRA_ADDR, 0, 4, EXTRA_FACTORY);
vibrato = readSettingBounded(VIBRATO_ADDR, 0, 9, VIBRATO_FACTORY);
deglitch = readSettingBounded(DEGLITCH_ADDR, 0, 70, DEGLITCH_FACTORY);
extracThrVal = readSettingBounded(EXTRAC_THR_ADDR, extracLoLimit, extracHiLimit, EXTRAC_THR_FACTORY);
extracMaxVal = readSettingBounded(EXTRAC_MAX_ADDR, extracLoLimit, extracHiLimit, EXTRAC_MAX_FACTORY);
patch = readSettingBounded(PATCH_ADDR, 0, 127, PATCH_FACTORY);
octave = readSettingBounded(OCTAVE_ADDR, 0, 6, OCTAVE_FACTORY);
ctouchThrVal = readSettingBounded(CTOUCH_THR_ADDR, ctouchLoLimit, ctouchHiLimit, CTOUCH_THR_FACTORY);
curve = readSettingBounded(BREATHCURVE_ADDR, 0, 12, BREATHCURVE_FACTORY);
velSmpDl = readSettingBounded(VEL_SMP_DL_ADDR, 0, 30, VEL_SMP_DL_FACTORY);
velBias = readSettingBounded(VEL_BIAS_ADDR, 0, 9, VEL_BIAS_FACTORY);
pinkySetting = readSettingBounded(PINKY_KEY_ADDR, 0, 31, PINKY_KEY_FACTORY);
fastPatch[0] = readSettingBounded(FP1_ADDR, 0, 127, 0);
fastPatch[1] = readSettingBounded(FP2_ADDR, 0, 127, 0);
fastPatch[2] = readSettingBounded(FP3_ADDR, 0, 127, 0);
fastPatch[3] = readSettingBounded(FP4_ADDR, 0, 127, 0);
fastPatch[4] = readSettingBounded(FP5_ADDR, 0, 127, 0);
fastPatch[5] = readSettingBounded(FP6_ADDR, 0, 127, 0);
fastPatch[6] = readSettingBounded(FP7_ADDR, 0, 127, 0);
dipSwBits = readSetting(DIPSW_BITS_ADDR);
rotations_a.parallel = readSettingBounded(PARAL_ADDR, 0, 48, PARAL_FACTORY);
rotations_a.rotations[0] = readSettingBounded(ROTN1_ADDR, 0, 48, ROTN1_FACTORY);
rotations_a.rotations[1] = readSettingBounded(ROTN2_ADDR, 0, 48, ROTN2_FACTORY);
rotations_a.rotations[2] = readSettingBounded(ROTN3_ADDR, 0, 48, ROTN3_FACTORY);
rotations_a.rotations[3] = readSettingBounded(ROTN4_ADDR, 0, 48, ROTN4_FACTORY);
priority = readSettingBounded(PRIO_ADDR, 0, 1, PRIO_FACTORY);
vibSens = readSettingBounded(VIB_SENS_ADDR, 1, 12, VIB_SENS_FACTORY);
vibRetn = readSettingBounded(VIB_RETN_ADDR, 0, 4, VIB_RETN_FACTORY);
vibSquelch = readSettingBounded(VIB_SQUELCH_ADDR, 1, 30, VIB_SQUELCH_FACTORY);
vibDirection = readSettingBounded(VIB_DIRECTION_ADDR, 0, 1, VIB_DIRECTION_FACTORY);
breathCC2 = readSettingBounded(BREATH_CC2_ADDR, 0, 127, BREATH_CC2_FACTORY);
breathCC2Rise = readSettingBounded(BREATH_CC2_RISE_ADDR, 1, 10, BREATH_CC2_RISE_FACTORY);
vibSensBite = readSettingBounded(VIB_SENS_BITE_ADDR, 1, 17, VIB_SENS_BITE_FACTORY);
vibSquelchBite = readSettingBounded(VIB_SQUELCH_BITE_ADDR, 1, 30, VIB_SQUELCH_BITE_FACTORY);
vibControl = readSettingBounded(VIB_CONTROL_ADDR, 0, 2, VIB_CONTROL_FACTORY);
dacMode = readSettingBounded(DAC_MODE_ADDR, DAC_MODE_BREATH, DAC_MODE_PITCH, DAC_MODE_FACTORY);
trill3_interval = readSettingBounded(TRILL3_INTERVAL_ADDR, 3, 4, TRILL3_INTERVAL_FACTORY);
extraCT2 = readSettingBounded(EXTRA2_ADDR, 0, 127, EXTRA2_FACTORY);
levelCC = readSettingBounded(LEVEL_CC_ADDR, 0, 127, LEVEL_CC_FACTORY);
levelVal = readSettingBounded(LEVEL_VAL_ADDR, 0, 127, LEVEL_VAL_FACTORY);
#if defined(NURAD)
fingering = readSettingBounded(FINGER_ADDR, 0, 4, FINGER_FACTORY);
#else
fingering = readSettingBounded(FINGER_ADDR, 0, 3, FINGER_FACTORY);
#endif
lpinky3 = readSettingBounded(LPINKY3_ADDR, 0, 25, LPINKY3_FACTORY);
batteryType = readSettingBounded(BATTYPE_ADDR, 0, 2, BATTYPE_FACTORY);
harmSetting = readSettingBounded(HARMSET_ADDR, 0, 6, HARMSET_FACTORY);
harmSelect = readSettingBounded(HARMSEL_ADDR, 0, 7, HARMSEL_FACTORY);
polySelect = (PolySelect)readSettingBounded(POLYSEL_ADDR, 0, 10, POLYSEL_FACTORY);
fwcType = readSettingBounded(FWCTYPE_ADDR, 0, 4, FWCTYPE_FACTORY);
fwcLockH = readSettingBounded(FWCLCH_ADDR, 0, 1, FWCLCH_FACTORY);
fwcDrop2 = readSettingBounded(FWCDP2_ADDR, 0, 1, FWCDP2_FACTORY);
hmzKey = readSettingBounded(HMZKEY_ADDR, 0, 11, HMZKEY_FACTORY);
hmzLimit = readSettingBounded(HMZLIMIT_ADDR, 2, 5, HMZLIMIT_FACTORY);
rotations_b.parallel = readSettingBounded(PARAB_ADDR, 0, 48, PARAB_FACTORY);
rotations_b.rotations[0] = readSettingBounded(ROTB1_ADDR, 0, 48, ROTB1_FACTORY);
rotations_b.rotations[1] = readSettingBounded(ROTB2_ADDR, 0, 48, ROTB2_FACTORY);
rotations_b.rotations[2] = readSettingBounded(ROTB3_ADDR, 0, 48, ROTB3_FACTORY);
rotations_b.rotations[3] = readSettingBounded(ROTB4_ADDR, 0, 48, ROTB4_FACTORY);
rotations_c.parallel = readSettingBounded(PARAC_ADDR, 0, 48, PARAC_FACTORY);
rotations_c.rotations[0] = readSettingBounded(ROTC1_ADDR, 0, 48, ROTC1_FACTORY);
rotations_c.rotations[1] = readSettingBounded(ROTC2_ADDR, 0, 48, ROTC2_FACTORY);
rotations_c.rotations[2] = readSettingBounded(ROTC3_ADDR, 0, 48, ROTC3_FACTORY);
rotations_c.rotations[3] = readSettingBounded(ROTC4_ADDR, 0, 48, ROTC4_FACTORY);
otfKey = readSettingBounded(OTFKEY_ADDR, 0, 1, OTFKEY_FACTORY);
breathInterval = readSettingBounded(BRINTERV_ADDR, 3, 15, BRINTERV_FACTORY);
portLimit = readSettingBounded(PORTLIMIT_ADDR, 1, 127, PORTLIMIT_FACTORY);
leverThrVal = readSettingBounded(LEVER_THR_ADDR, leverLoLimit, leverHiLimit, LEVER_THR_FACTORY);
leverMaxVal = readSettingBounded(LEVER_MAX_ADDR, leverLoLimit, leverHiLimit, LEVER_MAX_FACTORY);
brHarmSetting = readSettingBounded(BRHARMSET_ADDR, 0, 6, BRHARMSET_FACTORY);
brHarmSelect = readSettingBounded(BRHARMSEL_ADDR, 0, 3, BRHARMSEL_FACTORY);
biteControl = readSettingBounded(BITECTL_ADDR, 0, 3, BITECTL_FACTORY);
leverControl = readSettingBounded(LEVERCTL_ADDR, 0, 3, LEVERCTL_FACTORY);
biteCC = readSettingBounded(BITECC_ADDR, 0, 127, BITECC_FACTORY);
leverCC = readSettingBounded(LEVERCC_ADDR, 0, 127, LEVERCC_FACTORY);
cvTune = readSettingBounded(CVTUNE_ADDR, 1, 199, CVTUNE_FACTORY);
cvScale = readSettingBounded(CVSCALE_ADDR, 1, 199, CVSCALE_FACTORY);
cvVibRate = readSettingBounded(CVRATE_ADDR, 0, 8, CVRATE_FACTORY);
rollerMode = readSettingBounded(ROLLER_ADDR, 0, 3, ROLLER_FACTORY);
//Flags stored in bit field
fastBoot = (dipSwBits & (1<<DIPSW_FASTBOOT))?1:0;
legacy = (dipSwBits & (1<<DIPSW_LEGACY))?1:0;
legacyBrAct = (dipSwBits & (1<<DIPSW_LEGACYBRACT))?1:0;
widiOn = (dipSwBits & (1<<DIPSW_WIDION))?1:0;
gateOpenEnable = (dipSwBits & (1<<DIPSW_GATEOPEN))?1:0;
specialKeyEnable = (dipSwBits & (1<<DIPSW_SPKEYENABLE))?1:0;
bcasMode = (dipSwBits & (1<<DIPSW_BCASMODE))?1:0;
}
//Poke at a certain bit in a bit field
void setBit(uint16_t &bitfield, const uint8_t pos, const uint16_t value) {
bitfield = (bitfield & ~(1<<pos)) | ((value?1:0)<<pos);
}
//Read and write EEPROM data
void writeSetting(const uint16_t address, const uint16_t value) {
union {
uint8_t v[2];
uint16_t val;
} data;
data.val = value;
EEPROM.update(address, data.v[0]);
EEPROM.update(address+1, data.v[1]);
}
uint16_t readSetting(const uint16_t address) {
union {
uint8_t v[2];
uint16_t val;
} data;
data.v[0] = EEPROM.read(address);
data.v[1] = EEPROM.read(address+1);
return data.val;
}
uint16_t readSettingBounded(const uint16_t address, const uint16_t min, const uint16_t max, const uint16_t defaultValue) {
uint16_t val = readSetting(address);
if(val < min || val > max) {
val = defaultValue;
writeSetting(address, val);
}
return val;
}
//Functions to send and receive config (and other things) via USB MIDI SysEx messages
uint32_t crc32(const uint8_t *message, const size_t length) {
size_t pos=0;
uint32_t crc=0xFFFFFFFF;
while (pos<length) {
crc ^= message[pos++]; //Get next byte and increment position
for (uint8_t j=0; j<8; ++j) { //Mask off 8 next bits
crc = (crc >> 1) ^ (0xEDB88320 & -(crc & 1));
}
}
return ~crc;
}
/*
Send EEPROM config dump as sysex message. Message format is structured like this:
+------------------------------------------------------------------------------------+
| vendor(3) | "NuEVIc01" (8) | Payload size (2) | EEPROM data (variable) | crc32 (4) |
+------------------------------------------------------------------------------------+
Payload size is for the EEPROM data chunk (not including anything else before or after
CRC32 covers the entire buffer up to and including the eeprom data (but not the checksum itself)
This currently operates under the assumption that the whole EEPROM chunk only consists of unsigned 16 bit ints, only using the range 0-16383
*/
void sendSysexSettings() {
const char *header = "NuEVIc01"; //NuEVI config dump 01
//Build a send buffer of all the things
size_t sysex_size = 3 + strlen(header) + 2 + EEPROM_SIZE + 4;
uint8_t *sysex_data = (uint8_t*)malloc(sysex_size);
//Positions (offsets) of parts in send buffer
int header_pos = 3;
int size_pos = header_pos + strlen(header);
int payload_pos = size_pos + 2;
int checksum_pos = payload_pos + EEPROM_SIZE;
//SysEX manufacturer ID
memcpy(sysex_data, sysex_id, 3);
//Header with command code
memcpy(sysex_data+header_pos, header, strlen(header));
//Payload length
*(uint16_t*)(sysex_data+size_pos) = convertToMidiValue(EEPROM_SIZE);
//Config data
uint16_t* config_buffer_start = (uint16_t*)(sysex_data+payload_pos);
//Read one settings item at a time, change data format, and put in send buffer
for(uint16_t idx=0; idx<EEPROM_SIZE/2; idx++) {
uint16_t eepromval = readSetting(idx*2);
config_buffer_start[idx] = convertToMidiValue(eepromval);
}
uint32_t checksum = crc32(sysex_data, checksum_pos);
*(uint32_t*)(sysex_data+checksum_pos) = convertToMidiCRC(checksum);
usbMIDI.sendSysEx(sysex_size, sysex_data);
free(sysex_data);
}
//Send a simple 3-byte message code as sysex
void sendSysexMessage(const char* messageCode) {
char sysexMessage[] = "vvvNuEVIccc"; //Placeholders for vendor and code
memcpy(sysexMessage, sysex_id, 3);
memcpy(sysexMessage+8, messageCode, 3);
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=convertToMidiCRC(crc32(data, checksum_pos));
uint32_t crc_rcv;
memcpy(&crc_rcv, data+checksum_pos, 4);
if(crc != crc_rcv && crc_rcv != NO_CHECKSUM) {
configShowMessage("Invalid checksum");
return false;
}
//Verify that payload size matches the size of our EEPROM config
uint16_t payload_size = convertFromMidiValue(data+size_pos);
if(payload_size != EEPROM_SIZE) {
configShowMessage("Invalid config size");
return false;
}
uint16_t eeprom_version_rcv = convertFromMidiValue(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;
val = convertFromMidiValue(data+(payload_pos+addr));
//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(addr, val);
}
//All went well
return true;
}
//Send EEPROM and firmware versions
void sendSysexVersion() {
char sysexMessage[] = "vvvNuEVIc04eevvvvvvvv"; //Placeholders for vendor and code
uint8_t fwStrLen = min(strlen(FIRMWARE_VERSION), 8); //Limit firmware version string to 8 bytes
memcpy(sysexMessage, sysex_id, 3);
memcpy(sysexMessage+13, FIRMWARE_VERSION, fwStrLen);
*(uint16_t*)(sysexMessage+11) = convertToMidiValue(EEPROM_VERSION);
uint8_t message_length = 13+fwStrLen;
usbMIDI.sendSysEx(message_length, (const uint8_t *)sysexMessage);
}
extern Adafruit_SSD1306 display;
void configShowMessage(const char* message) {
display.fillRect(0,32,128,64,BLACK);
display.setCursor(0,32);
display.setTextColor(WHITE);
display.print(message);
display.display();
}
uint8_t* sysex_rcv_buffer = NULL;
uint16_t sysex_buf_size = 0;
void handleSysexChunk(const uint8_t *data, uint16_t length, bool last) {
uint16_t pos;
if(!sysex_rcv_buffer) {
//Start out with an empty buffer
pos = 0;
sysex_buf_size = length;
sysex_rcv_buffer = (uint8_t *)malloc(sysex_buf_size);
} else {
//Increase size of current buffer
pos = sysex_buf_size;
sysex_buf_size += length;
sysex_rcv_buffer = (uint8_t *)realloc(sysex_rcv_buffer, sysex_buf_size);
}
//Append this chunk to buffer
memcpy(sysex_rcv_buffer + pos, data, length);
//If it's the last one, call the regular handler to process it
if(last) {
handleSysex(sysex_rcv_buffer, sysex_buf_size);
//Discard the buffer
free(sysex_rcv_buffer);
sysex_rcv_buffer = NULL;
sysex_buf_size = 0;
}
}
void handleSysex(uint8_t *data, 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.
if(length<4) return;
//Verify vendor
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)
if(length<12 || strncmp((char*)(data+4), "NuEVI", 5)) {
configShowMessage("Invalid message.");
sendSysexMessage("e00");
return;
}
//Get message code
char messageCode[3];
strncpy(messageCode, (char*)(data+9), 3);
if(!strncmp(messageCode, "c00", 3)) { //Config dump request
configShowMessage("Sending config...");
sendSysexSettings();
configShowMessage("Config sent.");
} else if(!strncmp(messageCode, "c03", 3)) { //Version info request
configShowMessage("Sending version.");
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 {
configShowMessage("Unknown message.");
sendSysexMessage("e01"); //Unimplemented message code
}
}
void configModeSetup() {
statusLedFlash(500);
display.clearDisplay();
display.setCursor(0,0);
display.setTextColor(WHITE);
display.setTextSize(0);
display.println("Config mgmt");
display.println("Power off NuEVI");
display.println("to exit");
display.display();
usbMIDI.setHandleSystemExclusive(handleSysexChunk);
statusLedFlash(500);
sendSysexVersion(); //Friendly hello
configShowMessage("Ready.");
}
//"Main loop". Just sits and wait for midi messages and lets the sysex handler do all the work.
void configModeLoop() {
usbMIDI.read();
}