attwad / python-osc

Open Sound Control server and client in pure python
The Unlicense
517 stars 108 forks source link

OSC over SlipSerial #48

Open gratefulfrog opened 7 years ago

gratefulfrog commented 7 years ago

Hi,

Thanks so much for your cool OSC implementation!

I am sorry to bother you but I am trying to communicate with an arduino-like device sending OSC messages over SLIP Serial protocol.

My goal is to receive and interpret the OSC messages via serial in python3. In other words, the device would build an OSC message, then send it SLIP encoded on the serial line to my linux box. In python3, I would read a SLIPencoded message and convert it back to the original OSC message and then be able to extract the data fields from it.

I was not able to see a way of using your python-osc to interpret the messages coming in over python slipSerial.

Would you be able to help me code something like the following?

import SerialComm 
from pythonosc import dispatcher
from pythonosc import osc_server

s = SerialComm.connectToSerialPort('/dev/ttyACM0')
dispatcher = dispatcher.Dispatcher()
dispatcher.map("/test", print)
server = osc_server.SlipSerialServer(s, dispatcher);  ## THIS SERVER is what is needed ##
server.serve_forever()

Thanks so much, Bob

jsheedy commented 7 years ago

Right now all the servers are UDP network services. Most derive from osc_server.OSCUDPServer, where the builtin socketserver passes incoming datagrams containing raw OSC packets to its handle method, which eventually passes the OSC packets to osc_server._call_handlers_for_packet.

You could try writing a new class that doesn't derive from osc_server.OSCUDPServer, but captures OSC packets from SerialComm and passes them to the same _call_handlers_from_packet that others do. Are you able to obtain individual raw OSC packets from SerialComm? Once you have that, decide whether to run your service in a thread, process, or coroutine. I have successfully used pyserial in a coroutine, but threading might be more straightforward.

gratefulfrog commented 7 years ago

Thanks for your advice!

I am embarrassed to say that all that is a bit over my level of expertise.

I did manage to get a working experimental version though!

I run an Arduino which simply sends an OSC message over SLIP serial via usb to my linux pc. The code for that is ultra simple and available here https://github.com/gratefulfrog/SPI/tree/master/RPI/Python/Old/OSC_SLIP_TESTS/Arduino_OscSlip_00 .

On the pc, I have a tiny bit of python3 code that uses some of your pythonosc, and serial_asyncio, and a few bits that I found on Internet from OSC.py https://github.com/gratefulfrog/SPI/blob/master/RPI/Python/Old/OSC_SLIP_TESTS/PythonSlip/OSC.py (by Daniel Holth, Clinton McChesney). I had to fix up that code to get it to work properly, but at least that was within my comfort zone ;-)

So this test code https://github.com/gratefulfrog/SPI/blob/master/RPI/Python/Old/OSC_SLIP_TESTS/PythonSlip/asyncosc.py can read and interpret the incoming OSC messages. I wasn't able to get your dispatcher to work, though.

If you have any thoughts, they would be more than welcome!

Thanks already!

Cheers, Bob

On Fri, Oct 27, 2017 at 7:20 PM, Joseph L. Sheedy notifications@github.com wrote:

Right now all the servers are UDP network services. Most derive from osc_server.OSCUDPServer https://github.com/attwad/python-osc/blob/2cb6e669df94c4d909b4af32450ac848bc74beb4/pythonosc/osc_server.py#L80, where the builtin socketserver https://docs.python.org/3/library/socketserver.html passes incoming datagrams containing raw OSC packets to its handle method, which eventually passes the OSC packets to osc_server._call_handlers_for_packet https://github.com/attwad/python-osc/blob/2cb6e669df94c4d909b4af32450ac848bc74beb4/pythonosc/osc_server.py#L43 .

You could try writing a new class that doesn't derive from osc_server.OSCUDPServer, but captures OSC packets from SerialComm and passes them to the same _call_handlers_from_packet that others do. Are you able to obtain individual raw OSC packets from SerialComm? Once you have that, decide whether to run your service in a thread, process, or coroutine. I have successfully used pyserial in a coroutine, but threading might be more straightforward.

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/attwad/python-osc/issues/48#issuecomment-340032669, or mute the thread https://github.com/notifications/unsubscribe-auth/AB1Bl43UakITnXOgds-CrsH6LDzw7zoIks5swhDwgaJpZM4QJIig .

attwad commented 7 years ago

Hi there, glad that you managed to have a working prototype :) A few thoughts on the actual potential merge of a PR: a new server_osc impl written using pySerial internally would be great but the pyslip from https://github.com/mehdisadeghi/pyslip is not because the quality of this library is quite unappealing (no tests being my biggest concern, and last commit being from 4 years ago I am unsure how well it behaves with python 3.4+). I have no personal experience with the SLIP serial protocol but if people find it useful then I'm all for it, perhaps in a contrib/ package, else I believe it would be better for this to live in your own fork or separate library.

gratefulfrog commented 7 years ago

Hi again!

Thanks for your support.

I guess my previous email was a bit brief and hard to understand.

I did not use the pyslip at all. I simply wrote a small SLIP converter myself, based on stuff I found (see below). SLIP is incredibly simple to understand https://en.wikipedia.org/wiki/Serial_Line_Internet_Protocol. The only modules used to implement the prototype server are: import asyncio import serial_asyncio from pythonosc import dispatcher,osc_server, osc_message from OSC import decodeOSC # updated to python3 by me

So you see, it would not depend on poor quality code, but on genuine python modules from python.org, plus a bit of code that would be needed.

If you Google: Python OSC Serial, or Python OSC SLIP serial, you will see that many people are looking for this solution. The maker community, i.e. Arduino etc. could all use this because we are constantly faced with the question of how to send binary data over serial to a python application.

I would be happy to try myself, but I have no experience with co-routines, asyncio, etc. and the time it would take for me is huge, compared to the time I have... sadly... The only technical issue I ran into was my inability to get your dispatcher to work in my prototype. I just gave up since I need to integrate the prototype into my main code...

So perhaps, I've convinced you to write the async slip serial server? Again the reasons:

Have you ever shared your code with Adrien Freed, he's a really nice guy and a big maker and I'm sure he'd be interested!

In any case, Thank you so much for your help and your great code! Cheers, Bob

SLIP decoder code:

SLIP_END = 0o300 SLIP_ESC = 0o333 SLIP_ESC_END = 0o334 SLIP_ESC_ESC = 0o335

def getSerialByte(serialFD): next(serialFD)

def decodeFromSLIP(bytes, dataBuffer = []): """ arguments are bytes to decode and current decoded list if any return onGoing, Current where onGoing is True if the Current is not complete and onGoing is False if the Current is complete """ serialFD=iter(bytes) try: while True: serialByte = next(serialFD) if serialByte is None: raise ProtocolError elif serialByte == SLIP_END: if len(dataBuffer) > 0: return False,dataBuffer elif serialByte == SLIP_ESC: serialByte = next(serialFD) if serialByte is None: raise ProtocolError elif serialByte == SLIP_ESC_END: dataBuffer.append(SLIP_END) elif serialByte == SLIP_ESC_ESC: dataBuffer.append(SLIP_ESC) else: raise ProtocolError else: dataBuffer.append(serialByte) except StopIteration: return True, dataBuffer

On Sat, Oct 28, 2017 at 10:26 AM, attwad notifications@github.com wrote:

Hi there, glad that you managed to have a working prototype :) A few thoughts on the actual potential merge of a PR: a new server_osc impl written using pySerial internally would be great but the pyslip from https://github.com/mehdisadeghi/pyslip is not because the quality of this library is quite unappealing (no tests being my biggest concern, and last commit being from 4 years ago I am unsure how well it behaves with python 3.4+). I have no personal experience with the SLIP serial protocol but if people find it useful then I'm all for it, perhaps in a contrib/ package, else I believe it would be better for this to live in your own fork or separate library.

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/attwad/python-osc/issues/48#issuecomment-340159409, or mute the thread https://github.com/notifications/unsubscribe-auth/AB1Bl6UcrjCjkaAI8ToAW-dcTaqiVE0Mks5swuUagaJpZM4QJIig .

attwad commented 7 years ago

@gratefulfrog Thanks for the thorough reply and sharing your code, it does seem feasible indeed and it would be beneficial to the makers community. As I said I have no experience with PySerial/SLIP so I'd prefer someone who actually uses it to provide an implementation, with the bootstrap you provided I expect it'd be fairly easy for newcomers to do it so I'm going to leave this issue open and mark is as a good first time contribution :)

gratefulfrog commented 7 years ago

@attwad Thanks for your kind words! Your encouragement helped me move forward!

In the meantime I made a little demo of a python OSC SLIP server. I did not use threading so that more people could understand and potentially improve it.

I hope this helps!

Thanks again for you great code and kind support! Cheers, Bob