Several fixes.

Now state information is kept isinde SPIFFS.
After getting a client IP state information is send back via OSC messages.
master
Holger Wirtz 4 years ago
parent 05798b54a2
commit 5cd41d87ce
  1. 387
      OSC2MIDI.ino

@ -17,30 +17,38 @@
#include <SoftwareSerial.h>
#include <LiquidCrystal_I2C.h>
#include <looper.h>
#define MDNS_NAME "osc2midi"
#define AP_SSID_NAME "OSC2MIDI"
#define AP_PASSWORD "osc2midi"
#define AP_SSID_CONFIG_NAME "OSC2MIDI-Config"
#define AP_CONFIG_PASSWORD "osc2midi"
#define SOFT_SERIAL_RX 18
#define SOFT_SERIAL_TX 19
#define AP_DATA_RESET_PIN 35
#define AP_MODE_PIN 34
#define LCD_I2C_ADDR 0x27
#define LCD_COL 16
#define LCD_ROW 2
#define UDP_RECV_PORT 8000
#define UDP_SEND_PORT 9000
#define KRATE_MODE 200
#define KRATE_STATE 2000
#include <SPIFFS.h>
#include <ESPFlash.h>
#define MDNS_NAME "osc2midi"
#define AP_SSID_NAME "OSC2MIDI"
#define AP_PASSWORD "osc2midi"
#define AP_SSID_CONFIG_NAME "OSC2MIDI-Config"
#define AP_CONFIG_PASSWORD "osc2midi"
#define WIFI_CONNECT_TIMEOUT 30
#define CFG_PORTAL_TIMEOUT 90
#define SOFT_SERIAL_RX 18
#define SOFT_SERIAL_TX 19
#define AP_DATA_RESET_PIN 25
#define AP_MODE_PIN 26
#define LCD_I2C_ADDR 0x27
#define LCD_COL 16
#define LCD_ROW 2
#define UDP_RECV_PORT 8000
#define UDP_SEND_PORT 9000
#define KRATE_MODE 200
#define KRATE_STATE 2000
#define KRATE_RESET_AP_DATA 5000
#define KRATE_CHECK_WRITE_STATE 10000
#define LAST_USAGE_TIMER 5000
#define FORMAT_SPIFFS_IF_FAILED true
void OSCToMidiCC(OSCMessage &msg, int offset);
void MidiCCToOSC(uint8_t channel, uint8_t number, uint8_t value);
void change_midi_state(uint8_t midichannel, uint8_t cc, uint8_t value);
void show_midi_state(void);
void set_midi_state(void);
void check_mode(void);
/*void change_midi_state(uint8_t midichannel, uint8_t cc, uint8_t value);
void show_midi_state(void);
void set_midi_state(void);
void check_mode(void);*/
WiFiUDP udp;
IPAddress clientIP;
@ -54,19 +62,29 @@ HardwareSerial midi1(2); // RX: 16, TX: 17
//#define TX (1)
#endif
SoftwareSerial midi2;
bool ap_mode_state = digitalRead(AP_MODE_PIN);
int8_t state[16][128];
bool ap_mode_state;
int8_t midistate[16 * 128];
bool last_reset_ap_check = false;
looper sched;
bool write_state = false;
uint32_t last_usage = millis();
MIDI_CREATE_INSTANCE(HardwareSerial, midi1, MIDI1);
void setup()
{
pinMode(AP_DATA_RESET_PIN, INPUT_PULLUP);
pinMode(AP_MODE_PIN, INPUT_PULLUP);
Serial.begin(115200);
DEBUG_MSG("<SETUP>\n");
//Serial.print("FORMAT SPIFFS..."); SPIFFS.format(); Serial.println("done."); while (1);
memset(midistate, -1, 16 * 128);
pinMode(AP_DATA_RESET_PIN, INPUT_PULLDOWN);
pinMode(AP_MODE_PIN, INPUT_PULLDOWN);
ap_mode_state = digitalRead(AP_MODE_PIN);
Serial.setDebugOutput(true);
Serial.println(F("OSC2MIDI (c)2020 H. Wirtz <wirtz@parasitstudio.de>"));
@ -80,6 +98,20 @@ void setup()
lcd.print(F("(c)parasiTstudio"));
delay(1000);
WiFi.mode(WIFI_STA); // explicitly set mode, esp defaults to STA+AP
if (!SPIFFS.begin(FORMAT_SPIFFS_IF_FAILED))
{
DEBUG_MSG("An Error has occurred while mounting SPIFFS!");
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(F("SPIFFS ERROR"));
lcd.setCursor(0, 1);
lcd.print(F("Restarting..."));
delay(1000);
ESP.restart();
}
if (ap_mode_state == LOW)
{
DEBUG_MSG("Mode Access-Point\n");
@ -108,31 +140,18 @@ void setup()
{
DEBUG_MSG("Mode Client\n");
WiFi.mode(WIFI_STA); // explicitly set mode, esp defaults to STA+AP
WiFiManager wm;
if (digitalRead(AP_DATA_RESET_PIN) != LOW)
{
wm.resetSettings();
DEBUG_MSG("Resetting AP data\n");
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(F("Resetting AP data"));
delay(2000);
}
// Automatically connect using saved credentials,
// if connection fails, it starts an access point with the specified name ( "AutoConnectAP"),
// if empty will auto generate SSID, if password is blank it will be anonymous AP (wm.autoConnect())
// then goes into a blocking loop awaiting configuration and will return success result
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(F("Mode Config-AP"));
lcd.setCursor(0, 1);
lcd.print(F("192.168.4.1"));
wm.setConnectTimeout(WIFI_CONNECT_TIMEOUT);
wm.setConfigPortalTimeout(CFG_PORTAL_TIMEOUT);
wm.setBreakAfterConfig(true);
if (!wm.autoConnect(AP_SSID_CONFIG_NAME, AP_CONFIG_PASSWORD))
{
DEBUG_MSG("Failed to connect\n");
@ -163,8 +182,6 @@ void setup()
lcd.print(WiFi.localIP());
}
memset(state, -1, 16 * 128);
udp.begin(UDP_RECV_PORT);
DEBUG_MSG("Listening for UDP packets on port %d\n", UDP_RECV_PORT);
@ -176,10 +193,16 @@ void setup()
MIDI1.setHandleControlChange(MidiCCToOSC);
MIDI1.turnThruOff();
//set_midi_state();
sched.addJob(check_mode, KRATE_MODE);
sched.addJob(show_midi_state, KRATE_STATE);
sched.addJob(check_reset_ap_data, KRATE_RESET_AP_DATA);
sched.addJob(check_write_state, KRATE_CHECK_WRITE_STATE);
//sched.addJob(show_midi_state, KRATE_STATE);
listDir(SPIFFS, "/", 1);
//read_midistate(1);
show_midi_state();
DEBUG_MSG("<LOOP>\n");
}
void loop()
@ -190,10 +213,15 @@ void loop()
size_t size = udp.parsePacket();
while (udp.available())
if (udp.available())
{
IPAddress tmpIP = udp.remoteIP();
// Keep track of the client IP address for "talking back"
if (clientIP != tmpIP)
{
clientIP = tmpIP;
DEBUG_MSG("New connection from: %d.%d.%d.%d\n", clientIP[0],clientIP[1],clientIP[2],clientIP[3]);
}
// Check if there are any OSC packets to handle
udp.read(buffer, size);
msg.fill(buffer, size);
@ -202,6 +230,7 @@ void loop()
{
DEBUG_OSC_MESSAGE(msg);
msg.route("/midi/cc", OSCToMidiCC);
msg.route("/ping", ping);
//msg.route("/midi/sysex", OSCToMidiSYSEX);
//msg.route("/midi/note", OSCToMidiNote);
}
@ -209,13 +238,6 @@ void loop()
{
DEBUG_MSG("Error parsing OSC message: %d\n", msg.getError());
}
// Keep track of the client IP address for "talking back"
if (clientIP == tmpIP)
{
clientIP = tmpIP;
DEBUG_MSG("Connection from: %s", clientIP);
}
}
// Check if there are any CC messages from synth itself
@ -269,13 +291,13 @@ void OSCToMidiCC(OSCMessage & msg, int offset)
// XY pad, two values
cc = getVar(address, 1);
value = round(msg.getFloat(0));
value = value > 127 ? 127 : value;
value = constrain(value, 0, 127);
DEBUG_MSG("MSG: % s\tChannel: % u\t\tCC: % u\tValue: % u\n", address, midichannel, cc, value);
MIDI1.sendControlChange(cc, value, midichannel);
change_midi_state(midichannel, cc, value);
cc = getVar(address, 2);
value = round(msg.getFloat(1));
value = value > 127 ? 127 : value;
value = constrain(value, 0, 127);
DEBUG_MSG("MSG: % s\tChannel: % u\t\tCC: % u\tValue: % u\n", address, midichannel, cc, value);
MIDI1.sendControlChange(cc, value, midichannel);
change_midi_state(midichannel, cc, value);
@ -290,9 +312,17 @@ void MidiCCToOSC(uint8_t channel, uint8_t number, uint8_t val)
{
char buffer[1024];
snprintf(buffer, sizeof(buffer), " / midi / cc / % u / % u", channel, number);
if (channel < 1 && channel > 16)
return;
if (number > 127)
return;
val = constrain(val, 0, 127);
snprintf(buffer, sizeof(buffer), "/midi/cc/%d/%d", channel, number);
DEBUG_MSG("MidiCCToOsc: %s %f\n", buffer, val);
DEBUG_MSG("MidiCCToOsc: % s % f\n", buffer, val * 1.0);
change_midi_state(channel, number, val);
if (clientIP)
{
@ -302,12 +332,26 @@ void MidiCCToOSC(uint8_t channel, uint8_t number, uint8_t val)
udp.beginPacket(clientIP, UDP_SEND_PORT);
msg.send(udp);
udp.endPacket();
msg.empty();
}
else
{
DEBUG_MSG("No client IP.\n");
}
}
void change_midi_state(uint8_t midichannel, uint8_t cc, uint8_t value)
{
state[midichannel - 1][cc - 1] = value;
last_usage = millis();
DEBUG_MSG("Setting internal state of MIDI Channel %2d CC#%02d to %d\n", midichannel, cc, int8_t(value));
if (midistate[(midichannel - 1) * 128 + cc - 1] != int8_t(value))
{
midistate[(midichannel - 1) * 128 + cc - 1] = int8_t(value);
write_state = true;
}
}
void show_midi_state(void)
@ -316,52 +360,227 @@ void show_midi_state(void)
DEBUG_MSG("Current MIDI state:\n");
listDir(SPIFFS, "/", 1);
for (m = 0; m < 16; m++)
{
DEBUG_MSG("MIDI-Channel %d\n", m + 1);
DEBUG_MSG("MIDI-Channel %2d\n", m + 1);
for (c = 0; c < 128; c++)
{
if (state[m][c] >= 0)
if (midistate[m * 128 + c] >= 0)
{
DEBUG_MSG("\tCC: %03d = %03d\n", c, state[m][c]);
DEBUG_MSG("\tCC: %3d = %3d\n", c + 1, midistate[m * 128 + c]);
}
}
}
}
/*
void set_midi_state(void)
void check_mode(void)
{
if (ap_mode_state != digitalRead(AP_MODE_PIN))
{
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Changing mode...");
delay(500);
ESP.restart();
}
}
void check_write_state(void)
{
DEBUG_MSG("Checking midi state.\n");
if (write_state == true && millis() - last_usage > LAST_USAGE_TIMER)
{
uint8_t tmp;
uint8_t m;
uint16_t i;
write_midistate(1);
write_state = false;
show_midi_state();
}
}
DEBUG_MSG("Send MIDI state:\n");
void write_midistate(uint8_t setup_number)
{
DEBUG_MSG("Write MIDI state\n");
for (m = 0; m < 16; m++);
for (uint8_t m = 0; m < 16; m++)
{
if (state[m].size() > 0)
for (uint8_t c = 0; c < 128; c++)
{
DEBUG_MSG("MIDI-Channel %d\n");
for (i = 0; i < state[m].size(); i++)
if (midistate[m * 128 + c] >= 0)
{
tmp = state[m].get(i);
MIDI1.sendControlChange(tmp >> 4, tmp & 0x0f, m);
DEBUG_MSG("\tSet state of CC: %03d = %03d\n", tmp >> 4, tmp & 0x0f);
DEBUG_MSG("Found MIDI Channel %2d, CC#%2d = %3d\n", m, c, midistate[m * 128 + c]);
char midi_cc_name[33];
int16_t tmp_val;
sprintf(midi_cc_name, "/%d/midistate/%d/%d", setup_number, m, c);
if (SPIFFS.exists(midi_cc_name))
{
File midi_cc = SPIFFS.open(midi_cc_name, "r");
if (midi_cc)
{
tmp_val = midi_cc.read();
DEBUG_MSG("Data for MIDI Channel %d, CC %d exists: %d\n", m, c, tmp_val);
}
close(midi_cc);
}
else
{
tmp_val = -1;
}
if (midistate[m * 128 + c] != tmp_val)
{
DEBUG_MSG(" Change from %d to %d detected\n", tmp_val, midistate[m * 128 + c]);
File midi_cc = SPIFFS.open(midi_cc_name, "w");
if (midi_cc)
{
midi_cc.write(midistate[m * 128 + c]);
midi_cc.flush();
DEBUG_MSG("Wrote %d to %s.\n", midistate[m * 128 + c], midi_cc_name);
}
else
DEBUG_MSG("Cannot write to %s.\n", midi_cc_name);
close(midi_cc);
}
}
}
}
}
void read_midistate(uint8_t setup_number)
{
char setup_dir_name[33];
DEBUG_MSG("Read MIDI state\n");
sprintf(setup_dir_name, "/%d/midistate", setup_number);
File midistate_dir = SPIFFS.open(setup_dir_name);
if (!midistate_dir)
{
DEBUG_MSG("Failed to open directory %s.\n", setup_dir_name);
return;
}
*/
void check_mode(void)
if (!midistate_dir.isDirectory())
{
DEBUG_MSG("%s is not a directory.\n", setup_dir_name);
return;
}
File channel_cc = midistate_dir.openNextFile();
while (channel_cc)
{
DEBUG_MSG("Trying %s (Size: %d)\n", channel_cc.name(), channel_cc.size());
if (uint8_t(channel_cc.size()) == 1)
{
if (!channel_cc.isDirectory())
{
char tmp_name[33];
uint8_t midi_channel;
uint8_t midi_cc;
uint8_t count = 0;
DEBUG_MSG("Using %s\n", channel_cc.name());
strcpy(tmp_name, channel_cc.name());
for (String part = strtok(tmp_name, "/"); part; part = strtok(NULL, "/"))
{
count++;
if (count == 3)
midi_channel = atoi(part.c_str());
else if (count == 4)
midi_cc = atoi(part.c_str());
}
File cc = SPIFFS.open(channel_cc.name(), "r");
if (cc)
{
int8_t val = cc.read();
DEBUG_MSG(" MIDI-Channel %d CC#%d = %d\n", midi_channel + 1, midi_cc + 1, val);
midistate[midi_channel * 128 + midi_cc] = val;
MidiCCToOSC(midi_channel + 1, midi_cc + 1, val);
cc.close();
}
}
}
else
{
DEBUG_MSG("Removing %s: not the right size.\n", channel_cc.name());
SPIFFS.remove(channel_cc.name());
}
channel_cc = midistate_dir.openNextFile();
}
}
void check_reset_ap_data(void)
{
if (ap_mode_state != digitalRead(AP_MODE_PIN))
if (digitalRead(AP_DATA_RESET_PIN) == HIGH && last_reset_ap_check == true)
{
DEBUG_MSG("Reset AP data\n");
WiFiManager wm;
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Changing mode...");
delay(500);
lcd.print("Firmware reset");
wm.resetSettings();
SPIFFS.format();
lcd.setCursor(0, 1);
lcd.print("Done.");
delay(1000);
ESP.restart();
}
else if (digitalRead(AP_DATA_RESET_PIN) == HIGH)
{
if (digitalRead(AP_DATA_RESET_PIN) == HIGH)
DEBUG_MSG("Reset AP data pressed\n");
last_reset_ap_check = true;
}
else
last_reset_ap_check = false;
}
void ping(OSCMessage & msg, int offset)
{
if (clientIP)
read_midistate(1);
}
void listDir(fs::FS & fs, const char * dirname, uint8_t levels) {
Serial.printf("Listing directory: %s\r\n", dirname);
File root = fs.open(dirname);
if (!root) {
Serial.println("- failed to open directory");
return;
}
if (!root.isDirectory()) {
Serial.println(" - not a directory");
return;
}
File file = root.openNextFile();
while (file) {
if (file.isDirectory()) {
Serial.print(" DIR : ");
Serial.println(file.name());
if (levels) {
listDir(fs, file.name(), levels - 1);
}
} else {
Serial.print(" FILE: ");
Serial.print(file.name());
Serial.print("\tSIZE: ");
Serial.println(file.size());
}
file = root.openNextFile();
}
}

Loading…
Cancel
Save