OSC commands not fully sending unless OSCRouter is introduced

I have an OSC Out DAT that is suppose to send 5 commands. TD only sends one command, which is for tilt. Pan, x_focus, y_focus, and z_focus don’t send. If I change pan to pans, that sends. The second I route TD to OSCRouter and then route that into Protokol (the app im using to view the sent OSC), all works perfectly fine. That shouldn’t be. My OSC Out CHOP works fine and I have no issues. It’s only this DAT that’s causing me a headache. I have 5 separate CHOP Execute DAT’s with scripts to send that OSC command to my OSC Out DAT. Like I said, it works fine with OSCRouter in the mix, but without, only the tilt CHOP Execute DAT works. I dont get it. The full commands are listed below.

/eos/user/0/group/101/param/x_focus/release)
/eos/user/0/group/101/param/y_focus/release)
/eos/user/0/group/101/param/z_focus/release)
/eos/user/0/group/101/param/pan/release)
/eos/user/0/group/101/param/tilt/release)

Can you also post the scripts you are using to send the commands?

Absolutely. Here it is. After discussing with some folks, it turns out that i’m just sending raw commands and not padding it properly. After some digging and understanding how the padding process works, a little helo from chatgpt got me going. Here the before and after scripts

BEFORE:

def onOffToOn(channel, sampleIndex, val, prev):
    group_chop = op('Group_select')
    osc_out = op('EOS_Release_OSC_Out')

    if not group_chop or not osc_out:
        return

    group = int(group_chop['Group'][0])
    address = f"/eos/user/0/group/{group}/param/pan/release"
    osc_out.send(address, [])

AFTER:

def onOffToOn(channel, sampleIndex, val, prev):
    group_chop = op('Group_select')
    osc_out = op('EOS_Release_OSC_Out')

    if not group_chop or not osc_out:
        return

    if 'Group' not in [c.name for c in group_chop.chans()]:
        return

    try:
        group_val = int(group_chop['Group'][0])
    except:
        return

    # Create the OSC address string
    address = f"/eos/user/0/group/{group_val}/param/tilt/release"
    # Convert to bytes and null-terminate
    address_bytes = address.encode('utf-8') + b'\x00'
    # Pad to 4-byte boundary
    padding = (4 - len(address_bytes) % 4) % 4
    address_bytes += b'\x00' * padding

    # Send as raw bytes
    osc_out.sendBytes(address_bytes)

Then merged all into one CHOP Execute DAT:

def send_padded_osc(osc_out, addr):
    padded = addr.encode('utf-8') + b'\x00'
    padding_needed = (4 - len(padded) % 4) % 4
    padded += b'\x00' * padding_needed
    osc_out.sendBytes(padded)

def onOffToOn(channel, sampleIndex, val, prev):
    group_chop = op('Group_select')
    osc_out = op('EOS_Release_OSC_Out')

    if not group_chop or not osc_out:
        debug("Missing operator")
        return

    if 'Group' not in [c.name for c in group_chop.chans()]:
        debug("'Group' channel not found")
        return

    try:
        group_val = int(group_chop['Group'][0])
    except:
        debug("Invalid group")
        return

    addresses = [
        f"/eos/user/0/group/{group_val}/param/x_focus/release",
        f"/eos/user/0/group/{group_val}/param/y_focus/release",
        f"/eos/user/0/group/{group_val}/param/z_focus/release",
        f"/eos/user/0/group/{group_val}/param/pan/release",
        f"/eos/user/0/group/{group_val}/param/tilt/release"
    ]

    # Send each OSC message with a slight stagger
    for i, addr in enumerate(addresses):
        delay_frames = int(i * 0.00 * 60)  # 0ms delay per message at 60fps
        run(lambda a=addr: send_padded_osc(osc_out, a), delayFrames=delay_frames)

    debug(f"Sent 5 OSC commands with delay for Group {group_val}")

You should be using the sendOSC() method ideally, which handles the formatting for you.