Petoi Bittle BiBoard Coding Cheat Sheet

1. Big Picture

There are three different ways to control the robot:

MethodDo you upload new firmware?Best for
Serial Monitor commandsNoTesting built-in skills, moving servos, quick demos
q task queue scriptsNoSending a short “program” like sit -> wait -> move head -> balance
C++ code in OpenCatEsp32.inoYesCustom 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

Use Arduino IDE 2.x.

Install the ESP32 board package:

  1. Open Arduino IDE.
  2. Go to File > Preferences.
  3. In Additional Boards Manager URLs, add:
https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json
  1. Go to Tools > Board > Boards Manager.
  2. Search for esp32.
  3. Install esp32 by Espressif Systems.
  4. 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:

SettingValue
BoardESP32 Dev Module
PortYour robot port, for example COM5
Upload Speed921600, or 115200 if upload is unreliable
CPU Frequency240MHz (WiFi/BT)
Flash Frequency80MHz
Flash ModeQIO
Flash Size4MB
Partition SchemeDefault 4MB with spiffs
Core Debug LevelNone
PSRAMDisabled

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:

LibraryWhen needed
ArduinoJson by Benoit BlanchonNeeded by parts of the downloaded OpenCatEsp32 source
WebSockets / arduinoWebSocketsOnly if WEB_SERVER is enabled
MuVisionSensor3Only if CAMERA is enabled

If you get a missing file like:

esp_partition.h: No such file or directory

then 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 directory

then 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_0

Only 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

FileWhat it is for
OpenCatEsp32\OpenCatEsp32.inoMain Arduino sketch. Put beginner custom code here.
OpenCatEsp32\src\OpenCat.hMain firmware settings, board definitions, token names, pin definitions, servo limits.
OpenCatEsp32\src\taskQueue.hDefines the task queue used by q and tQueue->addTask(...).
OpenCatEsp32\src\reaction.hHandles commands like k sit, i 0 30, gB, etc.
OpenCatEsp32\src\InstinctBittleESP.hBuilt-in skills like sit, up, wkF, hi, etc.
OpenCatEsp32\src\moduleManager.hHandles sensors/modules and the X commands.

For beginner projects, spend most of your time in:

OpenCatEsp32\OpenCatEsp32.ino

4. 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:

  1. Write your trick as a function.
  2. Upload it once.
  3. Trigger it from Serial Monitor with your own command.

5. Uploading Code To The Robot

  1. Open OpenCatEsp32\OpenCatEsp32.ino in Arduino IDE.
  2. Select Tools > Board > ESP32 Dev Module.
  3. Select the robot port, for example Tools > Port > COM5.
  4. Click Verify first.
  5. Click Upload.

If upload fails with:

Wrong boot mode detected

try this:

  1. Click Upload.
  2. When Arduino IDE says Connecting..., hold the BOOT button on the BiBoard.
  3. Release BOOT once it starts writing.

If upload fails with serial corruption:

  1. Set Tools > Upload Speed > 115200.
  2. Use a direct USB cable, not a hub.
  3. Turn the battery switch off during upload.
  4. 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:

  1. Select the robot port, for example COM5.
  2. Set baud rate to 115200.
  3. Use No line ending for 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
ksit

Both usually work.

Common Commands

CommandMeaningExample
?Query robot info?
k <skill>Run a built-in skillk sit
k <gait> <number>Run a gait with an argumentk wkF 5
jPrint all current joint anglesj
j <index>Print one joint anglej 8
i <index angle ...>Move multiple joints at the same timei 0 30 8 60
m <index angle ...>Move joints one after anotherm 0 30 0 -30 0 0
dRest/shut off servosd
d <index>Shut off one servod 8
q...Queue a multi-step mini-programqk sit:1000>k balance:0>
b ...Beep notesb 12 8 14 8 16 4
gToggle gyro/balance behaviorg
gbTurn gyro balance offgb
gBTurn gyro balance ongB
gcCalibrate IMU/gyrogc
g?Print gyro stateg?
pPause/resume motionp
zToggle random/auto behaviorsz
.Accelerate gait speed.
,Decelerate gait speed,

Skill Commands

Basic posture examples:

k sit
k up
k balance
k rest

Walking examples:

k wkF
k wkL
k wkR
k bk

Parameterized examples:

k wkF 5
k wkF 3000
k wkL 90
k wkR 90

For straight gaits:

NumberMeaning
Less than 200Number of gait cycles
200 or moreTime 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:

PartMeaning
qStart a queued script
k sitThe command to run
:1000Wait 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 wkF

Most Useful Beginner Skills

SkillUse
sitSit posture
upStand up
balanceBalanced standing posture
restRest posture
strStretch
zeroZero posture
calibCalibration posture
buttUpRear-up posture
wkFWalk forward
wkLTurn/walk left
wkRTurn/walk right
bkBack up
trFTrot forward
crFCrawl forward
vtFStepping/trot style forward
hiWave/hi
hskHandshake
snfSniff
peeFunny demo behavior
puPush-up style behavior
pdPlay dead
rcRecover

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, zz

Note:

Some right-side commands like wkR, trR, crR, and vtR are mirrored internally from stored left-side skills.

Random skill:

k x

10. 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 indexBody partBeginner notes
0Head panSafe for beginner head turning
1Tail / optional add-on / arm-related jointAvoid unless you know your hardware uses it
2Optional add-on / arm-related jointAvoid on normal Bittle
3Optional add-on / arm-related jointAvoid on normal Bittle
4 to 7Not used for normal 8-DOF Bittle walking setupAvoid
8Left front shoulderLeg joint
9Right front shoulderLeg joint
10Right back shoulderLeg joint
11Left back shoulderLeg joint
12Left front kneeLeg joint
13Right front kneeLeg joint
14Right back kneeLeg joint
15Left back kneeLeg joint

The leg order in the firmware is:

LF, RF, RB, LB
Left Front, Right Front, Right Back, Left Back

Angle Limits From Your Firmware

These are software limits from OpenCat.h.

Joint indexMin angleMax angle
0-120120
1-8585
2-120120
3-120120
4-9060
5-9060
6-9090
7-9090
8-20080
9-20080
10-80200
11-80200
12-80200
13-80200
14-80200
15-80200

Beginner safety rule:

  1. Type j to read the current angles.
  2. Move only 10 to 20 degrees away from the current angle at first.
  3. Test on the floor, not near the edge of a table.
  4. If the robot gets weird, type d.

Example:

j
i 0 20
i 0 -20
i 0 0
d

11. 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:

ParameterTypeMeaning
tokenchar or token constantWhat kind of command this is
commandstringThe command data
delayAfterMsintegerHow long to wait before the next queued task

Common token constants:

Token constantSame asMeaning
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:

ParameterMeaning
jointIndexServo/joint number, like 0, 8, 12
angleTarget angle in degrees
speedRatioOptional 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:

ParameterMeaning
targetFrameArray of target angles
angleDataRatioMultiplies angle data, usually 1 for your code
speedRatioHigher means fewer interpolation steps
offsetStarting joint index, usually 0
periodUsed for gait data, usually 0 for simple target frames
runDelayDelay 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:

ParameterMeaning
notePitch step
durationHow long the note lasts
pausePause after the note
repeatNumber 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

FunctionMeaningExample
pinMode(pin, mode)Set a pin as input/outputpinMode(ANALOG2, INPUT);
analogRead(pin)Read analog value, usually 0 to 4095 on ESP32analogRead(ANALOG2)
digitalRead(pin)Read HIGH or LOWdigitalRead(BACKTOUCH_PIN)
digitalWrite(pin, value)Set an output HIGH or LOWdigitalWrite(2, HIGH)
millis()Milliseconds since startupif (millis() - lastTime > 1000)
delay(ms)Pause everythingdelay(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:

ydemo

and 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:

ydemo

You 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 -40

Meaning:

PairMeaning
0 30Move joint 0 to 30 deg
8 60Move joint 8 to 60 deg
12 -40Move 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:

NameESP32 pinNotes
ANALOG134Analog input
ANALOG235Analog input
ANALOG336Analog input
ANALOG439Analog input
BACKTOUCH_PIN38Back touch input
UART_RX29Grove UART2 receive
UART_TX210Grove UART2 transmit
VOICE_RX26Voice module serial receive
VOICE_TX25Voice module serial transmit

General sensor rules:

  1. Use sensors that support 3.3V logic.
  2. Connect sensor GND to robot GND.
  3. Use Grove/I2C/Qwiic style sensors when possible.
  4. Do not connect random 5V signal wires directly to ESP32 pins.
  5. 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

SensorPort styleProject idea
Time-of-flight distance sensorI2CDog backs up when your hand gets too close
Gesture sensorI2CWave hand left/right to pick tricks
Sound sensorAnalogClap once to sit, clap twice to stand
Touch stripI2C or analogPet the robot’s back to trigger happy motions
Color sensorI2CColor cards choose different behaviors
Line finderDigital/analogFollow a line on the floor

16. X Extension / Module Commands

The X token turns module demos on and off.

CommandMeaning
X?Show active module status
XDisable most active modules
XTEnable double touch module
XtDisable double touch module
XLEnable double light module
XlDisable double light module
XDEnable double IR distance module
XdDisable double IR distance module
XIEnable PIR module
XiDisable PIR module
XUEnable ultrasonic module
XuDisable ultrasonic module
XGEnable gesture module
XgDisable gesture module
XQEnable quick demo
XqDisable 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

  1. Check USB cable.
  2. Select the correct port in Arduino IDE.
  3. Close any other program using the port.
  4. Reopen Serial Monitor at 115200.

Robot Uploaded But Does Not Move

  1. The board may be powered by USB but servos need battery power.
  2. Turn the robot battery on after upload.
  3. Type k up or k sit.
  4. Check that the robot is not in d rest state.

Robot Thinks It Is Falling

Try:

d
gb
gc

Then put the robot flat and still while it calibrates.

Use gB to turn gyro balance back on.

Upload Fails

Common fixes:

  1. Close Serial Monitor.
  2. Use Upload Speed 115200.
  3. Hold BOOT during Connecting....
  4. Use a direct USB cable.
  5. 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:

  1. Serial Monitor: type k sit, k up, i 0 30, j.
  2. Variables: change angles and wait times.
  3. Functions: create myHeadDemo().
  4. Queues: use q and tQueue->addTask(...).
  5. If-statements: react to a sensor threshold.
  6. Timing: replace long delay(...) with millis().
  7. Arrays: store multiple servo poses.
  8. 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.

ProjectWhat the robot doesProgramming concepts
Emotion dogHas moods like happy, sleepy, curious, scaredFunctions, variables, state
Simon SaysStudent sends commands and robot must follow only if “Simon says”If-statements, strings, command parsing
Dance composerStudents build a dance from a list of posesArrays, loops, sequencing
Guard dogReacts to motion, touch, sound, or distanceSensors, thresholds, events
Follow my handMoves based on distance sensor readingsSensor input, feedback, control loops
Red light green lightWalks on green, freezes/sits on redState machines, rules
Robot petting gameTouch sensor changes mood or behaviorInput events, cooldown timers
Obstacle avoiderWalks until object is close, then turnsIf-statements, sensor logic
Bluetooth remoteSend custom commands wirelesslySerial protocols, debugging
Trick menuType y1, y2, y3 to run different tricksCommand routing, functions
Data loggerPrint sensor values to Serial Monitor while testingDebugging, data collection
Calibration labStudents measure how far servo angles actually moveVariables, 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.

FeatureCommand or codeWhy it is useful
Custom robot namenLucasDogMakes Bluetooth easier to find
Query info?Checks board/model/version
Voltage checkPTeaches battery safety and diagnostics
Gyro stateg?Shows balance/update/print state
Gyro calibrationgcTeaches calibration and sensor drift
Gyro balance off/ongb, gBUseful when the robot falsely thinks it is falling
Task queueq...Lets students make scripts without uploading
Parameterized walkingk wkF 5, k wkF 3000Teaches arguments and time/cycle control
Parameterized turningk wkR 90, k wkL 90Teaches sensor-based motion goals
Custom serial prefixy...Lets you add your own command language
Extension modulesX?, XU, XG, XITurns built-in module demos on/off
Random behaviorzDemonstrates 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
yr90

Pose 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:

  1. A posture.
  2. A head movement.
  3. A leg movement.
  4. A sound.
  5. A final pose.

Teaching value:

This teaches sequence, abstraction, and functions.

3. Robot Measurement Lab

Students test:

  1. How far does yr90 actually turn?
  2. How does battery level affect movement?
  3. How much does carpet vs. hard floor matter?
  4. 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 up

Teaching value:

This teaches that the same command system can work over USB or Bluetooth.

22. Best Beginner Habits

  1. Test one small motion at a time.
  2. Use j before moving legs.
  3. Move only 10 to 20 degrees at first.
  4. Use tQueue->cleared() for sensor triggers.
  5. Keep the robot on the floor for walking and big tricks.
  6. Keep a known-good copy of OpenCatEsp32.ino.
  7. Use Serial Monitor commands before writing C++.
  8. Upload only when changing firmware code.
  • 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