Overview
Note: This page provides an overview and quickstart guide. For the complete auto-generated C++ API reference with detailed class documentation, see the Doxygen Reference.
The C++ firmware runs on the OpenRB-150 microcontroller and provides real-time motor control, serial communication, and gesture management for the NML Hand Exoskeleton.
Key Features:
- Dynamixel motor control - Position, velocity, current control
- Serial command protocol - Text-based commands over USB/UART
- Gesture library - Pre-defined hand poses and movements
- IMU integration - Orientation tracking (optional)
- OLED display - Status information (optional)
- Safety limits - Current limits, joint angle limits
Source Code: src/cpp/nml_hand_exo/
Architecture
Files
nml_hand_exo.h/cpp- Main device classgesture_controller.h/cpp- Gesture management and executiongesture_library.h/cpp- Pre-defined gesture databaseconfig.h- Pin definitions and configuration constantsutils.h/cpp- Helper functionsoled.h/cpp- OLED display driver (optional)
Dependencies
#include <Dynamixel2Arduino.h> // Dynamixel motor control
#include <Wire.h> // I2C for IMU/OLED
#include <Adafruit_Sensor.h> // Sensor abstraction (optional)
#include <Adafruit_BNO055.h> // IMU driver (optional)
Setup & Flashing
Prerequisites
- Arduino IDE (1.8.19 or 2.x)
- OpenRB-150 Board Support - Install via Board Manager
- Dynamixel2Arduino Library - Install via Library Manager
Board Configuration
Board: OpenRB-150
Upload Speed: 115200
Port: (Select your COM port)
Upload Firmware
- Clone the repository:
git clone https://github.com/Neuro-Mechatronics-Interfaces/NML_Hand_Exo.git cd NML_Hand_Exo/src/cpp/nml_hand_exo -
Open
nml_hand_exo.inoin Arduino IDE - Configure
config.hfor your hardware: ```cpp // Motor IDs (must match Dynamixel IDs programmed via Wizard) const uint8_t MOTOR_IDS[] = {1, 2, 3, 4, 5}; const uint8_t NUM_MOTORS = 5;
// Joint limits [min, max] in degrees const float JOINT_LIMITS[][2] = { {-10.0, 90.0}, // Thumb {-5.0, 85.0}, // Index {-5.0, 85.0}, // Middle {-5.0, 85.0}, // Ring {-5.0, 85.0} // Pinky };
// Serial communication #define COMMAND_SERIAL Serial // USB serial #define DXL_SERIAL Serial1 // Dynamixel serial #define BAUD_RATE 57600
4. Click **Upload** (Ctrl+U)
---
# NMLHandExo Class
The main class for controlling the exoskeleton.
## Constructor
```cpp
NMLHandExo(const uint8_t* ids, uint8_t numMotors,
const float jointLimits[][2],
const float* homeState = nullptr);
Parameters:
ids- Array of motor IDsnumMotors- Number of motorsjointLimits- 2D array of [min, max] angles in degreeshomeState- Optional home positions (defaults to zero)
Example:
const uint8_t MOTOR_IDS[] = {1, 2, 3, 4, 5};
const float JOINT_LIMITS[][2] = {
{-10.0, 90.0},
{-5.0, 85.0},
{-5.0, 85.0},
{-5.0, 85.0},
{-5.0, 85.0}
};
NMLHandExo exo(MOTOR_IDS, 5, JOINT_LIMITS);
Initialization
initializeSerial(int baud)
Initialize the Dynamixel serial port.
exo.initializeSerial(57600);
initializeMotors()
Configure all motors: disable torque, set position mode, re-enable torque.
exo.initializeMotors();
Motor Control
Position Control
// Get relative angle (degrees) - relative to home position
float angle = exo.getRelativeAngle(motorID);
// Set relative angle (degrees)
exo.setRelativeAngle(motorID, 45.0);
// Get absolute angle (degrees) - raw encoder value
float absAngle = exo.getAbsoluteAngle(motorID);
// Set absolute angle (degrees)
exo.setAbsoluteAngle(motorID, 2048.0);
Velocity Control
// Get current velocity (units depend on mode)
float vel = exo.getVelocity(motorID);
// Set velocity
exo.setVelocity(motorID, 100.0);
Torque/Current
// Enable/disable motor torque
exo.enableTorque(motorID);
exo.disableTorque(motorID);
// Check torque state
bool enabled = exo.isTorqueEnabled(motorID);
// Get current draw (mA)
float current = exo.getCurrent(motorID);
// Set current limit (mA)
exo.setCurrentLimit(motorID, 1000.0);
Homing & Calibration
// Set current position as zero offset for a motor
exo.setZeroOffset(motorID);
// Get stored zero offset
float offset = exo.getZeroOffset(motorID);
// Reset all motors to current positions
exo.resetAllZeros();
// Start calibration mode
exo.beginCalibration(enableTimed=true, duration=5);
// Check calibration status
if (exo.isExoCalibrating()) {
exo.updateCalibration();
}
Operating Modes
// Set exo operating mode
exo.setExoOperatingMode("GESTURE_FIXED");
// Options: "FREE", "GESTURE_FIXED", "GESTURE_CONTINUOUS", "GESTURE_CALIBRATION"
// Get current mode
String mode = exo.getExoOperatingMode();
// Cycle through modes
exo.cycleExoOperatingMode();
// Set motor control mode
exo.setMotorControlMode("position");
// Options: "position", "velocity", "current_position", "extended_position"
// Get motor control mode
String motorMode = exo.getMotorControlMode();
Gestures
GestureController Class
#include "gesture_controller.h"
// Create gesture controller
GestureController gestureCtrl(&exo);
// Set a gesture by name
gestureCtrl.setGesture("IndexPinch");
// Get current gesture
String currentGesture = gestureCtrl.getCurrentGesture();
// Set gesture state
gestureCtrl.setGestureState("open"); // or "close"
// Get gesture state
String state = gestureCtrl.getGestureState();
// Cycle through gestures
gestureCtrl.cycleGesture();
// Cycle gesture state
gestureCtrl.cycleGestureState();
// Get list of available gestures
String gestures = gestureCtrl.getGestureList();
Built-in Gestures
The gesture library includes:
- Fist - All fingers closed
- Open - All fingers extended
- IndexPinch - Thumb-index pinch
- MiddlePinch - Thumb-middle pinch
- RingPinch - Thumb-ring pinch
- PinkyPinch - Thumb-pinky pinch
- ThreeFingerPinch - Thumb-index-middle
- Point - Index extended, others closed
- Peace - Index and middle extended
Utility Functions
// Get motor ID by name
int id = exo.getMotorIDByName("THUMB");
// Get motor ID by index
uint8_t id = exo.getMotorIDByIndex(0);
// Get motor name by ID
String name = exo.getMotorNameByID(1);
// Get device info string
String info = exo.getDeviceInfo();
// Get motor count
int count = exo.getMotorCount();
// Update exo state (call in loop)
exo.update();
Serial Command Protocol
The firmware listens for semicolon-delimited commands on the serial port.
Command Format
command:arg1:arg2;
Rules:
- Commands are case-insensitive
- Arguments separated by
: - Commands end with
;(configurable) - Motor ID can be
allor a specific number
Common Commands
Motor Control
enable:all; # Enable all motors
disable:2; # Disable motor 2
set_angle:1:45.0; # Set motor 1 to 45 degrees
get_angle:1; # Get motor 1 angle
set_velocity:all:100; # Set all motors to velocity 100
Homing
home:all; # Home all motors
set_home:1; # Set motor 1 current position as home
get_home:3; # Get motor 3 home position
Limits
set_current_limit:1:1000; # Set motor 1 current limit to 1000mA
set_limits:2:-5.0:85.0; # Set motor 2 angle limits
get_limits:1; # Get motor 1 limits
Gestures
set_gesture:IndexPinch; # Set gesture to IndexPinch
get_gesture; # Get current gesture
set_gesture_state:open; # Set state to open
cycle_gesture; # Cycle to next gesture
get_gesture_list; # List all gestures
System
help; # List available commands
version; # Get firmware version
info; # Get device info
reboot; # Reboot controller
LEDs
led:1:on; # Turn on motor 1 LED
led:all:off; # Turn off all LEDs
Example Sketch
#include "nml_hand_exo.h"
#include "gesture_controller.h"
#include "config.h"
// Motor configuration
const uint8_t MOTOR_IDS[] = {1, 2, 3, 4, 5};
const uint8_t NUM_MOTORS = 5;
const float JOINT_LIMITS[][2] = {
{-10.0, 90.0}, // Thumb
{-5.0, 85.0}, // Index
{-5.0, 85.0}, // Middle
{-5.0, 85.0}, // Ring
{-5.0, 85.0} // Pinky
};
// Create exo instance
NMLHandExo exo(MOTOR_IDS, NUM_MOTORS, JOINT_LIMITS);
GestureController gestureCtrl(&exo);
void setup() {
Serial.begin(57600);
exo.initializeSerial(57600);
exo.initializeMotors();
Serial.println("NML Hand Exo Ready!");
Serial.println(exo.getDeviceInfo());
}
void loop() {
// Process serial commands
if (Serial.available()) {
String cmd = Serial.readStringUntil(';');
processCommand(cmd);
}
// Update exo state
exo.update();
}
void processCommand(String cmd) {
cmd.trim();
if (cmd.startsWith("enable:")) {
String arg = cmd.substring(7);
if (arg == "all") {
for (int i = 0; i < NUM_MOTORS; i++) {
exo.enableTorque(MOTOR_IDS[i]);
}
}
}
else if (cmd.startsWith("set_angle:")) {
// Parse: set_angle:1:45.0
int firstColon = cmd.indexOf(':');
int secondColon = cmd.indexOf(':', firstColon + 1);
int motorID = cmd.substring(firstColon + 1, secondColon).toInt();
float angle = cmd.substring(secondColon + 1).toFloat();
exo.setRelativeAngle(motorID, angle);
}
else if (cmd == "help") {
Serial.println("Available commands: enable, disable, set_angle, get_angle, home, info");
}
}
Advanced Topics
Custom Gestures
Define custom gestures in gesture_library.cpp:
void initializeGestures(GestureLibrary& library) {
// Add custom gesture
Gesture customGesture;
customGesture.name = "MyCustomGesture";
customGesture.numStates = 2;
// State 0: open
customGesture.states[0].angles[0] = 0.0; // Thumb
customGesture.states[0].angles[1] = 0.0; // Index
customGesture.states[0].angles[2] = 0.0; // Middle
// ... etc
// State 1: closed
customGesture.states[1].angles[0] = 45.0;
customGesture.states[1].angles[1] = 80.0;
// ... etc
library.addGesture(customGesture);
}
IMU Integration
#include <Adafruit_BNO055.h>
Adafruit_BNO055 bno = Adafruit_BNO055(55);
void setup() {
if (!bno.begin()) {
Serial.println("IMU initialization failed!");
}
}
void loop() {
sensors_event_t event;
bno.getEvent(&event);
float roll = event.orientation.x;
float pitch = event.orientation.y;
float yaw = event.orientation.z;
// Use IMU data for control...
}
Troubleshooting
Motor not responding:
- Check Dynamixel ID matches firmware configuration
- Verify baud rate (57600 default)
- Check power supply (12V, adequate current)
- Test motor individually with Dynamixel Wizard
Serial communication issues:
- Ensure correct COM port selected
- Try different baud rates (57600, 115200, 1000000)
- Check USB cable quality
- Use
help;command to verify connection
Gestures not executing:
- Verify motors are enabled:
enable:all; - Check joint limits in
config.h - Ensure gesture exists:
get_gesture_list;
Compilation errors:
- Install Dynamixel2Arduino library via Library Manager
- Update Arduino IDE to latest version
- Check board support package is installed