hideakitai / ArduinoOSC

OSC subscriber / publisher for Arduino
MIT License
207 stars 20 forks source link

Receive an Udp Osc message as reply to a previus Osc message #18

Closed ClaudioCas closed 3 years ago

ClaudioCas commented 3 years ago

Hi, I would like to interact with my digital mixer using Osc messages. I'm able to send message, but I cannot read any reply form the server.

My scenario is the following: I send an OSC message to mixer (192.168.1.33 port 10024) and Mixer reply to my Arduino IP (192.168.1.199) but the mixer use the Remote UDP Port that Arduino use for send the message.

WhatsApp Image 2021-03-18 at 14 57 05

I sniff some udp packet and seems that OscEther.send use a local port (random probably, port 9 in my test) that is not releated to OscEtherServer [server.begin(serverPort)]. Can you give me some advices?

#include <ArduinoOSC.h>

byte myMac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
byte myIp[]  = { 192, 168, 1, 199 };

int serverPort  = 8888; //this is not right in my scenario

const char* mixerIp = "192.168.1.33";
const int mixerPort = 10024;

// required to use manual packet parsing
OscEtherServer server;

void setup()
{
    Serial.begin(115200);
    Ethernet.begin(myMac, myIp);
    server.begin(serverPort); // <--- I should listen reply on the same port that I use to send UDP request...
}

void loop()
{
    // manual sending instead of publishers
    static uint32_t prev_func_ms = millis();
    if (millis() > prev_func_ms + 2500)
    {
        prev_func_ms = millis();
        OscEther.send(mixerIp, mixerPort, "/info");
    }

    // manual parsing instead of subscribers
    if (server.parse())
    { 
      // I did not receive nothing on "serverPort" because is not releated with Oscether.send() Udp Local Port...
        Serial.println("server.parse()");
        const OscMessage* msg = server.message();
    }
}
hideakitai commented 3 years ago

@ClaudioCas Hi, sorry for the late reply. I've checked if your code works in my environment, but it worked fine. But I've used max patch in my example instead of mixer. I've just changed the ip and the port.

Could you check if your AVR board version is 1.8.3 (latest) and firewall setting? (if you are using windows)

#include <ArduinoOSC.h>

byte myMac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
byte myIp[]  = { 192, 168, 0, 201 };

int serverPort  = 10024; //this is not right in my scenario

const char* mixerIp = "192.168.0.12";
const int mixerPort = 10024;

// required to use manual packet parsing
OscEtherServer server;

void setup()
{
    Serial.begin(115200);
    Ethernet.begin(myMac, myIp);
    server.begin(serverPort); // <--- I should listen reply on the same port that I use to send UDP request...
}

void loop()
{
    // manual sending instead of publishers
    static uint32_t prev_func_ms = millis();
    if (millis() > prev_func_ms + 2500)
    {
        prev_func_ms = millis();
        OscEther.send(mixerIp, mixerPort, "/info");
    }

    // manual parsing instead of subscribers
    if (server.parse())
    { 
      // I did not receive nothing on "serverPort" because is not releated with Oscether.send() Udp Local Port...
        Serial.println("server.parse()");
        const OscMessage* msg = server.message();
    }
}
hideakitai commented 3 years ago

I think it's good to try your code with another one (not mixer but other OSC app) if you still have the problem.

I will close this issue but please feel free to comment about it.

ClaudioCas commented 3 years ago

I need that the code works for mixer, not for other application. I have the latest Arduino Ide (2.0.0 beta4) with Avr 1.8.3. There are no issue with firewall since Arduino and the Mixer talk directly via Ethernet. I have a Behringer XAir Mixer series (xr18) and It act like a server and it reply on the local port that the arduino use to send Udp messages, but I cannot see any releation with server.begin(serverPort) and OscEther.send function, since you can use OscEther without create any OscEtherServer... so how can I listen the reply from the mixer if I send my command without specify the localPort?

hideakitai commented 3 years ago

By testing with another app with same send/receive ports, you can check where is the problem. If it works with another app, the problem is maybe the mixer.

Because it works in my environment with Max patch, I think it's not problem of library.

In my library, send and receive use different udp instances, send instance listen the port 9 (discard port). And receive instance doesn't send any packet and just listen the port you specify in server.begin().

hideakitai commented 3 years ago

One possibility I thought is the format of the OSC message. Could you tell me the format of OSC from your mixer?

ClaudioCas commented 3 years ago

Dear Hideaki Tai, first of all, thanks for your replies. The issue is not related with Behringer mixer or Osc message. The mixer works via OSC messages with the official app and 3rd party application.

With ArduinoOSC I can correctly send message to mixer (change fader volume, mute channel, ecc), but I cannot receive any information from it.

The scenario probably can be describe like that:

Device A Server ` //Server listen on recv_port and reply to m.remotePort() of device B OscEther.subscribe(recv_port, "/command/needReply", [](OscMessage& m) {

    Serial.println("/need/reply");

    int i = millis();
    float f = (float)micros() / 1000.f;
    String s = "hello";

    OscEther.send(host, m.remotePort(), "/reply", i, f, s);

});`

Device B Client

//Device B send a command to server and need to receive a reply, the server use the local port (probably dinamically chosen?) //that OscEther of device B

OscEther.send(server_host, recv_port, "/command/needReply");

//here I don't know what is the local port of OscEther
hideakitai commented 3 years ago

Ok, I understand your scenario and mixer works with official and 3rd party app. But there are many unknown things, please let me know about following your ideal settings.

ClaudioCas commented 3 years ago

Dear Hideaki Tai, I did a lot of steps on my project. Here the details.

Here you can find all the details about x32 protocol https://www.academia.edu/9709659/UNOFFICIAL_X32_OSC_REMOTE_PROTOCOL at page 6 Screenshot_2021-04-06 Microsoft Word - X32 unofficial OSC - UNOFFICIAL_X32_OSC_REMOTE_PROTOCOL pdf

Patrick-Gilles Maillot also develop a lot of tools include an X32 Emulator https://sites.google.com/site/patrickmaillot/x32#h.p_rE4IH0Luimc0 that emulate the Mixer.

Below the full example sketch with ArduinoOSC | "Sketch A2"

#include <ArduinoOSC.h>

byte myMac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
byte myIp[]  = { 192, 168, 1, 40};

//this is the issue, I need to use the same UdpServer as sender for messages without add new one with another port.
int serverPort  = 10024; 

const char* mixerIp = "192.168.1.46"; //here you can write your PC address with X32Emu
const int mixerPort = 10023; //this port is for x32 Emu

// required to use manual packet parsing
OscEtherServer server;

void setup()
{
    Serial.begin(115200);
    Ethernet.begin(myMac, myIp);
    server.begin(serverPort); // <--- I should listen reply on the same port that I use to send UDP request...
}

void loop()
{
    // Send the same command /info every 2.5 seconds
    static uint32_t prev_func_ms = millis();
    if (millis() > prev_func_ms + 2500)
    {
        Serial.println("Send command /info");
        prev_func_ms = millis();
        OscEther.send(mixerIp, mixerPort, "/info");
    }
    //check for incoming messages, but the InPort is not the same that I use to send the /info command
    if (server.parse())
    { 
      // I did not receive nothing on "serverPort" because is not releated with Oscether.send() Udp Local Port...
        Serial.println("server.parse()");
        const OscMessage* msg = server.message();
    }
}

The x32 emulator recevie the command and reply the information, but the packet is not received because the InPort is not the same localport that use ArduinoOSC to send the command.

test


after a while I found this simple project https://www.youtube.com/watch?v=OfSsz2KgyZk&t=0s and get the source somewhere on the web (I cannot find it anymore). I strip down all the code of the unnecessary things and do the same demo sketch and it works with an old osc library. "Sketch B2"


#include <Ethernet.h>
#include <EthernetUdp.h>
#include <OSCMessage.h>

// Setup IP addresses, ports, MAC
IPAddress InIP(192, 168, 1, 40);                    // ethernet board's IP address
IPAddress mixerIp(192, 168, 1, 46);                  // X32/X-Air IP address assigned in the mixer
int mixerPort = 10023;                                // Outport (X32=10023, X-Air=10024)
byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};  // arduino MAC address

//here!
int localPort = 12345;                                // The local port to send and receive message!

long PollDelay       = 400;      // Time between polling current state (400)
char sValue[25]       = "";

EthernetUDP OscUDP;                            // setup a UDP object and errorcode routine

char   PacketBuffer[UDP_TX_PACKET_MAX_SIZE];   // Setup global variables
String CommandStr;

void setup()
{
  Serial.begin(115200);      // debug

  Ethernet.begin(mac,InIP);  // Start ethernet on the shield

  OscUDP.begin(localPort);      // Start UDP object, listening on port InPort 

}

 void loop()
{ 

    // Send the same command /info every 2.5 seconds
    static uint32_t prev_func_ms = millis();
    if (millis() > prev_func_ms + 2500)
    {
        Serial.println("Send command /info");
        prev_func_ms = millis();
        OSCMessage msg("/info");

        OscUDP.beginPacket(mixerIp, mixerPort);
        msg.send(OscUDP);
        OscUDP.endPacket();
        msg.empty();
    }

    // Check if packet was received
    int size = OscUDP.parsePacket();
    if (size > 0)
    {
        // Get the packet
        OSCMessage msg;
        while (size--) msg.fill(OscUDP.read());
        // Parse return OSC command
        msg.getAddress(PacketBuffer,0);                 // get message path
        CommandStr = String(PacketBuffer);              // assign to CommandStr

        Serial.print("Response received: ");
        Serial.print(CommandStr);

        for(int i = 0; i < msg.size(); i++)
        {
          msg.getString(i,sValue);   // string
          Serial.print(", arg : ");
          Serial.print(i);
          Serial.print(" value :");
          Serial.print(sValue);
        }

        Serial.println("");
        msg.empty();

    }

} 

example2

I hope this can help you to improve your great library to support other scenarios like Behringer/Midas mixers! Thanks

hideakitai commented 3 years ago

@ClaudioCas I haven't got following info, so I can't debug it.

  • Network diagram (device name, ip, port, hubs, etc., and what is device A and B??)
  • Arduino IDE, board, and board version (Uno? ESP32? anything else?)
  • List of all OSC messages sent from mixer (in Uno, there are limitations: please read README)
  • Simple and ideal Arduino sketch which can reproduce the problem (not part of it)

But if you are using Uno, there is limitation as I mentioned above. ArduinoOSC discards long packet automatically. You can manage it manually so please read the README carefully.

ClaudioCas commented 3 years ago

Dear Hideaki Tai,

@ClaudioCas I haven't got following info, so I can't debug it.

  • Network diagram (device name, ip, port, hubs, etc., and what is device A and B??)

Here my network diagram Network Of course you need to modify the sketch in prev post with your IP address instead 192.168.1.49 (the windows machine with x32 emulator)

  • Arduino IDE, board, and board version (Uno? ESP32? anything else?)

I use Arduino Mega with Eth 5100 shield and Arduino IDE 2.0.0 beta 4

  • List of all OSC messages sent from mixer (in Uno, there are limitations: please read README)

The mixer can send a large number of messages, but you need to subscribe with special command and receive them with in push. In this simplified scenario I send a message and mixer reply to me (like polling or client/server mode). Arduino send a /info and mixer reply with /info with argouments. You can see the message flow in the x32 emulator window. If you are courius, you can download also the x32 mixer control app form https://www.behringer.com/product.html?modelCode=P0ASF and you can interact with x32 emulator as you have the real mixer.

  • Simple and ideal Arduino sketch which can reproduce the problem (not part of it)

In previous post you can find a full sketch that can you compile and test.

hideakitai commented 3 years ago

@ClaudioCas I don't have Mega and time to test now, but if you are using Arduino Mega, maybe you can parse packets by defining following preprocessors before #include <ArduinoOSC>.

#define ARDUINOOSC_MAX_ARGUMENT_SIZE 16
#define ARDUINOOSC_MAX_BLOB_BYTE_SIZE 256
ClaudioCas commented 3 years ago

Dear Hideaki Tai, the issue is not related with Arduino limitation, you can test with any arduino with ethernet. The simple reply message form mixer at "/info" is a normal message with 4 args, no blobs o complex types. The issue is related to in port number that is different to the local ones that use for send. You can test also the second sketch that works (but with the old library OSC).

hideakitai commented 3 years ago

@ClaudioCas I've already checked if the same send/receive port works with my app (Max8), and it worked fine (with Arduino Uno). What is the reason you think the problem is the port?

MAX_BLOB_BYTE_SIZE is the udp packet size limitation in bytes, so if the raw packet size is bigger than that, the packet will be discarded. Could you send me a captured file of OSC reply?

@ClaudioCas Hi, sorry for the late reply. I've checked if your code works in my environment, but it worked fine. But I've used max patch in my example instead of mixer. I've just changed the ip and the port.

Could you check if your AVR board version is 1.8.3 (latest) and firewall setting? (if you are using windows)

#include <ArduinoOSC.h>

byte myMac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
byte myIp[]  = { 192, 168, 0, 201 };

int serverPort  = 10024; //this is not right in my scenario

const char* mixerIp = "192.168.0.12";
const int mixerPort = 10024;

// required to use manual packet parsing
OscEtherServer server;

void setup()
{
    Serial.begin(115200);
    Ethernet.begin(myMac, myIp);
    server.begin(serverPort); // <--- I should listen reply on the same port that I use to send UDP request...
}

void loop()
{
    // manual sending instead of publishers
    static uint32_t prev_func_ms = millis();
    if (millis() > prev_func_ms + 2500)
    {
        prev_func_ms = millis();
        OscEther.send(mixerIp, mixerPort, "/info");
    }

    // manual parsing instead of subscribers
    if (server.parse())
    { 
      // I did not receive nothing on "serverPort" because is not releated with Oscether.send() Udp Local Port...
        Serial.println("server.parse()");
        const OscMessage* msg = server.message();
    }
}
ClaudioCas commented 3 years ago

You can see a wireshark captured packet in first post. I send a message to mixer and mixer reply to my local port (9) and not the "server port" 8888 that I write in the first sketch. The scenario is the same.

Please, use "Sketch A2", change ip address of your pc (and arduino if you have a different subnet) with your Arduino Uno. Run x32 emulator on your pc. You can see directly the send and response.

You can also test the Sketch B2 and see that arduino receive the reply.

hideakitai commented 3 years ago

I saw the captured packet and I've finally got what you mean... Please declare the client like this based on the new OscEtherUno example. Please note to update the ArduinoOSC to version to v0.3.18.

OscEtherClient client(1234);  // you can set the local port of client manually (default: 9)
ClaudioCas commented 3 years ago

Dear Hideaki Tai, well done, now with this extra configuration the code works! Thanks