Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

sending data through OSC to 80 servo motors is too big? #16

Open
FRIENDRED opened this issue Aug 26, 2018 · 6 comments
Open

sending data through OSC to 80 servo motors is too big? #16

FRIENDRED opened this issue Aug 26, 2018 · 6 comments

Comments

@FRIENDRED
Copy link

Hey@bakercp,
I am making a project which needs to control 80 servo motors (daisy chained) from OF to Arduino, I was trying your 'example_packet_serial_slip', the example didn't work with Arduino Mega but worked with Arduino Uno.
I therefore changed to 'example_SLIP-OSC' as I need to use Mega to control something else as well. It worked with 20 servo motors, however, it will have some sort of unstable data for the servos if I increase the number of the micro servo motors (more than 25). I guess it might be the data I was sending is too big to send via OSC? Any suggestions would be really appreciated.

@bakercp
Copy link
Owner

bakercp commented Aug 26, 2018

If I'm controlling that many motors, I'd just come up with my own simple protocol that doesn't require any OSC overhead etc. OSC on the Arduino (in my experience) is better suited for prototyping and situations where you need a flexible protocol to speak across different kinds of (or existing) devices.

So for instance, a "protocol" that consists of packets of two bytes -- the motor address and the target position might be all you need. In this case, you could just use the packet serial example (https://github.com/bakercp/ofxSerial/tree/master/example_packet_serial) and create an ofBuffer that consists of those two bytes and send it to the Arduino. Then, on the Arduino, you could just read those two bytes (e.g.):

https://github.com/bakercp/PacketSerial/blob/master/examples/PacketSerialReverseEcho/PacketSerialReverseEcho.ino#L55

and decide what servo to set with first byte and what its position should be with the second byte. This will drastically reduce your overhead and memory requirements for the OSC library on the Arduino. That's how I'd do it!

@bakercp
Copy link
Owner

bakercp commented Aug 26, 2018

By the way this two byte protocol works great if you have <= 256 motors (the first byte) and your servo resolution is <= 256 steps. If your servos are more precise, you could break a 16 bit integer into two bytes and send a total of three bytes ADDRESS, POSITION_HIGH_BYTE, POSITION_LOW_BYTE.

@FRIENDRED
Copy link
Author

Thank you so much for the reply @bakercp I really appreciate it! I was trying your method and it worked without using OSC! The only difference is that I do not really understand how to send only two bytes (or three bytes) to Arduino. Instead, I sent a string of data from OF and converted from string to Int on the Teensy 3.6 side (I changed from Arduino to Teensy, it's faster as well). It worked fine with 32 servo motors (with two daisy-chained motor driver), however, it dragged down my OF framerate (from 60 to 35ish) somehow when I was trying to send to 48 servo motors.

I haven't connected the other 16 channel motor driver yet. But I am really concerned now as I thought the problem might be hardware wise, for instance, could be something wrong with the I2C of the Teensy can't handle the wire being too long? Could be the third driver which was driving the last group of 16 motors needs an external 3.3v logic power supply? Or do I need to have one pull up resistor for each I2C connection?

But what I couldn't understand is why my framerate in OF could be reduced due to uncanny reason? I'd be really appreciated if you can show me how to just send two or three bytes, and how to receive that two bytes in Teensy, then I could have a try! Thank you so much in advance! Have a good evening!

My code in the open framework is:

for(int i = 0; i < 48; i ++){
        servos[i] = pan;        
    }
    ostringstream command;
    for(int i = 0; i < 48; i++){
        command << setw(3) << setfill('0') << servos[i];
    }
     ofxIO::ByteBuffer buffer(command.str());
    device.send(buffer);

My code in the Teensy is:

void onPacketReceived(const uint8_t* buffer, size_t size)
{
  int c = 0;
  for (int i = 0; i < size; i += 3) {
    char myBuffer[4];
    myBuffer[0] = buffer[i];
    myBuffer[1] = buffer[i + 1];
    myBuffer[2] = buffer[i + 2];
    myBuffer[3] = '\0';

    String myString(myBuffer);
    int myInt = myString.toInt();
    if (c < 16) {
      int val = map(myInt, 0, 180, 150, 600);
//pwm in here is the instance of first motor driver
      pwm.setPWM(c, 0, val);
    }
    else if (c >= 16 && c < 32) {
      int val = map(myInt, 0, 180, 150, 600);
      pwm1.setPWM(c - 16, 0, val);
    }
    else if (c >= 32 && c < 48) {
      int val = map(myInt, 0, 180, 150, 600);
      pwm2.setPWM(c - 32, 0, val);
    }
    c++;
  }

@bakercp
Copy link
Owner

bakercp commented Aug 27, 2018

In your ofApp::update() you might try:

    for(uint8_t i = 0; i < 48; i ++)
    {
        uint8_t address = i;
        uint8_t value = i * i;
        device.send({ address, value });

        //  Same as ...
        //         ofx::IO::ByteBuffer buffer({ address, value });
        //         device.send(buffer);

    }

On the Arduino side:

void onPacketReceived(const uint8_t* buffer, size_t size)
{
if (size == 2)
{
    int c = buffer[0];
    int myInt = buffer[1];
  
    if (c < 16) {
      int val = map(myInt, 0, 180, 150, 600);
      pwm.setPWM(c, 0, val);
    }
    else if (c >= 16 && c < 32) {
      int val = map(myInt, 0, 180, 150, 600);
      pwm1.setPWM(c - 16, 0, val);
    }
    else if (c >= 32 && c < 48) {
      int val = map(myInt, 0, 180, 150, 600);
      pwm2.setPWM(c - 32, 0, val);
    }
}
else 
{
  // we only send packets that are two bytes long, so this is an error ... probably to be ignored.
}

@bakercp
Copy link
Owner

bakercp commented Aug 27, 2018

This method will just send the raw bytes, rather than 3 x bytes for every byte (since you always pad w/ ascii zeros).

One thing to keep in mind is that your servos probably can't (maybe you have special servos) cant be updated at 60FPS, so you may want to slow down your transmission rate and not send serial data every ofApp::update() { ... }. You can also play with the serial timeout settings if it is introducing a lag ... but generally that only affects the RX on the computer. Anyway hope it works!

@FRIENDRED
Copy link
Author

It worked! Now my 80 servo motors are running!!! Thank you so much! The only concern now is that it still drops down my framerate (from 60 to 25ish) in OF with increasing the number of the servo motors I am sending. I tried to modular the time to lower down the frequency. The framerate raised up to 60 again, but on the Teensy side, those servo motors seemed to become less smooth than sending 60fps. On the Teensy side, I am using the method you suggested. I'm actually a little bit worried about this as I'm using Kinect as well, all the servos will be triggered by skeleton tracking data in the Kinect and changing the patterns. The framerate was not high (30 ish) when I was only using Kinect with skeleton tracking.
I feel the framerate would be double 'discount' when I put two programs together.
I was just wondering do you know the reason why my framerate will drop since I sent data to more servos. Or is there a way I could give a try that might help the situation. The servos I am using are SG90 Micro Servo Motor (9g). Thank you so much!

    time ++;
    for(uint8_t i = 0; i < 80; i ++)
    {    
        uint8_t address = i;
        uint8_t value = debugValue;
        if(time%3 == 0){ //time is int
        device.send({ address, value });
        }
    }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants