Hi

I'm trying to use the Adafruit16CServiceDriver. I'm sure it's broken,but thats OK. I can spend some time to make it worky. I have downloaded the library for the driver and used MRLComm and the commented code from Adafruit16CServoDriver to make an arduino sketch that I think will work for my first experiments. But when I read the examples, and I try the code it looks like the implementation of attach differs from what I expected. Nomally I would do something like this to attach a servo.

comPort = "COM4"
Arduino = runtime.createAndStart("Arduino","Arduino")
thumb = runtime.createAndStart("Thumb","Servo")
thumb.attach("Arduino",2)
 
So the servo attaches to the Arduino ( servocontroller )
 
But in the example for Adafruit16CServoDriver things are reversed. If I understand things correct the Adafruit16CServoDriver will act like a servocontroller and be a layer between the servo and the Arduino.
 
So I do like this:
adaFruitRight = Runtime.createAndStart("adaFruitRight","Adafruit16CServoDriver")
adaFruitRight.connect("COM4")
thumb = runtime.createAndStart("Thumb","Servo")
 
But the next line from the example confuses me:
adaFruitRight.attach(thumb,3)
 
I would expect the servo to attach to the controller, not the controller to attach the servo, like this:
thumb.attach(adaFruitRight,3)
 
I have tried both alternatives, but in both cases I get an error, servo not attached when I try to move the servo:
thumb.moveTo(0) 
 
I can dig into debugging and reworking the  Adafruit16CServoDriver, but I need to know what syntax to use.
 
1. adaFruitRight.attach(thumb,3) 
2. thumb.attach(adaFruitRight,3)
 
Should alternative 1 or 2 work ?

 

Mats

8 years ago

After looking at how Servo is implemented and the servocontroler interface is defined it's now obvoius that alternative 2 is the one to go for, and alternative 1 will be abandoned. 

So I will continue to implement the missing parts and also try to add some more stuff to be able to use multiple Adafruit16CServoDriver on the same Arduino using different I2C addresses.

GroG

8 years ago

Hi Mats,
 
You've hit on a topic which I've never formally resolved.
What attaches to what ?
In some ways it doesn't matter - in that the pattern can be implemented
one way or the other or both....
 
This is my current thoughts:
I think you usually "attach" things in order from directly to the computer to more peripheral.
Meaning the thing which was there "first" attaches to peripherals.
 
A computer attaches to an Arduino,
An Arduino attaches to a Shield,
A Shield attaches to a Servo 
 
arduino.attach(shield)
shield.attach(servo)
 
I will work on standarizing this across MRL.
It a simple mantra .. "The thing which was there "first" attaches to the new thing" - not the other way around
 
Another thing is the attach should usually implement an Interface
 
And Finally :
The Interface should mandate a String interface (IF AND ONLY IF) attaching can be done remotely between processes.
 
I'll give a hypothetical example of some text publisher and another text filter
- so the publisher comes "first" so it should be the one which implements an attach
 
publisher.attach(filter)
 
but if this could happen in different processes, where one mrl instance is running the publisher and some remote mrl instance is
running the filter - if that is a valid use case then a string parameter attach should be implemented as well
 
publisher.attach(filterName)
 
Hopefully I havent made things more confusing ;)
 

Hi Grog

So for each service I create, some other service, that was already there should attach to my new service.

That sounds strange to me. 

I would prefer to create a new service and then for that service attach to one of the already existing services.

Otherwise it will be necessary to create different attach methods, one for each service that the parent can attach to. So for example an Arduino can have several different devices, like motors, servos, and so on. You can see how that scheme works in the Arduino service. motorAttach, servoAttach, sensorAttach and so on. Many different methods for attach.

If you do it the other way around a service can attach to any parent service that implements the interface that has been defined. Like the Servo can attach to the Arduino service or the Adafruit16CServoDriver or any other service that implements the ServoController. It will still only be one attach method that attaches to the service that the Servo wants to communicate thru.

So I'm thinking in a parent - child, where the child attaches to a single parent.

I understand that you have been thinking about this problem before. I found some comments in Adafruit16CServoDriver.java

// attachControllerBoard ??? FIXME FIXME FIXME - should "attach" call
// another's attach?
/**
* an Arduino does not need to know about a shield but a shield must know
* about a Arduino
 
That's also the way I'm thinking of it. The servo needs to know about the shield, the shield needs to know about the Arduino. So that's how they should attach. 
 
I also found this comment in motor.java
 
public void attach(MotorController controller) throws Exception {
controllerName = controller.getName();
// GOOD DESIGN !! - this is the extent of what our attach should be !!!
// just call the controller's motorAttach & broadcast our state
controller.motorAttach(this);
broadcastState();
}
 
Well. Both methods are implemented now, so either scheme will work, and the GUI also works.
 
---
 
I have a working version of Adafruit16CServoDriver now, Each instance can drive 16 servos, and I added I2C addressing so it's possible to connect multiple ( up to 62 ) ServoDrivers on the same I2C bus. One Servodriver per I2C address, but they can share the same Arduino. 
 
The MRLComm version that I have now is NOT dependant on the Adafruit_PWMServoDriver library. I couldn't use it because of the way that I2C addressing was implemented. The way it's written is good if you use the Arduino standalone, but not the way MRL uses it. So I only used it as a template. A few new methods in MRLComm.
 
I get some warning about low memory for local variables, but it still works fine. I also tried to make some changes to decrease the amount of memory that MRL use, but with limited success. I could only save one byte :-)
 
/Mats

Disregard my previous post.
 

It's very true:
In software - the important thing is "how much"  Service A knows about Service B.

The most preferred model would be where neither know of each other - and if necessary, only through interfaces.

To be pragmatic, in early development interfaces are cumbersome .. so I usually let things evolve into interfaces (once method creation, deletion, and renaming have slowed down)

An attach(String name) method is required if the expectation is for the method to "attach" thing on different processes..   In this case, that certainly won't happen, since the Servo, Arduino & AdaFruit will all be on one computer...

How are you currently accessing your methods Mats ?   I did not see anything added to the Arduino service..  But I'm excited to see your Wire I2C methods ...   

However, part of the MRLComm.c is completely generated.  I did this when maintenance became a hassle, and I don't want to maintain non-generated bindings..  but we can work that out.

I've noticed with I2C - the lowlevel methods are very similar to serial, of course, but every device needs its own "driver" ...   

I'm of the firm belief that the "driver" logic of a particular device should be implemented in the Service which represents it....

Would like to hear more about your implementation....

 

Hi Grog. 

I didn't make any changes to the Arduino service. I only use the methods in it to send the commands to the Arduino board. All the logic is in  Adafruit16CServoDriver and in MLRComm.c. I have marked all changes in MRLComm.c with Start  Adafruit16CServoDriver ... and End Adafruit16CServoDriver ... so that it's easy to find the changes. No changes really, just additions. I use the wire.h include for I2C commands, and added a section at the end that contains the I2C commands used to communicate with Adafruit16CServoDriver.

I first tried to use the  Adafruit16CServoDriver library but the way it supported I2C addressing was not easy to implement in MRLComm.c so I cut and pasted the parts that I needed from the library and changed it so that I pass the I2C address with each command. The default is x040 but it can be set with the setI2CAddress method in  Adafruit16CServoDriver.

If you want to help me get it to a good state, then I need help with 2 things.

1. I changed version in MRLComm to 28 but didn't find where to change it in MRL

2. The serial monitor doesn't show the command that i send. It shows null as the command. I found a mapping, but couldn't figure out how to change it, since it seems to be autogenerated from the Arduino service.

Please look at the changes. Any comments, good or bad are appreciated, since my goal is to understand MRL and to be able to contribute more. And the best way to learn is to try to implement something, and then have someone more experienced to do a code review.

I agree on your point that each device driver should have the logic for the device and that the I2C driver in the Arduino should be generic so that any I2C device can use it. Since I have two other I2C devices that I want to use, I will try to make it more generic and some of the code that now is in MRLComm.c should be moved to  Adafruit16CServoDriver.

/Mats

GroG

8 years ago

In reply to by Mats

Hi Mats !

Of course, I'm excited you're interested, and I think adding I2C would be great.

Lets start :)

 
// void sendServoEvent(servo_type& s, int eventType);
unsigned long getUltrasonicRange(pin_type& pin);

are function prototypes - in older C compilers - the method signature needs to be defined, so the it can be used in other methods and defined later.  I've uncommented it and checked it back in.  Perhaps it does not error for you because you are using a "smarter" pre-processor or newer version of the avr c compiler

I thinks its great you took what you needed out of Wire vs trying to use the AF library.
Its definately preferrable to use "native" libraries which come with Arduino vs dependencies which might not be there ... although at this point perhaps the necessary AF library is now part of Aruduino ?

With my original implementation I used the AF library and was unhappy with the implemenation

Very excited !   Does this work ?
Can you show us a AF servo controller in action !  Video !  :)

The Adafruit_PWMServoDriver library is not part of the Arduino default install. That's why I used copy / paste from it. The I2C library ( wire ) has been part if the default Ardino install for a long time, so I used  it.

I found that there is a RasPi service that can be used to access the Raspberry PI GPIO, including the I2C interface. But I haven't tested it. I'm thinking that with a well defined I2C interface we could connect things together in many different ways.

Servo <ServoControl=> Arduino 

Servo <ServoControl=> Adafruit16CServodriver <I2CControl=> Arduino

Servo <ServoControl=> Adafruit16CServodriver <I2CControl=> RasPI

"Any I2CDevice" <I2CControl=> Ardino

"Any I2CDevice" <I2CControl=> Raspi

Servo, Ardino, Adafruit16CServodriver, RasPI  and "Any I2CDevice"  are services.

ServoControl and I2CControl are interfaces. 

The ServoControl interface is already defined. I'm trying to define what methods should be in the I2CControl interface. But I need to study the I2C protocol a little more. 

 

Mats

7 years 11 months ago

In reply to by Mats

Things are moving forward with the i2c library. I installed the latest pi4j library ( 1.1-SNAPSHOT ) locally on my PI and made a test program to see if I could write a Java program to use it. And it works. I can move the servos uing this https://learn.adafruit.com/adafruit-16-channel-servo-driver-with-raspbe… connected to the GPOI / i2c pins on th PI. WOOHOO !

So the next step is to make the latest version of the pi4j library available to MRL, so that I can continue developing the RasPi service to use it. The plan is to move device specific things away from MRLComm and RasPI and have all the device logic in the device drivers. Arduino+MRLcomm and RasPi should have the same set of methods that only implements low level i2c read and writes. The drivers should know what i2c methods to use. 

But I'm not sure how to make the new pi4j library available to MLR the right way.

I created a folder 1.1-SNAPSHOT in my local repo in parallell to 0.0.5 version in C:\MRL2\repo\com.pi4j.pi4j and copied all the content from the pi4j library to that location, but restructring it to the same structre as 0.0.5 has. 

I also created a ivy.xml file, that I think is ok, but I'm not 100% sure.

So what next steps do I need to take ? I guess that I need to make git aware of the new libraries and push it to the repo. And Eclipse needs to know that I want to use 1.1-SNAPSHOT, not 0.0.5. And change the services that use pi4j to have the right dependency. How do I do this? Anything else that I need to do ?

 

After looking at how Servo is implemented and the servocontroler interface is defined it's now obvoius that alternative 2 is the one to go for, and alternative 1 will be abandoned. So you should continue to implement the missing parts and also try to add some more stuff to be able to use multiple Adafruit16CServoDriver on the same Arduino using different I2C addresses.

 

IC programming

I added a I2CMux service so now things can be connected like this:

"Any I2CDevice" <I2CControl=> Ardino

"Any I2CDevice" <I2CControl=> Raspi

But also

"AnyI2CDevice"  <I2CControl=> I2CMux <I2CControl=> RasPi

"AnyI2CDevice"  <I2CControl=> I2CMux <I2CControl=> Arduino

I2CMux, Ardino, , RasPI  and "Any I2CDevice"  are services.

ServoControl and I2CControl are interfaces. 

So now its possible to use multiple i2c devices even if they have conflicting addresses. 

I2CMux is a service that uses this device:

https://learn.adafruit.com/adafruit-tca9548a-1-to-8-i2c-multiplexer-bre…

/Mats