Haha, shows what I know!
Thanks for the simple comparison! Makes some good sense.
This I like quite a bit!
Haha, shows what I know!
Thanks for the simple comparison! Makes some good sense.
This I like quite a bit!
Iām not as fast as @kurte assembling my robot. But finally done. Now the hard part starts. Will try to follow your dialogue here. The āserial stuffā isnāt my strongest side.
Hi @zenta - looks like you are farther then meā¦ I have not tied down the servo wires yet
Now experimenting some with software and trying to understand documents.
@scharette - I will try next to see if the commands strings you mentioned would work or notā¦ I was sort of unsure as the wiki said:
A servoās baud rate cannot be set āon the flyā and must be configured via the CB command described below. The factory default baud rate for all servos is 115200. Since smart servos are intended to be daisy chained, in order to respond to the same serial command, all servos in a project should be set to the same baud rate. Setting different baud rates will have the servos respond differently and may create issues. Available baud rates are: 9600 bps, 19200 bps, 38400 bps, 57600 bps, 115.2 kbps, 230.4 kbps, 250.0 kbps, 460.8 kbps, 500.0 kbps. Servos are shipped with a baud rate set to 115200.
But as the commands output look like the ones mentioned will try to do the other legs using program.
Side note: For me what I would like to see in the WIKI here on the LSS - communcation protocol is a description of what comes back on the RX line? And sort of what are my limitations.
EDIT: Part of this was sitting right in front of me
Ex: #5QD Query position in (tenth of) degrees for servo #5The query will return a serial string (almost instantaneously) via the servoās Tx pin with the following format:
- Start with an asterisk * (Unicode Character: U+0023)
- Servo ID number as an integer
- Query command (one to four letters, no spaces, capital letters)
- The reported value in the units described, no decimals.
- End with a carriage return \r or Unicode Character (U+000D)
I figured out by watching the data go back and forth over the Serial port using Logic Analyzer. That Query commands, do return data and it looks like: it echoes back the query command, replacing the # with a * and then adding the resulting data on to the end.
I am guessing from what I am seeing that sending an action command like move, we donāt receive anything like an acknowledgement? But for example can I send out multiple query commands out and not have to wait for the servo respond first? Can these query commands be to the same servo and/or different servos?
Again this may be covered somewhere, but I did not see it.
@scharette and @cmackenzie - I already had an a version of AlternativeLSS installed
AlternativeLSS(V0.8.0) and Lynxmotionā¦ V1.3.1
Will soon look at the new version
Exactly!
No ACK on action or configuration commands.
When interacting with one specific servo you can (and should!) pack your queries together for efficiency.
For example, to read position, current, voltage and temperature of servo #0 you would send:
#0QD\r#0QC\r#0QV\r#0QT\r
And the LSS would respond quickly to all of these.
The one caveat of packing queries is that you cannot do so for multiple LSS as this would cause bus contention (hence why @cmackenzie 's library is helpful there). Therefore, you can pack queries for one LSS, wait for replies and then move on to the next one.
Thanks @scharette - Suggestion would be to add some of this information to wiki.
FYI - I updated all of the servos to 500000, using sketch (actually only 15 servos, the other I used the app).
For the fun of it, I tried to do it without using any library. I included here for your amusement
void setup()
{
while (!Serial && millis() < 5000) ;
Serial.println("Change all LSS Servos from 115200 to 500000 baud");
}
void loop() {
Serial1.begin(115200);
Serial.println("Scan servos ast 115200");
bool Servos_found = ScanServos();
if (Servos_found) {
Serial.println("Press any key to continue");
while (Serial.read() == -1) ;
while (Serial.read() != -1) ;
delay(250);
Serial.println("Try set baud cammand");
Serial1.print("#254CB500000\r");
delay(250);
Serial.println("Reset");
Serial1.print("#254RESET\r");
delay(250);
Serial1.end();
Serial1.begin(500000);
delay(250);
Serial.println("Scan servos at 500000");
ScanServos();
}
Serial1.end();
Serial.println("Press any key to try again");
while (Serial.read() == -1) ;
while (Serial.read() != -1) ;
}
bool ScanServos() {
bool found_servo = false;
for (uint8_t servo_id = 0; servo_id < 254; servo_id++) {
Serial1.printf("#%uQD\r", servo_id);
Serial1.flush();
delay(25);
if (Serial1.available()) {
int ch;
while ((ch = Serial1.read()) != -1) {
Serial.write(ch);
}
found_servo = true;
Serial.println();
}
}
return found_servo;
}
I did it one leg at a time. It sort of worked, in that it did find each servo on the leg at the lower baud rate, and then it did do the change of baud and reset. But it did not find the servos again at higher baud rate.
I should probably verify that my underlying Teensy code worked for switching the baud.
Or maybe I needed to do some other function after the reset to have the servos see the new baud or maybe wait a bit longerā¦ But at least the task is done.
Yeah!
I notice after calling for a reset you wait 250 ms before continuing.
Our official software waits a full 2000 ms to be on the safe side (IIRC the boot up sequence is about 1250 ms minimum).
So, going by those numbers, (1250 ms - 250 ms) / 25 ms [your delay when scanning], you are likely skipping the first 40 IDs ( [0, 39] ) since those LSS cannot answer yet. If you are using the old Phoenix IDs then that would mean youāll never find any of themā¦
Have a look at increasing the post-reset delay to 2000 and see if your results change.
Hmm, the only thing to do after a reset is to wait for servos to (soft) power-cycle/boot-up.
Yep it just needed a longer delay. I changed the sketch to allow the from and to be more easily changed as I then ran it to change one leg back to 115200 which worked and then changed back and it worked.
static const uint32_t from_speed = 115200;
static const uint32_t to_speed = 500000;
void setup()
{
while (!Serial && millis() < 5000) ;
Serial.printf("Change all LSS Servos from %u to %u baud\n", from_speed, to_speed);
}
void loop() {
Serial1.begin(from_speed);
Serial.printf("Scan servos at %u\n", from_speed);
bool Servos_found = ScanServos();
if (Servos_found) {
Serial.println("Press any key to continue");
while (Serial.read() == -1) ;
while (Serial.read() != -1) ;
delay(250);
Serial.println("Try set baud cammand");
Serial1.printf("#254CB%u\r", to_speed);
delay(250);
Serial.println("Reset");
Serial1.print("#254RESET\r");
delay(3000);
Serial1.end();
Serial1.begin(to_speed);
delay(250);
Serial.printf("Scan servos at %u\n", to_speed);
ScanServos();
}
Serial1.end();
Serial.println("Press any key to try again");
while (Serial.read() == -1) ;
while (Serial.read() != -1) ;
}
bool ScanServos() {
bool found_servo = false;
for (uint8_t servo_id = 0; servo_id < 254; servo_id++) {
Serial1.printf("#%uQD\r", servo_id);
Serial1.flush();
delay(25);
if (Serial1.available()) {
int ch;
while ((ch = Serial1.read()) != -1) {
Serial.write(ch);
}
found_servo = true;
Serial.println();
}
}
return found_servo;
}
Terminal output:
Change all LSS Servos from 115200 to 500000 baud
Scan servos at 115200
*7QD42
*9QD-1134
*11QD-692
Press any key to continue
Try set baud cammand
Reset
Scan servos at 500000
*7QD42
*9QD-1134
*11QD-692
Press any key to try again
Re: Queueing multiple commands
Yes, queueing is done under the hood in both versions. The second version you just send(ā¦) an array or collection of packets. Under the hood it will:
Iām on the verge of getting this humanoid to take itās first steps and Iām super-excited to see this workā¦but I can take some time after to finalize an Arduino release if you are interested. You can checkout the new API interface example code for ChannelReadWrite example. Notice it uses a Promise-like interface where the send(ā¦packetsā¦) call usese then(ā¦lambda).otherwise(ā¦lambda) to handle the async send/query/update loop and handle bus or timeout errors. Packets in the send() call are using std::initializer_list and in the update they are using std::vector but static C arrays work too. servos[] array is a simple C array of servo IDs.
Quick update: I have started playing around some with software. I thought I would hack up my own test servo program I had been using for Dynamixel Servos (on my own library then on OpenCMā¦).
Hope you donāt mind if I start issuing Issues on the libraries and/or Pull Requests.
I am running into lots of compiler warnings with the library: #include <lss.h>
So trying to resolve many of them.
On another note, I thought I would mention, I can now post pictures of the Teensy 4.1 as it was released today
https://www.pjrc.com/store/teensy41.html
And more information on it up at:
Neat.
Ah. Did not try it on anything not AVRā¦
Iāll have a look at the issues/pull requests shortly. Thatās what they are there for!
Cool!
Good to hear your doing some progress @kurte . Cool to see the 4.1 out. Iāve 3x 4.0ās I think should do the job also. Still waiting for the level shifters. One thing Iām wondering about is that Iām using the Teensy 3.6 on your old v0.3 breakout board on my MX phoenix with the MX 64/106 T servos. Iām not using any level shifters there so I might just got lucky? Anyway I donāt want to risk anything when testing the LSS.
@scharette - Now have the starting of my random junk test sketch limping along. Did run into several issues while testing it and I did push up more changes to the library, to the branch with the PR. I think I have all of the compiler warning gone, but who knows may have missed some. Should also run it on AVR to make sure it did not screw up things the other way.
I know I should probably wait until more of it is working, but in case anyone is curious. So far I only have the servos of one leg plugged in.
LSSTestServos-200511a.zip (6.3 KB)
In case any of you are following along. I just updated my branch of the library with some more updates.
I reworked some of the methods: genericRead_Blocking_str and genericRead_Blocking_s16
to not us sscanf. Instead it simply reads the bytes of the message, and first builds a simple number for the Servo ID, and then compares it against the number passed in. It then checks the CMD bytes one after another until end of passed in messageā¦ And then assumes everything else is results, so it reads it into the value string.
The s16 version simply calls the string version and then does a simple inline parse for a number. It appears to work (I think).
Again WIP and not a great sketch, but more pieces are sort of workingā¦
LSSTestServos-200512a.zip (6.3 KB)
Edit: I put my WIP test sketches up at: https://github.com/KurtE/LSS_Test_sketches
EDIT2: I went a head and ordered a few more things, for this, including SES210, a couple of your hubs, cables and screwsā¦ Thought I might want to test the code on your Arduinoā¦ But my guess is the order might take awhile to get here.
Interesting! Many good changes in there. The genericRead functions definitely needed some cleanup.
One customer had speed issues on a Teensy 3.6 (which I was able to reproduce) while doing QD, figuring out the current genericRead were problematic (and causing delays ~100 ms). I proposed to the user the following, originally (or something along those lines) for a quick test, which greatly improved reading speed. Hereās a copy of that code (I think):
// Query LSS position
lastPosLSS = posLSS;
Serial1.write("#0QD\r");
while(Serial1.available() == 0)
{
}
size_t readLength = 0;
readLength = Serial1.readBytesUntil('D', readBuffer, 100); // Read until after the command (QD), indicating the start of the returned value (position in 1/10 deg)
readLength = Serial1.readBytesUntil('\r', readBuffer, 100); // Read until the carriage return (\r), indicating the end of the reply
if(readLength > 0)
{
if(myLSS.charToInt(readBuffer, &posLSS))
{
Serial.print("Pos = "); Serial.println(posLSS);
}
else
{
Serial.println("Error in conversion");
}
}
else
{
Serial.write("No reply data\r\n");
}
Of course, a more generic version of this was always required.
Thereās one modification that did catch my eye more than the others though: youāve changed the writes from a snprintf to buffer / one call to Arduinoās write into 4, 5, and 7 calls (genericWrite) to it. From tests weāve done before, this is definitely slower (at least in AVR world, did not profile it on ARM or other platforms). I was curious about your reasoning for removing the buffering there and making more function calls.
Yes, that was also definitely needed!
@scharette - more later on other parts:
On removing snprintf and the buffer.
There are a couple of different reasons why I did it, which may or may not be valid.
a) code and buffer sizes - On Atmega328s and the like they usually are pretty limited and having functions like snprintf and sscanf can add a lot of code and buffer usage, so often times I try to avoid these especially if they bring in full blown version including floating pointā¦
b) On most systems (maybe not including SoftwareSerial) - the hardware has itās own internal software buffer, that the writes go into. So doing a couple of calls to Serial1.write(ch). Will simply just add bytes to queue. BUT: in some cases if it finds that the queue was empty and the hardware is ready for a byte to be sent to it, it will do that instead. So by making these calls early you can actually start the hardware Serial port to begin the output of the first character(s) sooner.
c) If you look at how in many cases functions like the bus->write(cmdBuffer) is implemented.
Example AVR:
This is a call to print class methods: So it calls
size_t write(const char *str) {
if (str == NULL) return 0;
return write((const uint8_t *)str, strlen(str));
}
i.e. it then scans you string to figure out the length of it, and then calls the write method:
which I donāt believe on the hardware serial port code for AVR they override this virtual method, so it does:
size_t Print::write(const uint8_t *buffer, size_t size)
{
size_t n = 0;
while (size--) {
if (write(*buffer++)) n++;
else break;
}
return n;
}
Where the virtual write method for one character is implemented: so it then calls: Serial1.write() for each character.
So I decided to bypassā¦
Again maybe some of my assumptions are off here. But hope some of this makes sense.
EDIT: - However I can easily imagine some other hardware, especially those who may have a HAL (Hardware Abstraction Layer) where each of these calls goes through a level transition or the like and in those configurations having the code buffer to itās own buffer and then doing one call would be beneficial. So maybe I should configure it such that maybe a #define is defined depending on hardware defines which allow the code to work either way.
Hi All,
I had some time to start assembling! Yay!
Unfortunately, I run into an issue because I think I received some incorrect SES brackets.
As you can see in the photo below Iāve received 5 brackets with identical hole patterns on both sides and one with only a small hole on one side.
Then I started looking at the photo Kurt and Kare posted. It seems that Kurt received the same set of brackets as I did, but Kare got brackets that look different. The ones Kare has match the design and style of the new brackets. Can it be that Kurt and I received the old-style SES brackets?
Additionally, I saw on Kareās photo that he uses some kind of clip on the new SES brackets which I donāt have. @Kare, did you make them yourself, or were they included?
As far as I understood it, building the hexapod is also to give some feedback on things I noticed along the process. So here we go
Servo boxing
As said before, I really like the premium look of the servo boxes. What I noticed, especially when opening 18 boxes in a row, that not everything is packed in the same way. Sometimes the ācable-boxā is upside down what makes it a bit harder to get to the cables. And also the orientation of the servo is not always the same. I think a small āpacking procedureā to china would make it perfect. But I know, somehow I notice those small things.
screw holes
In the new SES brackets, the unthreaded holes are a bit too small. The screw needs to be screwed in, slightly damaging the thread of the screw.
body plate stretched
In a separate package, Iāve received the 2 body plates. Upon arrival, they are where pretty scratched. It is unclear for me if this happened during transportation or production. The two plates were packed together so it could have happened during transportation. packaging them separately would improve once the plates will be shipped to customers.
body plate hole- layout
As a personal preference, I always try to place as much as possible inside the body. This by not getting a skyscraper of electronics of the back of the bot. Iāve noticed the two triangle-shaped cutouts in the place of the electronics and thought, if they could be turned 90 degrees, they line up with the connection pins of the LSS. This might give more freedom in placing the LSS inside the body and having the pins for the shield/controller board sticking out at the top.
leg length
While viewing the photos from Zentas build I saw that there is some free space between the rubber foot and the aluminum bracket exposing the tube. In my build, they almost touch each other. The tube itself is 51mm long (sorry, Iām using the metric system). Can it be that we received different lengths or did I force the tubes on to hard
Thatās all for now!
Xan
Hi Jeroen, you sure got an eye for details. The last thing first. Iām using the half sphere parts I got during beta testing on the tubes before placing the rubber foot.
I agree about the body it would be nice to be able to place some electronics between the plates too.
Weāll send out the good parts ASAP. Sorry about that. Can you confirm:
Appreciate the feedback!
A. Sometimes the ācable-boxā is upside down what makes it a bit harder to get to the cables. And also the orientation of the servo is not always the same.
Indeed, and weāve brought that up with our manufacturing partner. It will be resolved in the next batch.
B. In the new SES brackets, the unthreaded holes are a bit too small. The screw needs to be screwed in, slightly damaging the thread of the screw.
This is new though - on our end the screws seem to go in without having to be threaded. Anyone else?
C. In a separate package, Iāve received the 2 body plates. Upon arrival, they are where pretty scratched. It is unclear for me if this happened during transportation or production.
Good question. Itās not a final plate, so for now, so long as the functionality is thereā¦
D. Iāve noticed the two triangle-shaped cutouts in the place of the electronics and thought, if they could be turned 90 degrees, they line up with the connection pins of the LSS.
Purely aesthetic. Weāll get them walking and see about dimensional and aesthetic changes. Input is absolutely appreciated.
E. Can it be that we received different lengths or did I force the tubes on to hard
Kurte seems to have addressed that. Did you receive the white 3D printed hemispheres?
So, some questions become:
Tests on how to mount electronics, what to use, wiring (if it changes) etc.
What battery to use and how and where it will be mounted.
Morning all,
I also did not receive the hemispheresā¦ As for 3d printer. Mine has a busted main circuit board. I did order one on Kickstarter, that is a little behind schedule (so for 2 plus years and counting)
Tuesday I did put in an order and I did order 6 of the new released brackets plus some other stuffā¦ Not sure if it shipped yet( 981804) I ordered one of your Atmega328 boards so I could test changes to library worked on it. Thought about ordering spare servo, but you were out of the standard ones and did not check for others. If the order has not shipped yet and you have the things like the hemispheresā¦ But might have already shipped. Often times my mail from you gets eaten by the mail providers Spam filtering, where each morning I get an email showing which ones they quarantined, which I can then release some.
FYI - I pushed up the starting porting of one version of the Phoenix code to work with these servos. Started off with using XBee with the Trossen Commander as inputā¦ I have my T4.1 board with connection up to your servo board. Found another issue with my breakout board with XBee, which I probably should have double checked before I ordered updated boards (which should arrive today). But can easily path. I read the document wrong on which was RX and TX pin. Luckily easy enough I just did not connect those pins up and then used jumpers for the correct oneā¦ The main thing I was doing was routing an actual hardware serial port to your XBee pins. Note with T4 and 4.1 I could have also used FlexIO pins made into serial port and should easily work.
The code compiles. The XBees are talking. Some of the debug monitor stuff is working, when the XBees start talking it tries to stand up. But the math for the different angles is bad, was setup to do offsetting for Dynamixels and legs for PhantomXā¦ But hopefully will figure those out later today.
But if someone wants to beat me to it and see what is there. It is up on the github project I started with test sketches for this.
But right now it is early and I need more coffee!