On this page
- 1. Big Picture
- 2. Arduino IDE Setup
- 3. Main Files
- 4. What
setup()andloop()Mean - 5. Uploading Code To The Robot
- 6. Serial Monitor Setup
- 7. Core Serial Commands
- 8. The
qTask Queue Command - 9. Built-In Skills
- 10. Servo / Joint Numbers
- 11. C++ Functions You Will Actually Use
- 12. Writing Your Own Motion Function
- 13. Creating Your Own Serial Commands
- 14. Moving Multiple Servos At Once
- 15. Sensors
- 16.
XExtension / Module Commands - 17. Troubleshooting Quick Reference
- 18. Teaching Flow For Kids
- 19. Bigger Project Ideas
- 20. Extra Firmware Features Worth Knowing
- 21. Special Project Ideas
- 22. Best Beginner Habits
- 23. Useful Links
BittleDog
Petoi Bittle BiBoard Coding Cheat Sheet
1. Big Picture
There are three different ways to control the robot:
| Method | Do you upload new firmware? | Best for |
|---|---|---|
| Serial Monitor commands | No | Testing built-in skills, moving servos, quick demos |
q task queue scripts | No | Sending a short “program” like sit -> wait -> move head -> balance |
C++ code in OpenCatEsp32.ino | Yes | Custom functions, sensors, class projects, reusable tricks |
Important beginner rule:
You cannot send brand-new C++ code through Serial Monitor. C++ must be uploaded with Arduino IDE. But after a good firmware is uploaded, you can send many robot commands without reuploading.
2. Arduino IDE Setup
Recommended Arduino IDE Setup
Use Arduino IDE 2.x.
Install the ESP32 board package:
- Open Arduino IDE.
- Go to
File > Preferences. - In
Additional Boards Manager URLs, add:
https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json- Go to
Tools > Board > Boards Manager. - Search for
esp32. - Install
esp32 by Espressif Systems. - For this OpenCatEsp32 code, use version
2.0.12.
Why 2.0.12?
This project uses ESP32 Arduino APIs that compile correctly with the 2.x ESP32 core. Newer 3.x versions changed some lower-level APIs, which is why you saw errors around ESP32/NVS functions before.
Arduino Board Settings
Use these settings in Tools:
| Setting | Value |
|---|---|
| Board | ESP32 Dev Module |
| Port | Your robot port, for example COM5 |
| Upload Speed | 921600, or 115200 if upload is unreliable |
| CPU Frequency | 240MHz (WiFi/BT) |
| Flash Frequency | 80MHz |
| Flash Mode | QIO |
| Flash Size | 4MB |
| Partition Scheme | Default 4MB with spiffs |
| Core Debug Level | None |
| PSRAM | Disabled |
If upload gives Invalid head of packet, change Upload Speed to 115200.
Libraries You May Need
Install these from Tools > Manage Libraries only when needed:
| Library | When needed |
|---|---|
ArduinoJson by Benoit Blanchon | Needed by parts of the downloaded OpenCatEsp32 source |
WebSockets / arduinoWebSockets | Only if WEB_SERVER is enabled |
MuVisionSensor3 | Only if CAMERA is enabled |
If you get a missing file like:
esp_partition.h: No such file or directorythen ESP32 board support is missing, wrong, or the wrong board is selected.
If you get a missing file like:
MuVisionSensor.h: No such file or directory
WebSocketsServer.h: No such file or directorythen an optional module is enabled but its library is not installed. The beginner fix is usually to disable that feature by commenting out its #define.
Example:
// #define CAMERA
// #define WEB_SERVER
You do not need to install g++ separately just to upload Arduino code. Arduino IDE brings the compiler toolchain through the ESP32 board package.
Top Of OpenCatEsp32.ino
The top of your sketch chooses the robot model, board, and optional modules.
For your normal Bittle with BiBoard V1, the important part is:
#define BITTLE
#define BiBoard_V1_0Only one robot model should be enabled:
#define BITTLE
// #define NYBBLE
// #define VT
// #define CUB
// #define MINI
Only one board should be enabled:
// #define BiBoard_V0_1
// #define BiBoard_V0_2
#define BiBoard_V1_0
// #define BiBoard2
Optional modules can be turned on or off:
#define VOICE
#define ULTRASONIC
#define PIR
#define DOUBLE_TOUCH
#define DOUBLE_LIGHT
#define DOUBLE_INFRARED_DISTANCE
#define GESTURE
// #define CAMERA
#define QUICK_DEMO
// #define ROBOT_ARM
Beginner rule:
Leave optional modules off unless you are actively using that hardware or have installed the needed library.
3. Main Files
| File | What it is for |
|---|---|
OpenCatEsp32\OpenCatEsp32.ino | Main Arduino sketch. Put beginner custom code here. |
OpenCatEsp32\src\OpenCat.h | Main firmware settings, board definitions, token names, pin definitions, servo limits. |
OpenCatEsp32\src\taskQueue.h | Defines the task queue used by q and tQueue->addTask(...). |
OpenCatEsp32\src\reaction.h | Handles commands like k sit, i 0 30, gB, etc. |
OpenCatEsp32\src\InstinctBittleESP.h | Built-in skills like sit, up, wkF, hi, etc. |
OpenCatEsp32\src\moduleManager.h | Handles sensors/modules and the X commands. |
For beginner projects, spend most of your time in:
OpenCatEsp32\OpenCatEsp32.ino4. What setup() and loop() Mean
Arduino programs usually have two main functions:
void setup() {
// Runs once when the robot starts, resets, or finishes uploading.
}
void loop() {
// Runs forever after setup finishes.
}For this robot:
initRobot();must run once in setup(). It initializes the IMU, servos, skill list, Bluetooth, modules, and task queue.
Beginner mistake:
If you put your whole trick in setup(), it will only run after reset/upload.
Better pattern:
- Write your trick as a function.
- Upload it once.
- Trigger it from Serial Monitor with your own command.
5. Uploading Code To The Robot
- Open
OpenCatEsp32\OpenCatEsp32.inoin Arduino IDE. - Select
Tools > Board > ESP32 Dev Module. - Select the robot port, for example
Tools > Port > COM5. - Click
Verifyfirst. - Click
Upload.
If upload fails with:
Wrong boot mode detectedtry this:
- Click Upload.
- When Arduino IDE says
Connecting..., hold theBOOTbutton on the BiBoard. - Release
BOOTonce it starts writing.
If upload fails with serial corruption:
- Set
Tools > Upload Speed > 115200. - Use a direct USB cable, not a hub.
- Turn the battery switch off during upload.
- Close Serial Monitor before uploading.
When you see:
Leaving...
Hard resetting via RTS pin...that usually means the upload finished correctly.
6. Serial Monitor Setup
Open Serial Monitor in Arduino IDE:
- Select the robot port, for example
COM5. - Set baud rate to
115200. - Use
No line endingfor simple commands.
If No line ending acts odd, Newline usually also works because OpenCat trims newline characters.
Try this first:
?The robot should print model/version info.
7. Core Serial Commands
Serial commands are usually one letter plus optional parameters.
You can type with or without a space after the first letter:
k sit
ksitBoth usually work.
Common Commands
| Command | Meaning | Example |
|---|---|---|
? | Query robot info | ? |
k <skill> | Run a built-in skill | k sit |
k <gait> <number> | Run a gait with an argument | k wkF 5 |
j | Print all current joint angles | j |
j <index> | Print one joint angle | j 8 |
i <index angle ...> | Move multiple joints at the same time | i 0 30 8 60 |
m <index angle ...> | Move joints one after another | m 0 30 0 -30 0 0 |
d | Rest/shut off servos | d |
d <index> | Shut off one servo | d 8 |
q... | Queue a multi-step mini-program | qk sit:1000>k balance:0> |
b ... | Beep notes | b 12 8 14 8 16 4 |
g | Toggle gyro/balance behavior | g |
gb | Turn gyro balance off | gb |
gB | Turn gyro balance on | gB |
gc | Calibrate IMU/gyro | gc |
g? | Print gyro state | g? |
p | Pause/resume motion | p |
z | Toggle random/auto behaviors | z |
. | Accelerate gait speed | . |
, | Decelerate gait speed | , |
Skill Commands
Basic posture examples:
k sit
k up
k balance
k restWalking examples:
k wkF
k wkL
k wkR
k bkParameterized examples:
k wkF 5
k wkF 3000
k wkL 90
k wkR 90For straight gaits:
| Number | Meaning |
|---|---|
Less than 200 | Number of gait cycles |
200 or more | Time in milliseconds |
For turning gaits like wkL or wkR, the number can be used as a turn angle when gyro turning is active.
8. The q Task Queue Command
The q command lets you send a mini-program without uploading new C++.
Format:
q<token command>:<delayAfterMs>><token command>:<delayAfterMs>>Each step has:
| Part | Meaning |
|---|---|
q | Start a queued script |
k sit | The command to run |
:1000 | Wait 1000 ms before the next task |
> | End this task |
Example: sit, wait 3 seconds, then balance:
qk sit:3000>k balance:0>Example: sit, look right, look left, center head, balance:
qk sit:1000>m 0 20:500>m 0 -20:500>m 0 0:500>k balance:0>Example: move head and one leg at the same time:
qk sit:1000>i 0 30 8 60 12 -40:800>i 0 0:500>k balance:0>The delay after a task is not exactly speed. It is how long the queue waits before loading the next task.
9. Built-In Skills
Use them like:
k sit
k hi
k wkFMost Useful Beginner Skills
| Skill | Use |
|---|---|
sit | Sit posture |
up | Stand up |
balance | Balanced standing posture |
rest | Rest posture |
str | Stretch |
zero | Zero posture |
calib | Calibration posture |
buttUp | Rear-up posture |
wkF | Walk forward |
wkL | Turn/walk left |
wkR | Turn/walk right |
bk | Back up |
trF | Trot forward |
crF | Crawl forward |
vtF | Stepping/trot style forward |
hi | Wave/hi |
hsk | Handshake |
snf | Sniff |
pee | Funny demo behavior |
pu | Push-up style behavior |
pd | Play dead |
rc | Recover |
Full Skill Name List In Your Current Bittle Firmware
These are command names from InstinctBittleESP.h. Some are gentle, some are not. Test new ones on the floor with space around the robot.
ang, balance, bdF, bf, bk, bkF, bkL, bkR, buttUp, bx,
calib, carpetF, carpetL, chr, ck, clap, cmh, crF, crL, crR,
dg, dropped, dropRec, ff, fiv, flip, flipD, flipF, gdb, gpF, gpL,
hds, hg, hi, hlw, hsk, hu, hunt, jmp, jpF, kc, knock, launch,
lftF, lftL, lifted, lnd, lpov, lucky, mw, nd, pd, pee, phF, phL,
pick, pickD, pickF, pu, pu1, put, putD, putF, rc, rest, rl, scrh,
showOff, sit, snf, str, tbl, toss, tossD, tossF, trF, trL, trR,
ts, up, vtF, vtL, vtR, wh, wkF, wkL, wkR, zero, zzNote:
Some right-side commands like wkR, trR, crR, and vtR are mirrored internally from stored left-side skills.
Random skill:
k x10. Servo / Joint Numbers
The robot uses internal joint numbers. For normal Bittle coding, focus on joint 0 and joints 8 through 15.
Main Bittle Joint Map
| Joint index | Body part | Beginner notes |
|---|---|---|
0 | Head pan | Safe for beginner head turning |
1 | Tail / optional add-on / arm-related joint | Avoid unless you know your hardware uses it |
2 | Optional add-on / arm-related joint | Avoid on normal Bittle |
3 | Optional add-on / arm-related joint | Avoid on normal Bittle |
4 to 7 | Not used for normal 8-DOF Bittle walking setup | Avoid |
8 | Left front shoulder | Leg joint |
9 | Right front shoulder | Leg joint |
10 | Right back shoulder | Leg joint |
11 | Left back shoulder | Leg joint |
12 | Left front knee | Leg joint |
13 | Right front knee | Leg joint |
14 | Right back knee | Leg joint |
15 | Left back knee | Leg joint |
The leg order in the firmware is:
LF, RF, RB, LB
Left Front, Right Front, Right Back, Left BackAngle Limits From Your Firmware
These are software limits from OpenCat.h.
| Joint index | Min angle | Max angle |
|---|---|---|
0 | -120 | 120 |
1 | -85 | 85 |
2 | -120 | 120 |
3 | -120 | 120 |
4 | -90 | 60 |
5 | -90 | 60 |
6 | -90 | 90 |
7 | -90 | 90 |
8 | -200 | 80 |
9 | -200 | 80 |
10 | -80 | 200 |
11 | -80 | 200 |
12 | -80 | 200 |
13 | -80 | 200 |
14 | -80 | 200 |
15 | -80 | 200 |
Beginner safety rule:
- Type
jto read the current angles. - Move only 10 to 20 degrees away from the current angle at first.
- Test on the floor, not near the edge of a table.
- If the robot gets weird, type
d.
Example:
j
i 0 20
i 0 -20
i 0 0
d11. C++ Functions You Will Actually Use
initRobot()
initRobot();Initializes the robot. Call it once in setup().
Do not call it repeatedly in loop().
tQueue->addTask(...)
This is the most useful OpenCat C++ function for beginner projects.
tQueue->addTask(token, command, delayAfterMs);Parameters:
| Parameter | Type | Meaning |
|---|---|---|
token | char or token constant | What kind of command this is |
command | string | The command data |
delayAfterMs | integer | How long to wait before the next queued task |
Common token constants:
| Token constant | Same as | Meaning |
|---|---|---|
T_SKILL | 'k' | Built-in skill |
T_INDEXED_SIMULTANEOUS_ASC | 'i' | Move joints together |
T_INDEXED_SEQUENTIAL_ASC | 'm' | Move joints one at a time |
T_BEEP | 'b' | Beep |
T_REST | 'd' | Rest/shut off servos |
T_TASK_QUEUE | 'q' | Queue command |
Examples:
tQueue->addTask(T_SKILL, "sit", 1000);
tQueue->addTask(T_SKILL, "balance", 0);
tQueue->addTask(T_INDEXED_SIMULTANEOUS_ASC, "0 30 8 60", 800);
tQueue->addTask(T_INDEXED_SEQUENTIAL_ASC, "0 30 0 -30 0 0", 500);
tQueue->addTask(T_BEEP, "12 4 14 4 16 2", 0);
tQueue->addTask(T_REST, "", 0);tQueue->cleared()
if (tQueue->cleared()) {
// safe to add a new reaction
}Use this before adding sensor-triggered tasks. It prevents your code from filling the queue over and over.
loadBySkillName(...)
loadBySkillName("sit");Loads a skill directly. This is used inside the firmware, but for beginner projects prefer:
tQueue->addTask(T_SKILL, "sit", 1000);calibratedPWM(...)
calibratedPWM(jointIndex, angle, speedRatio);Directly moves one joint.
Parameters:
| Parameter | Meaning |
|---|---|
jointIndex | Servo/joint number, like 0, 8, 12 |
angle | Target angle in degrees |
speedRatio | Optional movement smoothing/speed value |
Example:
calibratedPWM(0, 30, 1);Beginner note:
Use tQueue->addTask(T_INDEXED_SIMULTANEOUS_ASC, "0 30", 500); first. It is easier and fits the command system better.
transform(...)
transform(targetFrame, angleDataRatio, speedRatio, offset, period, runDelay);Advanced function for moving many joints toward a full target frame.
Main parameters:
| Parameter | Meaning |
|---|---|
targetFrame | Array of target angles |
angleDataRatio | Multiplies angle data, usually 1 for your code |
speedRatio | Higher means fewer interpolation steps |
offset | Starting joint index, usually 0 |
period | Used for gait data, usually 0 for simple target frames |
runDelay | Delay used in gait-style movement |
Simple direct-frame example:
int frame[16] = {
0, 0, 0, 0,
0, 0, 0, 0,
75, 75, 75, 75,
-55, -55, -55, -55
};
transform(frame, 1, 1);For teaching, use the task queue first and teach transform(...) later.
beep(...)
beep(note, duration, pause, repeat);Parameters:
| Parameter | Meaning |
|---|---|
note | Pitch step |
duration | How long the note lasts |
pause | Pause after the note |
repeat | Number of repeats |
Example:
beep(15, 100, 50, 3);meow(...)
meow(startF, endF, duration);Makes a rising/falling tone.
Example:
meow(5, 25, 8);printToAllPorts(...)
printToAllPorts("Hello");
printToAllPorts(analogRead(ANALOG2));Prints to USB Serial, Bluetooth, and other active ports.
Useful for debugging student projects.
Standard Arduino Functions
| Function | Meaning | Example |
|---|---|---|
pinMode(pin, mode) | Set a pin as input/output | pinMode(ANALOG2, INPUT); |
analogRead(pin) | Read analog value, usually 0 to 4095 on ESP32 | analogRead(ANALOG2) |
digitalRead(pin) | Read HIGH or LOW | digitalRead(BACKTOUCH_PIN) |
digitalWrite(pin, value) | Set an output HIGH or LOW | digitalWrite(2, HIGH) |
millis() | Milliseconds since startup | if (millis() - lastTime > 1000) |
delay(ms) | Pause everything | delay(1000) |
Avoid long delay(...) calls inside loop() because they can block balance, sensors, and serial reading.
12. Writing Your Own Motion Function
Put custom functions above setup() in OpenCatEsp32.ino.
Example:
void myHeadDemo() {
tQueue->addTask(T_SKILL, "sit", 1000);
tQueue->addTask(T_INDEXED_SIMULTANEOUS_ASC, "0 30", 500);
tQueue->addTask(T_INDEXED_SIMULTANEOUS_ASC, "0 -30", 500);
tQueue->addTask(T_INDEXED_SIMULTANEOUS_ASC, "0 0", 500);
tQueue->addTask(T_SKILL, "balance", 0);
}To run it once at startup:
void setup() {
Serial.begin(115200);
Serial.setTimeout(SERIAL_TIMEOUT);
while (Serial.available() && Serial.read())
;
initRobot();
myHeadDemo();
}But for repeated testing, do not call it from setup(). Trigger it with Serial instead.
13. Creating Your Own Serial Commands
This lets you type something like:
ydemoand run your own C++ function.
Step 1: Add Your Motion Function
Put this above setup():
void myHeadDemo() {
tQueue->addTask(T_SKILL, "sit", 1000);
tQueue->addTask(T_INDEXED_SIMULTANEOUS_ASC, "0 30", 500);
tQueue->addTask(T_INDEXED_SIMULTANEOUS_ASC, "0 -30", 500);
tQueue->addTask(T_INDEXED_SIMULTANEOUS_ASC, "0 0", 500);
tQueue->addTask(T_SKILL, "balance", 0);
}Step 2: Add A Custom Command Handler
Also above setup():
void clearMyCommand() {
token = '\0';
newCmd[0] = '\0';
cmdLen = 0;
newCmdIdx = 0;
}
void runMySerialCommands() {
if (token != 'y') return;
if (!strcmp(newCmd, "demo")) {
myHeadDemo();
} else {
printToAllPorts("Unknown y command");
}
clearMyCommand();
}Step 3: Call It From loop()
Find this part of loop():
readSignal();Right after it, add:
runMySerialCommands();Now upload once.
Then type this in Serial Monitor:
ydemoYou can add more commands:
if (!strcmp(newCmd, "demo")) {
myHeadDemo();
} else if (!strcmp(newCmd, "situp")) {
tQueue->addTask(T_SKILL, "sit", 1000);
tQueue->addTask(T_SKILL, "up", 1000);
}Why use y?
OpenCat already uses many letters like k, i, m, g, q, and X. The letter y is not one of the main built-in command tokens, so it is a convenient beginner custom command prefix.
14. Moving Multiple Servos At Once
Use the i command or T_INDEXED_SIMULTANEOUS_ASC.
Serial Monitor:
i 0 30 8 60 12 -40Meaning:
| Pair | Meaning |
|---|---|
0 30 | Move joint 0 to 30 deg |
8 60 | Move joint 8 to 60 deg |
12 -40 | Move joint 12 to -40 deg |
C++:
tQueue->addTask(T_INDEXED_SIMULTANEOUS_ASC, "0 30 8 60 12 -40", 800);Move joints one at a time:
tQueue->addTask(T_INDEXED_SEQUENTIAL_ASC, "0 30 0 -30 0 0", 500);15. Sensors
BiBoard V1 Pin Names In This Firmware
These are defined in OpenCat.h for BiBoard V1 Rev D/E:
| Name | ESP32 pin | Notes |
|---|---|---|
ANALOG1 | 34 | Analog input |
ANALOG2 | 35 | Analog input |
ANALOG3 | 36 | Analog input |
ANALOG4 | 39 | Analog input |
BACKTOUCH_PIN | 38 | Back touch input |
UART_RX2 | 9 | Grove UART2 receive |
UART_TX2 | 10 | Grove UART2 transmit |
VOICE_RX | 26 | Voice module serial receive |
VOICE_TX | 25 | Voice module serial transmit |
General sensor rules:
- Use sensors that support
3.3Vlogic. - Connect sensor
GNDto robotGND. - Use Grove/I2C/Qwiic style sensors when possible.
- Do not connect random 5V signal wires directly to ESP32 pins.
- Start with reading values in Serial Monitor before making the dog move.
Simple Analog Sensor Pattern
Example using a PIR-style or analog sensor on ANALOG2:
bool motionWasDetected = false;
unsigned long lastSensorActionTime = 0;
void setup() {
Serial.begin(115200);
Serial.setTimeout(SERIAL_TIMEOUT);
while (Serial.available() && Serial.read())
;
initRobot();
pinMode(ANALOG2, INPUT);
}
void myPirReaction() {
int reading = analogRead(ANALOG2);
bool motionNow = reading > 3000;
if (motionNow && !motionWasDetected && tQueue->cleared()
&& millis() - lastSensorActionTime > 2000) {
printToAllPorts("Motion detected");
tQueue->addTask(T_SKILL, "sit", 500);
tQueue->addTask(T_INDEXED_SIMULTANEOUS_ASC, "0 40", 500);
tQueue->addTask(T_INDEXED_SIMULTANEOUS_ASC, "0 -40", 500);
tQueue->addTask(T_INDEXED_SIMULTANEOUS_ASC, "0 0", 500);
lastSensorActionTime = millis();
}
motionWasDetected = motionNow;
}Inside loop(), call the sensor reaction after readSignal():
readSignal();
myPirReaction();Beginner Sensor Project Ideas
| Sensor | Port style | Project idea |
|---|---|---|
| Time-of-flight distance sensor | I2C | Dog backs up when your hand gets too close |
| Gesture sensor | I2C | Wave hand left/right to pick tricks |
| Sound sensor | Analog | Clap once to sit, clap twice to stand |
| Touch strip | I2C or analog | Pet the robot’s back to trigger happy motions |
| Color sensor | I2C | Color cards choose different behaviors |
| Line finder | Digital/analog | Follow a line on the floor |
16. X Extension / Module Commands
The X token turns module demos on and off.
| Command | Meaning |
|---|---|
X? | Show active module status |
X | Disable most active modules |
XT | Enable double touch module |
Xt | Disable double touch module |
XL | Enable double light module |
Xl | Disable double light module |
XD | Enable double IR distance module |
Xd | Disable double IR distance module |
XI | Enable PIR module |
Xi | Disable PIR module |
XU | Enable ultrasonic module |
Xu | Disable ultrasonic module |
XG | Enable gesture module |
Xg | Disable gesture module |
XQ | Enable quick demo |
Xq | Disable quick demo |
In your BiBoard V1 firmware, Grove Serial, Voice, and BackTouch are enabled by default.
For beginner coding, it is often easier to write your own sensor function than to use the built-in module demos.
17. Troubleshooting Quick Reference
Serial Monitor Says Not Connected
- Check USB cable.
- Select the correct port in Arduino IDE.
- Close any other program using the port.
- Reopen Serial Monitor at
115200.
Robot Uploaded But Does Not Move
- The board may be powered by USB but servos need battery power.
- Turn the robot battery on after upload.
- Type
k upork sit. - Check that the robot is not in
drest state.
Robot Thinks It Is Falling
Try:
d
gb
gcThen put the robot flat and still while it calibrates.
Use gB to turn gyro balance back on.
Upload Fails
Common fixes:
- Close Serial Monitor.
- Use Upload Speed
115200. - Hold
BOOTduringConnecting.... - Use a direct USB cable.
- Turn battery off during upload.
Compile Fails With Missing Library
Either install the missing library or disable the module that needs it.
Examples:
// #define CAMERA
// #define WEB_SERVER
18. Teaching Flow For Kids
Good lesson order:
- Serial Monitor: type
k sit,k up,i 0 30,j. - Variables: change angles and wait times.
- Functions: create
myHeadDemo(). - Queues: use
qandtQueue->addTask(...). - If-statements: react to a sensor threshold.
- Timing: replace long
delay(...)withmillis(). - Arrays: store multiple servo poses.
- Final project: students design one trick, one sensor reaction, and one custom Serial command.
Starter student challenge:
Make the dog sit, look left, look right, beep twice, then stand.Serial solution:
qk sit:1000>i 0 30:500>i 0 -30:500>b 12 4 16 4:300>k up:0>C++ solution:
void studentTrick() {
tQueue->addTask(T_SKILL, "sit", 1000);
tQueue->addTask(T_INDEXED_SIMULTANEOUS_ASC, "0 30", 500);
tQueue->addTask(T_INDEXED_SIMULTANEOUS_ASC, "0 -30", 500);
tQueue->addTask(T_BEEP, "12 4 16 4", 300);
tQueue->addTask(T_SKILL, "up", 0);
}19. Bigger Project Ideas
These projects are good because they teach real programming ideas, not just robot tricks.
| Project | What the robot does | Programming concepts |
|---|---|---|
| Emotion dog | Has moods like happy, sleepy, curious, scared | Functions, variables, state |
| Simon Says | Student sends commands and robot must follow only if “Simon says” | If-statements, strings, command parsing |
| Dance composer | Students build a dance from a list of poses | Arrays, loops, sequencing |
| Guard dog | Reacts to motion, touch, sound, or distance | Sensors, thresholds, events |
| Follow my hand | Moves based on distance sensor readings | Sensor input, feedback, control loops |
| Red light green light | Walks on green, freezes/sits on red | State machines, rules |
| Robot petting game | Touch sensor changes mood or behavior | Input events, cooldown timers |
| Obstacle avoider | Walks until object is close, then turns | If-statements, sensor logic |
| Bluetooth remote | Send custom commands wirelessly | Serial protocols, debugging |
| Trick menu | Type y1, y2, y3 to run different tricks | Command routing, functions |
| Data logger | Print sensor values to Serial Monitor while testing | Debugging, data collection |
| Calibration lab | Students measure how far servo angles actually move | Variables, measurement, accuracy |
Project Levels
Level 1 projects need only Serial Monitor:
k sit
i 0 30
qk sit:1000>i 0 30:500>k up:0>Level 2 projects use custom C++ functions:
void happyDog() {
tQueue->addTask(T_SKILL, "sit", 500);
tQueue->addTask(T_INDEXED_SIMULTANEOUS_ASC, "0 35", 300);
tQueue->addTask(T_INDEXED_SIMULTANEOUS_ASC, "0 -35", 300);
tQueue->addTask(T_BEEP, "12 4 16 4 19 4", 300);
tQueue->addTask(T_SKILL, "up", 0);
}Level 3 projects use sensors:
if (analogRead(ANALOG2) > 3000 && tQueue->cleared()) {
happyDog();
}Level 4 projects use state machines:
enum DogMode {
MODE_IDLE,
MODE_HAPPY,
MODE_GUARD,
MODE_SLEEPY
};
DogMode dogMode = MODE_IDLE;
void updateDogMode() {
if (!tQueue->cleared()) return;
if (dogMode == MODE_HAPPY) {
tQueue->addTask(T_SKILL, "hi", 1000);
dogMode = MODE_IDLE;
} else if (dogMode == MODE_SLEEPY) {
tQueue->addTask(T_SKILL, "rest", 0);
}
}20. Extra Firmware Features Worth Knowing
These are already in the OpenCat firmware and are useful for more advanced lessons.
| Feature | Command or code | Why it is useful |
|---|---|---|
| Custom robot name | nLucasDog | Makes Bluetooth easier to find |
| Query info | ? | Checks board/model/version |
| Voltage check | P | Teaches battery safety and diagnostics |
| Gyro state | g? | Shows balance/update/print state |
| Gyro calibration | gc | Teaches calibration and sensor drift |
| Gyro balance off/on | gb, gB | Useful when the robot falsely thinks it is falling |
| Task queue | q... | Lets students make scripts without uploading |
| Parameterized walking | k wkF 5, k wkF 3000 | Teaches arguments and time/cycle control |
| Parameterized turning | k wkR 90, k wkL 90 | Teaches sensor-based motion goals |
| Custom serial prefix | y... | Lets you add your own command language |
| Extension modules | X?, XU, XG, XI | Turns built-in module demos on/off |
| Random behavior | z | Demonstrates autonomous behavior |
Custom Command Menu Pattern
This is a nice classroom pattern. Students can add new commands without touching the deep firmware.
void runMySerialCommands() {
if (token != 'y') return;
if (!strcmp(newCmd, "happy")) {
happyDog();
} else if (!strcmp(newCmd, "guard")) {
dogMode = MODE_GUARD;
} else if (!strcmp(newCmd, "sleep")) {
dogMode = MODE_SLEEPY;
} else if (!strcmp(newCmd, "r90")) {
turnRightDegrees(90);
} else {
printToAllPorts("Unknown y command");
}
token = '\0';
newCmd[0] = '\0';
cmdLen = 0;
newCmdIdx = 0;
}Then students type:
yhappy
yguard
ysleep
yr90Pose Array Pattern
This teaches arrays and loops.
const char *headDance[] = {
"0 30",
"0 -30",
"0 20",
"0 -20",
"0 0"
};
void playHeadDance() {
tQueue->addTask(T_SKILL, "sit", 500);
for (int i = 0; i < 5; i++) {
tQueue->addTask(T_INDEXED_SIMULTANEOUS_ASC, headDance[i], 300);
}
tQueue->addTask(T_SKILL, "up", 0);
}Cooldown Timer Pattern
This prevents a sensor from triggering the same trick hundreds of times.
unsigned long lastTrickTime = 0;
const unsigned long trickCooldown = 3000;
void reactWithCooldown() {
if (millis() - lastTrickTime < trickCooldown) return;
if (!tQueue->cleared()) return;
int reading = analogRead(ANALOG2);
if (reading > 3000) {
happyDog();
lastTrickTime = millis();
}
}21. Special Project Ideas
1. Robot Personality System
Make several mood functions:
void moodHappy();
void moodCurious();
void moodScared();
void moodSleepy();Then switch moods with serial commands or sensors.
Teaching value:
Students learn that software can model behavior, not just run commands.
2. Choreography Builder
Students create a dance by choosing:
- A posture.
- A head movement.
- A leg movement.
- A sound.
- A final pose.
Teaching value:
This teaches sequence, abstraction, and functions.
3. Robot Measurement Lab
Students test:
- How far does
yr90actually turn? - How does battery level affect movement?
- How much does carpet vs. hard floor matter?
- What angle range feels safe for each joint?
Teaching value:
This turns coding into experimental science.
4. Sensor Threshold Lab
Students print sensor readings:
printToAllPorts(analogRead(ANALOG2));Then choose a threshold:
if (analogRead(ANALOG2) > 3000) {
happyDog();
}Teaching value:
This teaches debugging, real-world noisy data, and decision-making.
5. Wireless Command Challenge
After Bluetooth is working, students control the robot from across the room.
Example commands:
yhappy
yr90
yl90
k sit
k upTeaching value:
This teaches that the same command system can work over USB or Bluetooth.
22. Best Beginner Habits
- Test one small motion at a time.
- Use
jbefore moving legs. - Move only 10 to 20 degrees at first.
- Use
tQueue->cleared()for sensor triggers. - Keep the robot on the floor for walking and big tricks.
- Keep a known-good copy of
OpenCatEsp32.ino. - Use Serial Monitor commands before writing C++.
- Upload only when changing firmware code.
23. Useful Links
- Petoi OpenCatESP32 GitHub: https://github.com/PetoiCamp/OpenCatEsp32
- Petoi BiBoard upload guide: https://docs.petoi.com/arduino-ide/upload-sketch-for-biboard
- Petoi robot joint index: https://docs.petoi.com/petoi-robot-joint-index
- Petoi BiBoard V1 guide: https://docs.petoi.com/biboard/biboard-v1-guide