diff --git a/OSC2MIDI.ino b/OSC2MIDI.ino index 1f45bc3..d6b20a8 100644 --- a/OSC2MIDI.ino +++ b/OSC2MIDI.ino @@ -69,6 +69,9 @@ #define ENC_BUTTON_PIN 32 #define ENC_A_PIN 34 #define ENC_B_PIN 35 +#define MAX_CLIENTS 5 +#define KRATE_CHECK_CLIENTIP 2000 +#define CLIENT_TIMEOUT 10000 void OSCToMidiCC(OSCMessage &msg, int offset); void OSCMixerMuteToMidiCC(OSCMessage &msg, int offset); @@ -83,7 +86,8 @@ void ConfigAPStarted(WiFiManager* wm); void check_mode(void);*/ WiFiUDP udp; -IPAddress clientIP; +IPAddress clientIP[MAX_CLIENTS]; +uint32_t clientIP_time[MAX_CLIENTS]; LiquidCrystal_I2C lcd(LCD_I2C_ADDR, LCD_COL, LCD_ROW); HardwareSerial midi1(2); // RX: 16, TX: 17 #ifndef D5 @@ -102,7 +106,7 @@ bool last_reset_ap_check = false; looper sched; bool write_state = false; uint32_t last_usage = millis(); -bool broadcast_send = false; +bool broadcast_send[MAX_CLIENTS]; uint32_t midi_ignore_volume_cc = 0; ESP32Encoder enc; @@ -124,9 +128,12 @@ void setup() DEBUG_MSG("\n"); - memset(midistate_cc, -1, 16 * 128); - memset(midistate_mute, -1, 16); - memset(midistate_solo, -1, 16); + memset(midistate_cc, -1, 16 * 128 * sizeof(uint8_t)); + memset(midistate_mute, -1, 16 * sizeof(uint8_t)); + memset(midistate_solo, -1, 16 * sizeof(uint8_t)); + memset(clientIP, 0, MAX_CLIENTS * sizeof(IPAddress)); + memset(clientIP_time, 0, MAX_CLIENTS * sizeof(uint8_t)); + memset(broadcast_send, false, MAX_CLIENTS * sizeof(uint8_t)); Serial.setDebugOutput(true); Serial.println(F("OSC2MIDI (c)2020 H. Wirtz ")); @@ -246,7 +253,9 @@ void setup() sched.addJob(check_mode, KRATE_MODE); sched.addJob(check_reset_ap_data, KRATE_RESET_AP_DATA); sched.addJob(check_write_state, KRATE_CHECK_WRITE_STATE); + sched.addJob(check_clientIPs, KRATE_CHECK_CLIENTIP); sched.addJob(enc_show, 1000); + sched.addJob(show_clientIP, 5000); //sched.addJob(show_midi_state, KRATE_STATE); listDir(SPIFFS, "/", 1); @@ -268,14 +277,16 @@ void loop() { IPAddress tmpIP = udp.remoteIP(); // Keep track of the client IP address for "talking back" - if (clientIP != tmpIP) + uint8_t slot; + if (slot = check_for_clientIP(tmpIP) < 0) { - clientIP = tmpIP; - DEBUG_MSG("New connection from: %d.%d.%d.%d\n", clientIP[0], clientIP[1], clientIP[2], clientIP[3]); + slot = add_clientIP(tmpIP); + DEBUG_MSG("New connection from: %d.%d.%d.%d\n", tmpIP[0], tmpIP[1], tmpIP[2], tmpIP[3]); } // Check if there are any OSC packets to handle udp.read(buffer, size); msg.fill(buffer, size); + clientIP_time[slot] = millis(); if (!msg.hasError()) { @@ -286,6 +297,33 @@ void loop() msg.route("/ping", ping); //msg.route("/midi/sysex", OSCToMidiSYSEX); //msg.route("/midi/note", OSCToMidiNote); + + char address[100]; + msg.getAddress(address, 0, sizeof(address)); + if (strcmp(address, "/ping")) + { + DEBUG_MSG("Broadcasting [%s] from %d.%d.%d.%d to\n", address, tmpIP[0], tmpIP[1], tmpIP[2], tmpIP[3]); + for (uint8_t i = 0; i < MAX_CLIENTS; i++) + { + if (clientIP_time[i] > 0 && clientIP[i]) + { + if (!same_ip(tmpIP, clientIP[i])) + { + udp.beginPacket(clientIP[i], UDP_SEND_PORT); + udp.write(buffer, size); + udp.endPacket(); + udp.flush(); + DEBUG_MSG("\t%d.%d.%d.%d\n", clientIP[i][0], clientIP[i][1], clientIP[i][2], clientIP[i][3]); + } + } + } + } + else + { + int8_t slot = check_for_clientIP(tmpIP); + if (slot >= 0) + clientIP_time[slot] = millis(); + } } else { @@ -435,30 +473,26 @@ void MidiCCToOSC(uint8_t channel, uint8_t number, uint8_t val) if (number == 7) if (millis() - MIDI_IGNORE_VOLUME_CC < midi_ignore_volume_cc) return; - else - DEBUG_MSG("\n!!!%d - %d!!!\n\n", millis() - MIDI_IGNORE_VOLUME_CC, midi_ignore_volume_cc); snprintf(buffer, sizeof(buffer), "/midi/cc/%d/%d", channel, number); - DEBUG_MSG("MidiCCToOsc: %s %f\n", buffer, val); - change_midistate_cc(channel, number, val); - if (clientIP) - { - OSCMessage msg = OSCMessage(buffer); - msg.add(val); - - udp.beginPacket(clientIP, UDP_SEND_PORT); - msg.send(udp); - udp.endPacket(); + OSCMessage msg = OSCMessage(buffer); + msg.add(val); - msg.empty(); - } - else + for (uint8_t i = 0; i < MAX_CLIENTS; i++) { - DEBUG_MSG("No client IP.\n"); + if (clientIP_time[i] > 0 && clientIP[i]) + { + udp.beginPacket(clientIP[i], UDP_SEND_PORT); + msg.send(udp); + udp.endPacket(); + udp.flush(); + DEBUG_MSG("Sending MidiCCToOsc: %s %f to %d.%d.%d.%d:%d\n", buffer, val, clientIP[i][0], clientIP[i][1], clientIP[i][2], clientIP[i][3], UDP_SEND_PORT); + } } + //msg.empty(); } void MidiMuteToOSC(uint8_t channel, uint8_t val) @@ -471,23 +505,21 @@ void MidiMuteToOSC(uint8_t channel, uint8_t val) snprintf(buffer, sizeof(buffer), "/midi/mixer/mute/%d", channel); - DEBUG_MSG("MidiMuteToOsc: %s %f\n", buffer, val); + OSCMessage msg = OSCMessage(buffer); + msg.add(val); - if (clientIP) + for (uint8_t i = 0; i < MAX_CLIENTS; i++) { - OSCMessage msg = OSCMessage(buffer); - msg.add(val); - - udp.beginPacket(clientIP, UDP_SEND_PORT); - msg.send(udp); - udp.endPacket(); - - msg.empty(); - } - else - { - DEBUG_MSG("No client IP.\n"); + if (clientIP_time[i] > 0 && clientIP[i]) + { + udp.beginPacket(clientIP[i], UDP_SEND_PORT); + msg.send(udp); + udp.endPacket(); + udp.flush(); + DEBUG_MSG("Sending MidiMuteToOsc: %s %f to %d.%d.%d.%d\n", buffer, val, clientIP[i][0], clientIP[i][1], clientIP[i][2], clientIP[i][3]); + } } + //msg.empty(); } void MidiSoloToOSC(uint8_t channel, uint8_t val) @@ -501,23 +533,21 @@ void MidiSoloToOSC(uint8_t channel, uint8_t val) snprintf(buffer, sizeof(buffer), "/midi/mixer/solo/%d", channel); - DEBUG_MSG("MidiSoloToOsc: %s %f\n", buffer, float(val)); - - if (clientIP) - { - OSCMessage msg = OSCMessage(buffer); - msg.add(val); - - udp.beginPacket(clientIP, UDP_SEND_PORT); - msg.send(udp); - udp.endPacket(); + OSCMessage msg = OSCMessage(buffer); + msg.add(val); - msg.empty(); - } - else + for (uint8_t i = 0; i < MAX_CLIENTS; i++) { - DEBUG_MSG("No client IP.\n"); + if (clientIP_time[i] > 0 && clientIP[i]) + { + udp.beginPacket(clientIP[i], UDP_SEND_PORT); + msg.send(udp); + udp.endPacket(); + udp.flush(); + DEBUG_MSG("Sending MidiSoloToOsc: %s %f to %d.%d.%d.%d\n", buffer, val, clientIP[i][0], clientIP[i][1], clientIP[i][2], clientIP[i][3]); + } } + //msg.empty(); } void change_midistate_cc(uint8_t midichannel, uint8_t cc, uint8_t value) @@ -1014,7 +1044,7 @@ void broadcast_midistate(void) { uint8_t m, c; - DEBUG_MSG("Broadcast MIDI CC state : \n"); + DEBUG_MSG("Broadcast MIDI CC state:\n"); for (m = 0; m < 16; m++) { @@ -1063,10 +1093,13 @@ void check_reset_ap_data(void) void ping(OSCMessage & msg, int offset) { - if (clientIP && broadcast_send == false) + for (uint8_t i = 0; i < MAX_CLIENTS; i++) { - broadcast_send = true; - broadcast_midistate(); + if (clientIP[i] && broadcast_send[i] == false) + { + broadcast_send[i] = true; + broadcast_midistate(); + } } } @@ -1125,6 +1158,78 @@ void enc_show(void) DEBUG_MSG(" button[%d] = %d\n", ENC_BUTTON_PIN, (uint8_t)digitalRead(ENC_BUTTON_PIN)); } +void check_clientIPs(void) +{ + for (uint8_t i = 0; i < MAX_CLIENTS; i++) + { + if (clientIP_time[i] > 0) + { + if (millis() - clientIP_time[i] > CLIENT_TIMEOUT) + { + DEBUG_MSG("Removing client slot %d: %d.%d.%d.%d\n", i, clientIP[i][0], clientIP[i][1], clientIP[i][2], clientIP[i][3]); + clientIP[i] = {0, 0, 0, 0}; + clientIP_time[i] = 0; + broadcast_send[i] = false; + } + } + } +} + +int8_t check_for_clientIP(IPAddress ip) +{ + for (uint8_t i = 0; i < MAX_CLIENTS; i++) + { + if (same_ip(clientIP[i], ip)) + return (i); + } + return (-1); +} + +uint8_t add_clientIP(IPAddress ip) +{ + uint8_t oldest = 0; + + for (uint8_t i = 0; i < MAX_CLIENTS; i++) + { + if (clientIP_time[i] > 0) + { + for (uint8_t n = 0; n < MAX_CLIENTS; n++) + { + if (clientIP_time[i] < clientIP_time[n]) + oldest = i; + } + } + else + { + oldest = i; + break; + } + } + clientIP[oldest] = ip; + clientIP_time[oldest] = millis(); + DEBUG_MSG("Adding client slot %d: %d.%d.%d.%d\n", oldest, clientIP[oldest][0], clientIP[oldest][1], clientIP[oldest][2], clientIP[oldest][3]); + return (oldest); +} + +void show_clientIP(void) +{ + DEBUG_MSG("Connected clients:\n"); + + for (uint8_t i = 0; i < MAX_CLIENTS; i++) + { + if (clientIP[i]) + DEBUG_MSG("Slot %d: %d.%d.%d.%d - %ld\n", i, clientIP[i][0], clientIP[i][1], clientIP[i][2], clientIP[i][3], clientIP_time[i]); + } +} + +bool same_ip(IPAddress ip1, IPAddress ip2) +{ + if (ip1[0] == ip2[0] && ip1[1] == ip2[1] && ip1[2] == ip2[2] && ip1[3] == ip2[3]) + return (true); + else + return (false); +} + void listDir(fs::FS & fs, const char * dirname, uint8_t levels) { DEBUG_MSG("Listing directory : %s\n", dirname);