top of page
Forum Posts
Timothy McCarthy
Jul 04, 2024
In Software
My OpenCatEsp32 build is broken. It looks like the code is incompatible with the Arduino ESP32 3.x upgrade. I get the same results with both the Adruino IDE and the command line tool. Did I miss this mentioned somewhere?
The broken API's:
• ledcSetup
• ledcAttachPin
• ledcDetachPin
• timerBegin
• timerAttachInterrupt
• timerAlarmWrite
• timerAlarmEnable
The API changes are documented in Migration from 2.x to 3.0 - - — Arduino ESP32 latest documentation (espressif.com)
Library version info:
Used library Version
Wire 2.0.0
BLE 2.0.0
BluetoothSerial 2.0.0
MU Vision Sensor 3 1.2.2
Used platform Version
esp32:esp32 3.0.2
1
7
99
Timothy McCarthy
Jun 12, 2024
In Software
I've submitted issue #17 for this.
The sketch contains an stl::string variable.
The issue is the String to stl::string conversion.
This is the first I've a seen a reference to an STL string in the codebase. There are other stl object in teh libraries, I believe, but not string.
If string can be used then so can vector and I'd dearly love to get that in.
Addendum: This issue was resolved by the changes 5be1d6b
Thanks
0
0
16
Timothy McCarthy
Jun 06, 2024
In Software
I've encountered some odd behavior testing the calibrate command. The behavior is the response to the command
c0 1 8 9 9 10 10 11 11 12 12 13 13 14 14 15 15 16
The command attempts to set all the joint offsets to a known value for testing. The documentation describes the T_CALIBRATE ('c') command as
send the robot to calibration posture for attaching legs and fine-tuning the joint offsets. c jointIndex1 offset1 jointIndex2 offset2 ... e.g. c0 7 1 -4 2 3 8 5
This defines two forms for the command:
1. A single 'c', (basic command)
2. A 'c' followed by a offset list
When I issue the basic command
TEST_F(ftfBittleXProtocol, CALIBRATE) {
EXPECT_TRUE(on_calibrate());
EXPECT_EQ(5, response.size());
ASSERT_TRUE(on_abort());
}
I get the response:
response : 5 lines
calib
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
c
c
response : end
The line "calib" is in accordance with the documentation which specifies the calibration posture is set by the command. This is the result of executing the posture skill command "calib". Notice the superfluous trailing comma at the end of the offset list and that command terminates with the echo of the command token twice.
Given this, my expectation of the command response is
response : 5 lines
calib
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
1, 0, 0, 0, 0, 0, 0, 0, 9, 10, 11, 12, 13, 14, 15, 16,
c
c
response : end
That's not what I get, though:
response : 9 lines
calib
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
c
calib
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
c
c
response : end
Disturbia.
I try changing a single offset, "c8 8":
response : 5 lines
calib
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0,
c
c
response : end
That seems right. Now I try commands in sequence: "c0 1" followed by "c8 9":
response : 5 lines
calib
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
c
c
response : end
...
response : 4 lines
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
1, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0,
c
c
response : end
The explanation for why the "calib: line, that sets the posture, is dropped is that the code recognizes that the command prior to the current command (the lastcmd) was the calibrate command so it doesn't reset the posture. This efficiency causes difficulty in response processing, i.e., the response to the same command depends on the prior command. However, it does imply that the calibration posture is somehow related to setting the calibration offsets.
But all this doesn't explain the response to setting a list of more than one offset:
c0 1 8 9 9 10 10 11 11 12 12 13 13 14 14 15 15 16
response : 9 lines
calib
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
c
calib
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
c
c
response : end
To understand this I need to look at how the T_CALIBRATE command is processed in the workhorse function reaction. When I looked at the code I came across the following (at line 381)
else {
// ...
char *pch;
pch = strtok(newCmd, " ,");
// ...
do { /* ... */ } while (pch != NULL);
// ...
delete[] pch;
}
Disturbia.
First, I thought that microcontroller code avoided dynamic memory allocation although it does exist (e.g., QList)
But second, strtok doesn't allocate memory, so there's nothing to delete unless there's an allocation after the do-while loop ends. There isn't one. However, the newCmd buffer is allocated during initialization.
char *newCmd = new char[BUFF_LEN + 1];
Since the do-while loop terminates when pch is NULL, the delete operator deletes a nul pointer which is allowed and benign. This leaves newCmd unaffected.
OTOH, if pch is ever non-nul the delete will do some sort of very bad thing.
Digging deeper, the body of the do-while loop must update pch in order to process the space-comma-tab separated list. It does,
do {
//...
for (byte b = 0; b < 2 && pch != NULL; b++) {
//...
pch = strtok(NULL, " ,\t");
//...
}
//...
The intent is to extract each element pair from the list, which is fine. But then we get
if (token == T_CALIBRATE) {
//...
if (lastToken != T_CALIBRATE) {
//...
strcpy(newCmd, "calib");
loadBySkillName(newCmd);
}
//...
More Disturbia.
The problem is that strok is currently using the newCmd buffer and this overwrites the contents in order to load the calibrate posture. This is done in the body of the do-while loop so the next iteration will try to execute
for (byte b = 0; b < 2 && pch != NULL; b++) {
target[b] = atoi(pch); //@@@ cast
pch = strtok(NULL, " ,\t");
inLen++;
}
using a buffer that now contains "calib". The pch pointer has the address of the 4th element of the newCmd buffer (after scanning the first 2 elements of the string "0 1 8 9...") but is actually pointing to the 'b' of "calib". The function atoi should return zero and the call to strtok return a nul. This results in a partial read of the first of the two elements.
After this the only significant action is a second printing of the servoCalib array, which explains the output I see.
There's a surprise!
A test is necessary for this sort of analysis. First I perform the baseline test
TEST_F(utfreaction, strtok_baseline)
{
char newCmd[]{ "0 1 8 9 10 11 12 13 14 15 15 16" };
char delim[]{ " ,\t" };
char* pch{ strtok(newCmd, delim) };
do {
int target[2]{};
int inLen{0};
for (byte b = 0; b < 2 && nullptr != pch; ++b) {
cout << "pch: " << setw(3) << pch
<< " atoi(pch): " << setw(3) << atoi(pch)
<< '\n';
target[b] = atoi(pch);
pch = strtok(NULL, " ,\t");
inLen++;
}
} while (nullptr != pch);
}
[ RUN ] utfreaction.strtok_baseline
pch: 0 atoi(pch): 0
pch: 1 atoi(pch): 1
pch: 8 atoi(pch): 8
pch: 9 atoi(pch): 9
pch: 10 atoi(pch): 10
pch: 11 atoi(pch): 11
pch: 12 atoi(pch): 12
pch: 13 atoi(pch): 13
pch: 14 atoi(pch): 14
pch: 15 atoi(pch): 15
pch: 15 atoi(pch): 15
pch: 16 atoi(pch): 16
[ OK ] utfreaction.strtok_baseline (0 ms)
Next is the version with strcpy,
TEST_F(utfreaction, strtok_strcpy)
{
char newCmd[]{ "0 1 8 9 10 11 12 13 14 15 15 16" };
char delim[]{ " ,\t" };
char* pch{ strtok(newCmd, delim) };
do {
int target[2]{};
int inLen{ 0 };
for (byte b = 0; b < 2 && nullptr != pch; ++b) {
cout << "pch: " << setw(3) << pch
<< " atoi(pch): " << setw(3) << atoi(pch)
<< '\n';
target[b] = atoi(pch);
pch = strtok(NULL, " ,\t");
inLen++;
}
strcpy(newCmd, "calib");
} while (nullptr != pch);
}
[ RUN ] utfreaction.strtok_strcpy
pch: 0 atoi(pch): 0
pch: 1 atoi(pch): 1
pch: b atoi(pch): 0
pch: 9 atoi(pch): 9
pch: 10 atoi(pch): 10
pch: 11 atoi(pch): 11
pch: 12 atoi(pch): 12
pch: 13 atoi(pch): 13
pch: 14 atoi(pch): 14
pch: 15 atoi(pch): 15
pch: 15 atoi(pch): 15
pch: 16 atoi(pch): 16
[ OK ] utfreaction.strtok_strcpy (16 ms)
That was unexpected. The 'b of "calib" just happens to align with the '8' of the input and allows strtok to skip over it.
This twist leaves the explanation for the output unanswered.
Any ideas?
Conclusion
The delete [] pch seems dangerous and I'd remove it.
Consider using a local buffer to process the offset list. Its size should be enough to hold 16 pairs of int8_t values plus 15 delimiters. Something like,
//...
char list[(DOF * sizeof(int8_t)) + (DOF-1)]{};
memcpy(list, newCmd, sizeof(list));
char *pch;
pch = strtok(list, " ,");
//...
Keeping in mind the input is free format though.
Edit: replace newCmd with use list variable in suggestion
Addendum: After additional thought, it's probably best to separate the list extraction from setting the posture and then setting the offsets. It frees up the newCmd buffer..
0
4
57
Timothy McCarthy
Jun 01, 2024
In Software
The time has finally come to try to share the test code I've been using. (The operative word here "try" because I've never done this before.) This will be the first of 3 repositories that comprise the test code.
The serial library;
The serial library gtest unit test code;
The BittleX gtest feature test code
The serial library repository can be found at https://github.com/FlexoTim/opencat_serial_cpp.git. The repository is a fork of the PetoiCamp repository. I develop with Visual Studio so the solution and project files are the main interface. I have not attempted to modify code for other platforms.
The original documentation for the library should still apply. The serial library solution includes a test project that uses the serial library with e Arduino as a loopback connector.
You should be able to open the solution and build with no changes
If there is an enterprising soul out there who is willing to try, I am seeking confirmation that the repository is complete, and the build process works.
I did not know that a fork is a repository is automatically made public. Otherwise, I would have published the first two repositories together.
0
0
20
Timothy McCarthy
Jun 01, 2024
In Software
This is the second repository associated with OpenCat_Serial_cpp (v1.2.0).
The repository is found at https://github.com/FlexoTim/utSerial.git
The unit tests use Google gtest for the test framework. The solution builds under Visual Studio Community edition with the GTEST package installed. In addition, and Arduino UNO is used for a loopback connection. The GTEST package is out of date with the current Google GTEST code, but the documentation should mostly apply. The OpenCat_Serial documentation should apply.
Usage: utSerial -h should bring up the usage information.
Once more I'm seeking assistance in verifying that I've created the repository fully and the build process completes.
0
0
13
Timothy McCarthy
May 25, 2024
In General Discussions
I recently purchased a ESP32 CAM dev board (with the USB connector board) from Aideepen but I'm having trouble configuring it in Arduino. I can't find the board and the example sketch doesn't have it listed either.
Anyone know how this should be configured? I tried to use AI Thinker but get error messages at boot. It doesn't seem to recognize the ESP chip and there's another about the core dump file.
0
2
64
Timothy McCarthy
May 25, 2024
In Clinic
Recently, I had a problem with my BittleX robot and, after reporting it here, it was diagnosed as having a power circuit failure. I then requested a replacement from support, and they sent the replacement board. (Many thanks go to the Petoi team, from top-to-bottom, for their efforts towards getting me the new board.)
Replacement Biboard installation
The installation of the board was mostly uneventful with one, minor exception. The Ultrasonic sensor that sticks out at the front of the board is unadjusted and may interfere with the movement of the robot head. You should bend the sensors downward towards the shoulder in order to prevent interfering with the head. You can test the head clearance with all power off.
Mysterious Behavior
With the Biboard installed I ran my diagnostic software with terrible results; the bot moved in a chaotic manner, with many collisions between legs and body. I kept reducing the tests performed until I was able to isolate a single test that demonstrates some mysterious behavior previously unknown to me.
The first video (https://youtu.be/EmBHQHdSwBM) shows the mysterious behavior (n.b., the test is run twice for clarity). The same test is performed for each servo, HEAD, RLEG, LLEG, in that order. The test places the bot into a pose that is suitable for performing the test without interference. This is done before and after the actual test to provide a visual cue the test has completed. When the test completes the test framework resets the pose to the "zero" pose.
The test itself is fairly simple. First the servo is set to the maximum angle (120 degrees for the head servo; 90 for all others). When that command completes the servo angle is read and compared to the expected angle. The smaller of the two is declared the maximum value. For the minimum angle the value is -120/-90 digress and the maximum result is used as the minimum angle for the servo.
The mysterious behavior is that the LLEG does not return to the test pose after the minimum angle test. Note that for all other servos the move is performed.
So what is the reported position of the LLEG (servo 15)?
The test pose command sent is verified by reading the servo positions. We can see this in the log. First, the -90 position is set
ftBittleX::ftfBittleX::on_send
TX command : m15 -90
write count : 8
expected : 8
actual : 8
ftBittleX::ftfBittleX::on_response
Command completed: normal
description : INDEXED_SEQUENTIAL_ASC
cmd : m
id : 12
data : 15 -90
1747 ms : RX_latency
0 ms : RX_elapsed
response : 1 lines
m
response : end
Then the position is checked
ftBittleX::ftfBittleX::on_send
TX command : j15
write count : 4
expected : 4
actual : 4
ftBittleX::ftfBittleX::on_response
Command completed: normal
description : Joints
cmd : j
id : 8
data : 15
3 ms : RX_latency
1 ms : RX_elapsed
response : 3 lines
=
-80
j
response : end
ANGLE MISMATCH: -90 != -80
The failure is the purpose of the test.
The test pose is then reset
ftBittleX::ftfBittleX::on_send
TX command : i0 0 12 90 8 0 13 90 9 0 15 90 11 45 14 90 10 45
write count : 49
expected : 49
actual : 49
ftBittleX::ftfBittleX::on_response
Command completed: normal
description : INDEXED_SIMULTANEOUS_ASC
cmd : i
id : 6
data : 0 0 12 90 8 0 13 90 9 0 15 90 11 45 14 90 10 45
692 ms : RX_latency
1 ms : RX_elapsed
response : 1 lines
i
response : end
The pose is verified
ftBittleX::ftfBittleX::on_verify
ftBittleX::ftfBittleX::on_send
TX command : j
write count : 2
expected : 2
actual : 2
ftBittleX::ftfBittleX::on_response
Command completed: normal
description : JOINTS
cmd : j
id : 8
5 ms : RX_latency
9 ms : RX_elapsed
response : 4 lines
=
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
0, 0, -49, -4, 0, 0, 0, 0, 0, 0, 45, 45, 90, 90, 90, 90,
j
response : end
ftBittleX::ftfBittleX::on_verify: joint list match
Disturbia.
The physical state does not match the reported state.
The result of this is that when the test framework tries to move to the "zero" pose, the LLEG is in a collision condition with the body and the movement "slides" the leg along the body until it suddenly moves properly. I have to conclude this is the reverse kinematic algorithm that is controlling this. The mystery is why it didn't happen when the test pose was performed at the test end?
But the mystery gets deeper. I puzzled over this behavior and observed it a number of times when I noticed that the LLEG servo was "twitching" (not "chattering") at the end of the test. I wondered if the servo was still trying to reach -90 and, as a result, it ignored or dropped subsequent commands. As a experiment, I lowered the target angle to one that should be achievable: -80 degrees. The second video (https://youtu.be/eLVuo9PSa1k) shows the result. Sure enough, the LLEG servo now returns to the test pose properly.
So the mystery is,
"What's happening to the servo?"
"Can the condition be detected?"
This behavior leads to undesirable results and potential disturbing collisions..
0
7
54
Timothy McCarthy
May 16, 2024
In Clinic
The purpose of the test is to measure the amount of current drawn by the P1L servo. For current measurement background see How to Measure Current with a Multimeter (youtube.com)
Equipment
•
• Multimeter
• 1 P1L servo
• 2 breadboards (830 tie points each)
• 1 BittleX battery
• ESP32S microcontroller
• BittleX BiBoard, separated (not shown)
• ESP32 USB cable
• USB BittleX cable
• 3 male-male black wires
• 3 male-male red wires
• 1 male-male green wire
• 1 pair male - female dupont connector wires (not shown)
Connect the ESP32 ground pin with a black jumper wire to the top power rail ground rat ow 4.
Connect the battery black ground wire to the top power rail ground at row 61,
Connect the battery red power wire to the top power rail positive at row 61,
Add jumper wires to the P1L servo connector, matching the black and red wires, and using the green wire for the signal. This is the same connection used in the Servo Test post.
Connect a red jumper wire from the top power rail at row 50 to row 50, a
Connect the black servo ground wire to the top power rail ground at row 50
Connect the red servo power wire to the top breadboard at row 40, a. (Not the power rail.)
Connect the ESP32 GPIO pin 33 to row 35,a
Connect the servo signal wire to row 35,d
This produces a "open circuit" similar to that of the servo test but with a open connection between the servo power and the positive battery. This is where we will be make the servo current measurement.
Turn the batter on and connect the ESP32 with the USB cable. Verify that the battery light is on and the ESP32 power light is on. The servo should not move.
We make the current measurement by "closing the circuit" between the servo power and the battery power with the multimeter probes. When the circuit is closed, the servo moves. The video demonstrates the measurement.
Link: https://youtu.be/taWsxyK686c
The current fluctuates between 100 and 130 mA. This provides a reference for the servo current when powered.
The final video replaces the ESP32 with the BiBoard to provide the servo signal (but not the servo power.) I made a simple jig to mount the BiBoard on two wooden sticks and used a pair of wires with dupont connectors from the BiBoard ground and signal pins to the ground rail and servo signal at 35, a, replacing the ESP32 signal wire there.
The purpose of this step is not measure current but to verify that the USB only powered BiBoard sends a valid signal to the servo during the bootup sequence. I would not be able to measure the current due to the short time between powering the BiBoard and servo signal end. I would need a more complex circuit to perform that measurement.
Link: https://youtu.be/5V0Bayp_AjI
Results
The servo current ranges between ~(100 - 130) mA. There are 9 servos in the bot so the total current is ~900 - 1170) mA. This does not include the "leak" current for the signal (per servo), the current for the BiBoard itself, or the current to the BiBoard hat. The head servo isn't as active as the others, so it can reduce the overall demand. Even so, that demand is north of 1A
2
0
17
Timothy McCarthy
May 14, 2024
In Clinic
The purpose of the test is to verify the proper operation of a P1L servo. We'll use the common SG90 servo as a reference.
Equipment
•
• 1 P1L servo
• 1 SG90 servo
• 2 breadboards (830 tie points each)
• One 3.3-5v Power Supply Module with power cable
• 1 BittleX battery
• ESP32S microcontroller
• ESP32 USB cable
• 5 male-male black wires
• 3 male-male red wires
• 4 male-male green wires
We will control the circuit with an ESP32 sketch that will have both servos "sweep" through positions between 0 and 180. This allows us to compare the P1L behavior with the SG90.
It is worth noting that the circuit uses 3 separate power supplies, USB for the ESP32, 3.3-5v module for the SG90 servo, and tje BittleX Battery for the P1L servo. The voltage requirements of each of tthese devices is different from the others. The ESP32 uses a 3.3 volt, the SG90 uses 4.8 v, and the P1L uses 8v.
First, we add the ESP32 module to the breadboard 1 (on the top). The module is inserted into row 1, columns b and i. This provides a single column of ties on either side of the module (columns a and j) that simplfies locating the GPIO pins.
We add the 3.3-5v power supply to breadboard 2, inserting it on the power rails on the right side, taking the first 5 rows.
To provide a common ground for the circuit we connect the ground rails of the top power rail of each breadboard and add a jumper wire from the ESP32 GND pin to the ground rail of the bottom board
For the SG90 servo connections add a red wire to the power (red) connector, a black wire to the ground (brown) connector, and a green wire to the signal (yellow) connector.
Connect the SG90 power and ground jumper wires to the second breadboard top power rail at row 25. This rail is connected to the 5v output of the power supply.
Connect the SG90 signal jumper wire to row 25, a. This completes the circuit for the SG90 servo.
For the P1L servo, repeat the steps to add jumper wires for power, ground, and signal to the P1L connector.
Connect the P1L power and ground jumper wires to the top breadboard, top power and ground rail at row 35.
Connect the P1L signal jumper wire to the top breadboard at row 35, a
For the battery, add red and black wires to the power (red) and ground (black) connectors
For the SG90 signal, use GPIO pin 32, located at row 13, i. Add a jumper wire that to the top breadboard at row 25,c. This connects the GPIO pin to the SG90 signal wire.
For the P1L signal, use GPIO pin 33 located at row 12, i. Add a jumper wire that to the bottom breadboard at row 25,d. This connects the GPIO pin to the P1L signal wire.
Connect the power lines by connecting the USG to the ESP32, the power cable to the 3.3-5 power module, and the battery to the top power rail red wire to the positive and black wire to the negative rails. The battery connection is not shown in the picture.
The ESP32 sweep sketch to control the servos is an adaptation of a common sketch that "sweeps" the servo back and forth through 180 degrees.
/* Adapted from ..
Sweep
by BARRAGAN
This example code is in the public domain.
modified 8 Nov 2013
by Scott Fitzgerald
modified for the ESP32 on March 2017
by John Bennett
see http://www.arduino.cc/en/Tutorial/Sweep for a description of the original code
* Different servos require different pulse widths to vary servo angle, but the range is
* an approximately 500-2500 microsecond pulse every 20ms (50Hz). In general, hobbyist servos
* sweep 180 degrees, so the lowest number in the published range for a particular servo
* represents an angle of 0 degrees, the middle of the range represents 90 degrees, and the top
* of the range represents 180 degrees. So for example, if the range is 1000us to 2000us,
* 1000us would equal an angle of 0, 1500us would equal 90 degrees, and 2000us would equal 1800
* degrees.
*
* Circuit: (using an ESP32 Thing from Sparkfun)
* Servo motors have three wires: power, ground, and signal. The power wire is typically red,
* the ground wire is typically black or brown, and the signal wire is typically yellow,
* orange or white. Since the ESP32 can supply limited current at only 3.3V, and servos draw
* considerable power, we will connect servo power to the VBat pin of the ESP32 (located
* near the USB connector). THIS IS ONLY APPROPRIATE FOR SMALL SERVOS.
*
* We could also connect servo power to a separate external
* power source (as long as we connect all of the grounds (ESP32, servo, and external power).
* In this example, we just connect ESP32 ground to servo ground. The servo signal pins
* connect to any available GPIO pins on the ESP32 (in this example, we use pin 18.
*
* In this example, we assume a Tower Pro MG995 large servo connected to an external power source.
* The published min and max for this servo is 1000 and 2000, respectively, so the defaults are fine.
* These values actually drive the servos a little past 0 and 180, so
* if you are particular, adjust the min and max values to match your needs.
*/
#include "ESP32Servo.h"
namespace esp32 {
namespace example {
namespace sweep {
// create servo object to control a servo
// 16 servo objects can be created on the ESP32
Servo SG90;
Servo P1L;
int pos{ 0 }; // variable to store the servo position
// Recommended PWM GPIO pins on the ESP32 include
// 2,4,12-19,21-23,25-27,32-33
int SG90_servoPin{ 32 };
int P1L_servoPin{ 33 };
void on_setup() {
Serial.begin(115200);
// Allow allocation of all timers
ESP32PWM::allocateTimer(0);
ESP32PWM::allocateTimer(1);
ESP32PWM::allocateTimer(2);
ESP32PWM::allocateTimer(3);
SG90.setPeriodHertz(50); // standard 50 hz servo
P1L.setPeriodHertz(50); // standard 50 hz servo
// attaches the servo on pin SG90_servoPin to the servo object
// using default min/max of 1000us and 2000us
// different servos may require different min/max settings
// for an accurate 0 to 180 sweep
SG90.attach(SG90_servoPin, 500, 2400);
P1L.attach(P1L_servoPin, 500, 2500);
SG90.write(0);
P1L.write(0);
}
void on_loop() {
Serial.print("Forward\n");
// goes from 0 degrees to 180 degrees
// in steps of 1 degree
for (pos = 0; pos <= 180; pos += 1) {
// tell servo to go to position in variable 'pos'
SG90.write(pos);
P1L.write(pos);
delay(15); // waits 15ms for the servo to reach the position
}
Serial.print("Backward\n");
// goes from 180 degrees to 0 degrees
for (pos = 180; pos >= 0; pos -= 1) {
SG90.write(pos);
P1L.write(pos);
delay(15);
}
}
} // namespace sweep
} // namespace example
} // namespace esp32
using namespace esp32::example::sweep;
void setup() { on_setup(); }
void loop() { on_loop(); }
This produces the output:
Sketch uses 272949 bytes (20%) of program storage space.
Maximum is 1310720 bytes.
Global variables use 21716 bytes (6%) of dynamic memory,
leaving 305964 bytes for local variables. Maximum is 327680 bytes.
If all goes well, then turn the power supply on with the button on the module and the SG90 servo should start to rotate. Turn the battery on by pressing the button and the P1L servo should start rotating.
Test Result
Here's the video of the circuit in action. https://youtube.com/shorts/aSo24Ph85Go?feature=share
The test verification is visual. You should see the servos move, with the SG90 sweeping through ~180 degrees and the P1L servo sweep ~250 degrees. There's a problem with the P1L servo if it doesn't move, or moves in a erratic or jerky manner.
To test additional P1L servos, simply swap the servo out via its jumper wires.
Question: I know that the P1L servo has a large angle of rotation (like 240-270) but clearly the position of the SG90 and the P1L for the same angle don't agree. My question is, "How do I know where the P1L actually servo is when I tell it to move to a n angle?" What's the transform between 180 degree and P1L position?
1
0
17
Timothy McCarthy
May 11, 2024
In Clinic
The purpose of the test is to verify that the BittleX battery is providing the correct voltage to power the BittleX robot.
Equipment:
BittleX battery with USB connector
Multi-meter with probes
breadboard
2 male-male 18 or 22 guage wires, one red, one black
Recharge the battery
Make sure the battery is fully charged
Connect the battery to a USB power connection. The battery light should come on.
When the light turns green the battery is fully charged.
Disconnect the USB cable from the battery
Connect the battery to the breadboard
Turn the battery off by pressing the battery button until the light is off
Insert one end of the red wire into the battery connector connected to the red wire of the battery.
Insert the other end of the red into the breadboard positive rail colored red and marked '+'.
Insert one end of the black wire into the battery connector connected to the black wire of the battery.
Insert the other end of the black wire into the breadboard ground rail colored blue (or black) and marked '-'.
Follow the user manual for the multimetter to connect the probes to the multimeter.
Remove any protective cover to the probe ends.
Turn the multimeter on and follow the user manual to set to measure DC voltage.
Press the battery button so the blue light is on.
Insert the Probe for ground (should be black color) into one of the breadboard ground rail holes of the rail that has the battery black wire.
Insert the Probe for positive voltage (should be red color) into one of the breadboard positive rail holes of the rail that has the battery red wire.
Read the voltage from the meter. Here it's 8.29 v
If everything goes well, the meter should read about 8.3 volts.
Common electronics voltages are 3.3 and 5 volts. So 8.3v doesn't seem to be a coincidence.
Turn the battery off by pressing the battery button until the light is off
1
1
35
Timothy McCarthy
May 02, 2024
In Clinic
This started out of the blue.
bootfail.mp4
It looks like he got into the liquor cabinet.
The only additional symptoms I have are:
1. I had just completed a run of unit tests with a failure for SKILL_DATA. The write of the data failed trying to write 934 byte and only writing 704 bytes. So, there may have been a buffer overrun issue.
2. The voice command has been dropping out for some reason. A stray sound (like a cough) can turn it back on. But then it drops out again.
The battery was just recharged.
Tell me doc, will he make it? What's the matter?
0
66
226
Timothy McCarthy
Apr 21, 2024
In Hardware
I'm looking for some guidance on how to tidy up the cables on the arms and legs. The problem is that they keep poking out after any activity. I keep trying to tuck them into the sleeve guards, but they work their way out very quickly. When put on my test stand, the wires keep banging against the column, which is annoying, but the real concern is they will get caught up something and befoul movement.
Any tips would be greatly appreciated.
0
7
35
Timothy McCarthy
Apr 20, 2024
In Software
(aside: I think something went south on the first attempt of this post so this may be a duplicate)
I'm writing unit/feature tests of the serial protocol API and I'm working on the analog read command (Ra<pin>). The pin I use (VOLTAGE, pin 4) comes from the lowBattery function in reaction.h and is defined in opencat.h.
The test is performed on a fully changed battery. I'm having difficulty understanding the response:
=
383
R
I expected to get a value between 0 - 4096 but at the high end of the range. The lowBattery code reads the value into a float
bool lowBattery() {
//...
float voltage = analogRead(VOLTAGE);
and compares the value with a precalculated low_voltage value:
//...
float vFactor = 4096 / 3.3 / 3;
float low_voltage = LOW_VOLTAGE * vFactor;
//...
if (voltage == 0 || voltage < low_voltage ...)
The value of vFactor should be 413.73..., and thus the value of low_voltage is 2813.41...
This doesn't look right. So, I'm missing something here. Is the command response (383) valid? I expect it to be an integer. I don't understand the calculation in lowBattery.
:
0
3
37
Timothy McCarthy
Apr 20, 2024
In Software
I'm writing unit/feature tests of the serial protocol API and I'm working on the digital read command (Rd<pin>). The pin I use (VOLTAGE, pin 4) comes from the lowBattery function in reaction.h and is defined in opencat.h.
The test is performed on a fully charged battery. I'm having difficulty understanding the response:
=
383
R
I expected to get a value between 0 - 4096 but at the high end of the range. The lowBattery code reads the valiue into a float.
bool lowBattery() {
//...
float voltage = analogRead(VOLTAGE);
and compares the value with a precalculated low_voltage value:
#define LOW_VOLTAGE 6.8
//...
float vFactor = 4096 / 3.3 / 3;
float low_voltage = LOW_VOLTAGE * vFactor;
//...
if (voltage == 0 || voltage < low_voltage ...)
The value of vFactor should be 413.73..., and thus the value of low_voltage is 2813.41...
This doesn't look right. I'm missing something here. Is the command response (383) valid? I expect it to be an integer. I don't understand the calculation in lowBattery.
0
0
14
Timothy McCarthy
Apr 19, 2024
In Software
I misstated the command.
I'm testing the analog command Ra<pin>
0
0
15
Timothy McCarthy
Feb 17, 2024
In Basic Assembly and Setup
Disclaimer
The ideas expressed here are my subjective opinions based upon 40 years of software development. I've spent a considerable amount of time documenting or explaining software to people with a wide range of experience with a equally wide range of success. Documenting technical information, either hardware or software, is a difficult task and more often evolves over time. I don't have clear guidelines but do have a few principles that have been helpful, and I know what feels wrong or right when I find it. In any event, these are my opinions and yours may vary. No offense taken or given.
Petoi Doc Center and the Bittle X Assembly
My experience with the Petoi Doc Center and the assembly process for the Bittle X robot is the focus my comments here. The Petoi Doc Center has lots of links and documents on a wide range of topics. This is both an advantage and a disadvantage. The advantage is the range of information available to the user, The disadvantage is that all that information needs organization and maintenance. For the majority of the Doc Center cross links can significantly reduce the organization and maintenance. Users won't be distracted by following the links to different pages ... except when following installation or assembly instructions. Then they want straight line, sequential steps with no deviations. Don't refer them to another page (that saved you maintenance effort. Remember, you're saving their time, not yours.) Whenever possible, verification steps should be used for positive feedback. Background information or justification should be avoided until after the final step. After things are working they'll appreciate the why and if things aren't working the explainations won't be all that helpful.
Keep the target user in focus. For assembly instruction it's the least experienced user. More experienced users will skim over the basics with little effort. Less expereinced users will be easily confused.
Identify things early and be consistent. Is it a sholder or hip? Leg or arm? Or just joint1, joint2, etc. Honestly, the joint numbers still doesn't make sense to me. In my opinion they should be numbered so that I can tell you which is which off the top of my head. There are only 8 servos. Zero is the head and the rest follow sequentially, left sholder-arrm, right sholder-arm, left hip-leg, right hip-leg. I can identify which one given the number or the description in my head. I think the average 12 year old could as well. There are only 8 servos but there are joint numbers 9-15 that hop around the body. This kind of complex numbering should come after initial assembly.
So, as a concrete example, let me describe some of my thoughts on the Bittle X assembly and go into some detail on the calibration instructions.
That package contains a parts list and I do go through these lists and locate all the parts. I was successful but there was an extra part. There's a spare servo but there's also a tiny nylon servo reduction gear that's not on the packing list. (I hope we aren't expecting to open a servo and replace a reduction gear.)
The instructions in the package give me the QR code that brings me to the Doc Center website that I've nver been to before. I'm looking for assembly instructions for a Bittle. (At this point I'm not aware of the distinction between a Bittle and a Bittle X.) The page has the Getting Started link and that links to the Bittle page. I quickly get past sections 1 and 2 with no cause for alarm.
Principle: the product should have a clear means of identification and the user instructed to find it.
As it turns out, if I had followed the Bittle X link I would have ended up on Step 3 which tells me:
"The assembly process of Bittle X is the same as Bittle except for the wire connection on BiBoard."
This has a link to the Bittle assembly section 3 because they are the same, right? But as soon as I go the page I realize they aren't. The heading on the page says, "3.1 Start with the Unassembled Kit" but my Bittle X is partially assembled. I now must read through sections 3.1, 3.2, 3.2.1, 3.2.2, 3.2.3 and stop at section 3.2.4 and forget everyting I just read.
Everything went fine up until section "3.2.7 Battery" where this note appears:
The controller board is already fixed on the body in the following picture. You can finish the Connect Wires chapter and then return here.
This is what I think is a major flaw in presentaions, instructions, and documentaion. This skips a step and tells you to read the instructions out-of-order then return here. But it gets worse. We were told when we came here that the only difference between Bittle and Bittle X was the wiring. What we wonder is where are the wiring instruction for Bittle X? What we don't know until we read past the NyBoard wiring, which doesn't look anything like our board, is the the BiBoard wiring is shown. But then the pictures revert back to the NyBoard! Those photos show the wire wrapped around the board support posts, which is physically impossible for the BiBoard with the hat. Nor have we been shown how to install the board (is the hat up or down?)
I won't go further here into my experience with the wiring other than to say it was extremely cramped and fraught with peril. It was as a result of this that I desperately wanted to verify that the wiring was electrically sound before I went any further.
After tearing down and reassembling the robot 5 or 6 times I found a way to do it easily and reliably. You first need to know that the hat faces down, not up. You might deduce that from the photo of the Bittle X wiring. But it should be pointed out.
Second, and most important, don't screw the board to the frame until after you connect the servo wires. You have more slack in the wires and more room to connect them to the proper pins.
Third, the target user doesn't know positive or negative or ground or color coding. They do know black from white though. A simple guide, "Black on top, pin closest to the main board with the big silver square" leads to the second guide: the pins are numbered and should be used. Left front arm goes to 32, left sholder goes to 33, head goes to 19. Isn't this the reason the numbers are on the board?
OK, this is long enough on this. Once more, these are my opinions and I'm retired and probably a little out of step. Next post I'll try to tackle the calibration process.
1
2
79
Timothy McCarthy
Feb 15, 2024
In Basic Assembly and Setup
My Bittle X is misbehaving. I purchased the STEM version and followed and completed the assembly process but I'm not certain it's correct. Running the UI program to calibrate the joints I get unexpected results when selecting the different postures. All the joints are zeroed. Using the IR remote I get similar misbehavior. The Arduino IDE can successfully connect the console to the BiBoard and get output. I have not uploaded any sketch to the ESP32 nor updated the firmware so the software is as shipped. I'm reluctant to upload a sketch or firmware until I know the cause of the misbehavior. I'm not familiar with the ESP32 archetecture (yet) so I don't know if uploading a sketch will erease the misbehaving code. I have not compiled the OpenCat software yet.
Misbehavior Description
1. Uncertain Calibration posture
2. Incorrect Stand posture
3. Intermittent Voice Command
4. Head-Arm clearance
5. Remote Control layout
At this point I need assitance to get past these issues; I've run out of ideas for non-destructive tests and afraid of making unrecoverable changes. I suspect the problem is due to a simple assembly or software error. I won't describe each problem in this post but will in subsequent posts as I become more familiar with using this forum. But to start I will post the output from the UI and Aduino consoles and (hopefully) a picture of the incorrect stand posture to provide a starting point for discussion.
Some questionss:
"Is Bittle X properly assembled?"
Rest posture
Note: The sholder servos have the logo facing inward. The leg servos have the logo facing outward. As far as I can tell the leg and arm connectors are identical; I followed the videos and put the red sholder on the top-outside of all 4 legs.
"Is this the correct calibration posture?"
Calibration Posture
I'm not sure what the purpose of the Calibration pose is. The UI uses it when adjusting the servo offsets but the Rest pose is the one used for the initial calibration. Adjusting from the calibration pose has no reference. The back legs tend to collide with the body frame with disturbing sounds.
This is the incorrect stand posture
Stand Posture
UI Console output
ardSerial date: Jan 23, 2024
['3', '7', '9']
C:\Users\...\.config\Petoi already exists
C:\Users\...\.config\Petoi\defaultConfig.txt
2024-02-12 23:50:16,911 ardSerial - INFO - ['English', 'Bittle X', '.\\release', '2.0', 'NyBoard_V1_2', 'Standard', 'Nature', 'Earth']
*** Available serial ports: ***
COM5
Bittle
B_231106
2024-02-12 23:50:30,859 ardSerial - INFO - Connect to serial port list:
2024-02-12 23:50:30,859 ardSerial - INFO - COM5
re ['c\r\n', 'calib\r\n0\t1\t2\t3\t4\t5\t6\t7\t8\t9\t10\t11\t12\t13\t14\t15\t\r\n0,\t0,\t0,\t0,\t0,\t0,\t0,\t0,\t0,\t0,\t0,\t0,\t0,\t0,\t0,\t0,\t\r\nc0,\t0,\t\r\n']
of calib
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
c0, 0,
Arduino IDE Console output (Battery OFF)
Scanning I2C network...
- I2C device found at address 0x54 !
- I2C device found at address 0x5C !
- I2C device found at address 0x68 !
- I2C device found at address 0x69 !
- I2C device found at address 0x7E !
- done
Initializing MPU...
OK
- Testing MPU connections...attempt 0
- MPU6050 connection successful
- Initializing DMP...
- Enabling DMP...
- Enabling interrupt detection (Arduino external interrupt 26)...
- DMP ready! Waiting for the first interrupt...
Bluetooth name: Bittle0E
Waiting for a client connection to notify...
Bluetooth name: Bittle0E
The device is started, now you can pair it with bluetooth!
Setup ESP32 PWM servo driver...
Calibrated Zero Position
135 120 135 135 190 80 190 80 190 80 80 190
Build skill list...60
Init voice
Number of customized voice commands on the main board:
10
TaskQ
Ready!
rest
g
d
Thanks
0
10
109
Timothy McCarthy
Feb 15, 2024
In Software
Does anyone have a working Arduino build for Bittle X ESP32 they can share?
I'm struggling to follow the documentation but it's left me uncertain if I have a proper build process.
I have a STEM Bittle X ESP32 but it's misbehaving at the calibration step. I'm resonably certain of the body and electrical assembly are correct; I can use the Arduino console to control all the servos and they seem to display a full 180 degree range (-90 to 90); the Rest and Calibration poses appear to be correct. The Stand pose is invalid; the rest of the poses are "close" but still invalid. Most of the behaviors are unstable and some are spasmotic. The voice command has worked only once and stopped. The IR works but some buttons are inactive.
Before I overwrite the shipped softwrae I want to be sure I have a correct build so I don't "brick" the robot. I'd also like to diagnose what's wrong with the shipped code.
I have a Windows 11 with Arduino 2.3.1. This is working; I have an Elegoo Arduino that I can write sketches to.
I've installed the ESP32 library from Expressif (v2.0.12) but found nothing on how it is used in the OpenCat.ino build..
I was able to successfully compile the OpenCat build for an Arduino UNO when disconnected from robot. It uses 99% of the program space and nearly all the dynamic memory. That doesn't use all ESP32 memory.
I didn't find any documentation on how to setup the BiBoard and only by chance, after searching all over the site, I found OpenCat32. I successfuly compiled (with a few changes due to compile errors) that with the ESP32 Dev Module:
"Sketch uses 1207073 bytes (92%) of program storage space. Maximum is 1310720 bytes. Global variables use 43640 bytes (13%) of dynamic memory, leaving 284040 bytes for local variables. Maximum is 327680 bytes."
These results are disappointing. I'm just not sure I have this right.
Questions:
• Do I have to use Arduino 1.18 IDE?
• Do I have to use 2.0.12 Expressif version?
• How to setup a BiBoard or is ESP32 Dev Module the right board?
Thanks
0
3
105
Timothy McCarthy
更多動作
bottom of page