Servo via Arduino via OSC to MAX

Annie's icon

Hi,

I want to control a servo motor (connected to an Arduino MEGA Board) via OSC in MAX MSP for a cool audio project.
I managed to read several potentiometers via OSC and to switch on LEDs, but I didn't find a way to dim these LEDS nor send values to my servo.

Using a code / patch I found to only switch between ON and OFF for the LEDs (But I'm not entirely sure which part of the code does this).
So I need something to actually send values to my LED or servo to make them fade.

Any chance to control outputs via OSC?
I can't use Maxuino or any other kind of workarounds, it's a university project and we're supposed to use OSC.

Thanks!

Annie

ANALOGER_OSC.maxpat
Max Patch
Source Audio's icon

OSC or serial or whatever.
You want first to learn how to control servo motor in Arduino,
or how to fade a LED. (pwm)
Forget about complicated tutorials, first learn LED fading using analog.write(value 0 - 255 here);
and Servo motor control using myServo.write( value 0 - 179 here);
When You've learned that, next step is how to send this 2 values from Max
to arduino to control servo motor movement of LED fading.
OSC in Arduino syntax has it's learning curve, but
If You are at university than they are supposed to teach You how to do stuff or ?

Annie's icon

Hey, thanks for the reply. I'm totally able to do all you said, but the tricky part starts with OSC. I was hoping for someone to tell me how to write a method in Arduino to control the dimming of an LED / servo via OSC.

Source Audio's icon

Hi, Annie.
That is what I mean.
If You don't know how to send and receive 2 values form Max to Arduino,
using whatever serial or OSC, then it gets difficult.
Did You read any tutorials, or tried any examples ?
I guess You are using CNMAT osc stuff, read the text at :

https://github.com/CNMAT/OSC
there is part titled "Receiving Data"
-----------
here is arduino sketch, that You could study :

#include
#include
#include
#ifdef BOARD_HAS_USB_SERIAL
#include
SLIPEncodedUSBSerial SLIPSerial( thisBoardsSerialUSB );
#else
#include
SLIPEncodedSerial SLIPSerial(Serial1);
#endif
Servo myServo;
int ledPin = 2;
void setup() {SLIPSerial.begin(9600); myServo.attach(3); pinMode(2, OUTPUT); }
// servo motor on pin 3, LED on pin 2 ...

void loop() { OSCBundle bundleIN; int size;
while(!SLIPSerial.endofPacket())
if( (size =SLIPSerial.available()) > 0)
{while(size--)bundleIN.fill(SLIPSerial.read());}
if(!bundleIN.hasError())
bundleIN.dispatch("/led", LEDcontrol); bundleIN.dispatch("/servo", SERVOcontrol)
}
void LEDcontrol(OSCMessage &msg)
{if (msg.isInt(0)) {analogWrite(ledPin, (msg.getInt(0));}}

void SERVOcontrol(OSCMessage &msg)
{if (msg.isInt(0)) { myServo.write((msg.getInt(1));}}

// this should receive /led 0 - 255 and /servo 0 - 180 and control LED brightness
// and Servo Motor position

Annie's icon

Hey,
great! Thank you so far.
This is actually what i was searching for!

I'm just struggling with my MAX Patch.

Right now i'm sending data from a (0-127) dial with "prepend led/0" via "opensoundcountrol"-object and the "openSlip"-object.
So i can see what you've changed but it's still not working with the communication between Max and Arduino.

Can you give me an example of how a working max patch could look like ?

that would be awesome!

Annie

Source Audio's icon

You should send exactly what is also expected in Arduino
/led and then value 0 -255.
So You need to change dial range to 256.
/led $1
for servo the same, just servo needs range 0 -180
------
message could contain the bang es well :
{/led $1, bang} going straight to OpenSoundControl
And please read that section on the link.
If You'd have done that, it would be clear to You that
/led/0 and then value is wrong, because only zero would be sent to LED pin, and not the following number.
I have simplified the receiving string to just /led and the value.
----------
here is Arduino patch, it seems that pasting here did remove some chars, very strange.

Source Audio's icon

The sketch :

FadeOSC.zip
zip
Annie's icon

Hey,
thanks for your help. I finally managed to control my servos and address them correctly. Now I'm trying to use the "preset" object to store different positions to easily recall values. It's not working properly, I reckon it's more a problem of power supply or the lack of using shields for the servos. I'll figure something out.
Big thanks again!
Cheers
-Annie

Source Audio's icon

Hi Annie, sorry to hear You are still struggling with that.
What is actually not working ?
Preset store/recall ? Or Motor Driving electronic ?
What is power demand of Your Servo Motors ?
You probably have to supply external power for them to work,
only very small motors can be driven directly from Arduino pins.

ygreq's icon

Hi, Source Audio!

I am trying to figure out the examples you sent here.

I am using Annie's code in Max and your code in Arduino with a servo attached.

I does not seem to work.
BTW, I had to change SLIPEncodedSerial SLIPSerial(Serial1); to SLIPEncodedSerial SLIPSerial(Serial);
I hope that was the right thing to do as I don't have a Serial1 in Arduino.

Any suggestions on making this work?
Thank you so much!

ygreq's icon

This is the patch I used:

Max Patch
Copy patch and select New From Clipboard in Max.

ygreq's icon

Ok. I finally managed to control the servo using the demo code in the SerialReceiveWithServo

Source Audio's icon

Do you really need all that OSC stuff ?
Servo only needs to receive single int for position control.
Can be done with Serial.parseInt() for any number of servos attatched.
here 3 servos example

#include <Servo.h>
Servo myservo1; Servo myservo2; Servo myservo3;
int pos1 = 0; int pos2 = 0; int pos3 = 0;
void setup() {Serial.begin(9600);
myservo1.attach(9); myservo2.attach(10);myservo3.attach(11);
}
void loop() {
if (Serial.available() > 0) {
int pos1 = Serial.parseInt(); int pos2 = Serial.parseInt(); int pos3 = Serial.parseInt();
if (Serial.read() == '\n') {
myservo1.write(pos1); myservo2.write(pos2); myservo3.write(pos3);
delay(10);}
}}

Max Patch
Copy patch and select New From Clipboard in Max.

ygreq's icon

In the end I would like to send data from Max to a robot running on arduino teensy + Wifi shield or ESP (don't know yet which one would be better). So the data will be sent wirelessly and there will be 5 motors (4 brushless motors -oDrive brand- and 1 stepper) to control. I was thinking that OSC would be useful in this case as it works via UTP and it parses multiple streams of data easily.

Do you think I can skip OSC also in the configuration mentioned above?

I want to control it via wifi and not via RC RF because I would also want to control it via internet so this way it is much easier.

Thank you for the patch and the help!! :)

PS: I am going to control the motors with a gamepad attached to the pc running Max

Source Audio's icon

Post the code you would use on arduino to run the motors,
ignoring which way control values would come in.
Make simple test run sketch with few values per motor
in a loop, test that everything works and then post it here.

I think you could do with single ESP32 board
but I can't tell more without seeing the code
for the motors.


ygreq's icon

This is the Max code I am using. It takes either a gamepad or a joystick (or an iPhone but that's a different story). You have the following options:
- set which controller to use
- set what type of tag to use: multiple slashes OSC tag, single slash OSC tag, no slash tag, or no tag at all
- set what type of output to have: UDP or Serial
- set to send either float or integer values
- set low and high values for the 2 joysticks the gamepad comes with or the values for the big joystick (different hardware)
The patch comes with a txt file for saving the settings via [coll]

I set all this just to know I have options. For now I tested the patch by controlling a servo and 2 LEDs. The servo I controlled with the x axis of the left joystick of the gamepad. And I controlled the LEDs (on and off) with the A and B buttons. I set the buttons to send either 0 or 255 as values. I am using a Logitech F310 gamepad.

Max Patch
Copy patch and select New From Clipboard in Max.


12, l 5;
6, f 6;
1, a 0;
2, b 1;
11, k 1;
4, d 192.168.100.28;
5, e 6666;
8, h 0;
9, i 0;
10, j 0;
3, c 1;
13, m 1 0. 178. -2000. 2000. -2000. 2000. -2000. 2000. -2000. 2000. -2000. 2000.;

OSC_Settings.txt
text/plain 0.20 KB


ygreq's icon

And here is the arduino code I am using to control the servo and the 2 LEDs

/*
* Control a servo and 2 leds according to incoming OSC control
*/
#include <OSCBundle.h>
#include <OSCBoards.h>
#include <Servo.h>

// OSC Related
#ifdef BOARD_HAS_USB_SERIAL
#include <SLIPEncodedUSBSerial.h>
SLIPEncodedUSBSerial SLIPSerial( thisBoardsSerialUSB );
#else
#include <SLIPEncodedSerial.h>
SLIPEncodedSerial SLIPSerial(Serial);
#endif

Servo myservo;
int led1Pin = 12;
int led2Pin = 8;

void servoControl(OSCMessage &msg)
{
if (msg.isInt(0))
{
myservo.write(msg.getInt(0));
}
}

void LED1control(OSCMessage &msg)
{
if (msg.isInt(0))
{
analogWrite(led1Pin, (msg.getInt(0)));
}
}

void LED2control(OSCMessage &msg)
{
if (msg.isInt(0))
{
analogWrite(led2Pin, (msg.getInt(0)));
}
}

void setup() {
SLIPSerial.begin(115200); // Serial baud

myservo.attach(3); // Pin 3 for servo
myservo.write(90);
pinMode(8, OUTPUT); // Pin 11 for LED
pinMode(12, OUTPUT); // Pin 12 for LED

#if ARDUINO >= 100
while(!Serial)
; // Leonardo bug
#endif
}

void loop(){

// OSC related
OSCBundle bundleIN;
int size;
while(!SLIPSerial.endofPacket())
if( (size =SLIPSerial.available()) > 0)
{
while(size--)
bundleIN.fill(SLIPSerial.read());
}

// Parse OSC messages according to tag
if(!bundleIN.hasError())
bundleIN.dispatch("/gp_ljx", servoControl);
bundleIN.dispatch("/gp_a", LED1control);
bundleIN.dispatch("/gp_b", LED2control);

}

ygreq's icon

Everything works for now.

Next step is to test the stepper motor and the 2 oDrive brushless motors https://odriverobotics.com/ and https://docs.odriverobotics.com/ using the Serial

And afterwards I will test via UDP because like I said, the robot will be wireless. I tested a few weeks ago to control an ESP via UPD and OSC, but I lost the code due to HDD failure. So I am trying to figure out how to make this code work via UDP.

So any suggestions? :)

Source Audio's icon

ESP32 itself is not a problem, beside the fact that it has no usual analogwrite
function. One has to use extra libs for that, and pick one of several choices.

But the problem I see is UDP over Internet, if I understand you correctly,
you want to send data using OSC UDP on local wifi net, but also send
controls from web browser.
Did you know of a way to send OSC UDP from standard web browser out of the box?
-------------

Here is ESP32 example to receive /srv $1 $2 $3 $4 $5 string from Max
and unpack it
But you need to decide at the end what is more efficient - to send full string
and parse it, or to send individual controls, which might reduce data a lot.

#include <WiFi.h>
#include <WiFiUdp.h>
#include <OSCMessage.h>
#include <OSCBundle.h>
#include <OSCData.h>

char ssid[] = "enter your ssid here";
char pass[] = "enter password here";

WiFiUDP Udp;
const unsigned int localPort = 8888; // incoming UDP port

void setup() {
WiFi.begin(ssid, pass);
Udp.begin(localPort);
}

void servos(OSCMessage &msg) {
int SRV1 = msg.getInt(0); // 1st servo value
int SRV2 = msg.getInt(1); // 2nd servo value
int SRV3 = msg.getInt(2); // 3rd servo value
int SRV4 = msg.getInt(3); // 4th servo value
int SRV5 = msg.getInt(4); // 5th servo value
}

void loop() {OSCMessage msg; int size = Udp.parsePacket();
if (size > 0) {while (size--) {msg.fill(Udp.read());}
if (!msg.hasError()) {msg.dispatch("/srv", servos);}
}}


-------------------

Being lazy to look for IPs , I use broadcasting for home testing....

ygreq's icon

<< But the problem I see is UDP over Internet, if I understand you correctly, you want to send data using OSC UDP on local wifi net, but also send controls from web browser. Did you know of a way to send OSC UDP from standard web browser out of the box? >>

I will not use a web browser for controlling the robot. I will control it from Max via Wifi. And I was thinking of using OSC for that. And to my knowledge, I need to send OSC data via udpsend.

And for controlling it over the internet, I will just send the controller data via udpsend as well. Like this.

Remote PC -> OSC -> Local PC -> OSC -> robot.

I guess I can send directly from the remote PC to the robot, but considering that the local PC needs to take over anytime, I consider it is best that the Remote PC data be sent to the Local PC and then on to the robot.

Nice trick with the broadcasting :D

I will test your code asap. But I don't get what you mean by << But you need to decide at the end what is more efficient - to send full string and parse it, or to send individual controls, which might reduce data a lot. >>

Which is better? A string and parse it or individual controls?
And how would individual controls reduce data?

Thank you so much!

Source Audio's icon

If you don't plan to control ESP32 via web browser directly using UDP all is ok.

Question about string or individual controls.
simple answer :
in case of /srv $1 $2 $3 $4 $5
all 5 values will get sent out even if only 1 member of 5 items changed.
In case of separated osc messages :

/srv1 $1
/srv2 $1

etc
you send a single value only if it changed and parse it individually in arduino.
If you anyway expect all 5 values above to be frequently updated,
than /srv $1 $2 $3 $4 $5 would be a better choice.
If you expect one or two to be moved frequently, and the rest once in a while,
than separated osc messages would be more efficient.
That is a difference.
Same applies to joystick - if you send only axis values
that really moved, or collect all current values into list and send the list.
--------
In a complex project, like your one seems to be, one has to well plan
efficiency of data streams and try to keep osc strings short.

Why would you use
/MyProject/MyControls/Robot1/leftarm/ $1
if /r1a $1 would do ?
it is not only number of needed bytes, but also code gets sooo looong on the screen.

ygreq's icon

Thank you so much for the explanations!

<< Why would you use /MyProject/MyControls/Robot1/leftarm/ $1 if /r1a $1 would do ? >>
Yeap. This is what I did. I first used /gamepad/leftJoystick/x and then I also made an option to just send /gp_ljx
:D

I will test the sketch you sent for ESP and will let you know!
Cheers!!

DimB's icon

@ sourceaudio : I do not understand one thing : on which pins are connected the servo? They are not defined in the code. Thanks !

Source Audio's icon

that code is just example how to send values to esp32.
to use the values for servo control, you need to
map received values to servo controls.
You can learn that from using examples
in servo library

DimB's icon

ok thanks