Cheap hobby servos such as MG90 and MG996 can only do so much. You can tell them to go to an angle…and that’s about it. At some point you’ll ask yourself “what position is the servo at now?”. That’s one of the many shortcomings that smart servos solves. They’re much pricier, but they unlock a whole new dimension of projects that you can build. This article will focus on the smart serial bus servos manufactured by Feetech, in particular the ST3215 model.
I’ll explain why these are far superior to cheaper servos and how to use some of the features that are common points of confusion.
Serial BusAs suggested by the name, these servos communicate over the serial port and you can daisy chain them as they all work on a single bus. Instead of connecting multiple servos to a control board, you only need to connect one to the control board, then connect the next servo to that first servo, and so on. This massively reduces the amount of wires that you need to manage.
The STS3215 servos can do much more beyond simply rotating to a position. Its capabilities include:
- Position, speed, and torque feedback
- Current, voltage, and temperature feedback
- Continuous rotation mode and fixed position mode
- Assigning IDs to the servos
There are many other features that are documented in their SDK.
HardwareI would recommend using this all-in-one board to get started quickly. It combines an ESP32 (bluetooth + wifi), servo drivers, connectors, and mini display in one small form-factor board.
With this board, we can start programming!
ToolsDespite being loaded with functionality, the SDK is quite unpolished. We will need to make some modifications to make it more user-friendly.
The manufacturer provides a web user interface and a desktop interface for setting up and performing basic functions on the servos. However, neither works very well; most of the buttons on the web interface don’t even work. It is more productive to just use Python to perform those functions, and it’s a good learning experience as well.
FirmwareWifi Connection
By default, the ESP32 is set up as an access point to broadcast its own network for you to connect to and access the web interface. You can also set it up as a client that connects to your network, though it’s limited to the 2.4GHz band.
If you control the ESP32 over wifi, the ESP32 sends the commands to the servo serial interface.
Enter your network credentials in the block in ServoDriver.ino that looks like this:
// ServoDriver.ino
// WIFI_STA settings
const char* STA_SSID = "Your_Network_SSID";
const char* STA_PWD = "Your_Network_Password";Look for the toggle for the network connection mode and set it to 2 (so it acts as a client instead of an access point).
// ServoDriver.ino
// set the default wifi mode here.
// 1 as [AP] mode, it will not connect other wifi.
// 2 as [STA] mode, it will connect to known wifi.
#define DEFAULT_WIFI_MODE 2Serial Connection
This board has two serial interfaces. One goes to the servo bus, and the other is used for external connections. By default, these two interfaces can’t talk to each other. If you want to use Python to control the servos over the serial interface, you’ll find that you can’t see the servos if serial forwarding is off.
Serial forwarding must be turned on to allow the two serial interfaces to talk to each other.
This setting is toggled in ServoDriver.ino.
// set the SERIAL_FORWARDING as true to control the servos with USB.
bool SERIAL_FORWARDING = true;The servos are set up to use a baud rate of 921600. Ensure that ServoDriver.ino obeys this baud rate.
// Look for this line in void setup()
Serial.begin(921600);Servo Type
The SDK supports both the SC- and ST- series servos. Comment out (or delete) the type that you’re not using. Since we’re using ST, we’ll comment out the SC section as shown below.
include <SCServo.h>
#include <math.h>
// === SC Servo ===
// #define CTRL_SC_SERVO
// SCSCL st;
// float ServoDigitalRange = 1023.0;
// float ServoAngleRange = 210.0;
// float ServoDigitalMiddle= 511.0;
// #define ServoInitACC 0
// #define ServoMaxSpeed 1500
// #define MaxSpeed_X 1500
// #define ServoInitSpeed 1500
// int SERVO_TYPE_SELECT = 2;
int MAX_MIN_OFFSET = 30;
#define SMS_STS_ID SCSCL_ID
// === ST Servo ===
#define CTRL_ST_SERVO
SMS_STS st;
float ServoDigitalRange = 4095.0;
float ServoAngleRange = 360.0;
float ServoDigitalMiddle= 2047.0;
#define ServoInitACC 100
#define ServoMaxSpeed 4000
#define MaxSpeed_X 4000
#define ServoInitSpeed 2000
int SERVO_TYPE_SELECT = 1;Now you’re ready to flash the firmware onto the ESP32! Connect your computer to the board using a USB cable and upload ServoDriver.ino.
The SDK uses a binary protocol! We just need to know the memory addresses for the attributes that we’re setting, and the size of the data. Lucky for us, there’s a memory table in sms_sts.py for us to reference, and that’s the exact file that we’ll be using for the setup.
The binary protocol works by setting up the message constants identically on both the Python side and the firmware side. To send messages, we use the appropriate “write” function based on the number of bytes we want to send/receive. We can even write our own convenience methods (functions) that replace the need for the clunky web interface.
Set Servo IDFor example, if we want to set the servo’s ID, we use the write2ByteTxRx() function because the ID is a 2-byte short integer. We need to unlock the Eprom to allow writing, then lock it again when we’re done.
# Add this function in sms_sts.py
def SetID(self, current_id, new_id):
self.unLockEprom(current_id)
self.write2ByteTxRx(current_id, SMS_STS_ID, new_id)
self.LockEprom(new_id)
return TrueEnable/Disable Holding TorqueIf we want the servo to be able to hold its position, we can write a holding torque method with a 1-byte payload.
def writeEnable(self, id, enable):
'''
Sends an enable/disable command.
Pass enable=0 for disable and enable=1 for enable.
'''
return self.write1ByteTxRx(id, SMS_STS_TORQUE_ENABLE, enable)Python ControlTo test the built-in methods and the custom methods that we wrote, all we need is a simple Python script that calls them.
To begin, we can look for any connected servos.
from scservo_sdk import * # Uses SC Servo SDK library
# Create serial connection. Change the device to your device name.
connection = PortHandler('/dev/ttyUSB0')
# Initialize the sms_sts library
device = sms_sts(connection)
# Look for servos with IDs up to 60 (you can increase this if you want)
for id in range(60):
sts_model_number, sts_comm_result, sts_error = device.ping(id)
if sts_comm_result == 0:
br = device.readBaudrate(id)
print(f'Found active servo. ID: {id}. Baudrate is {br}')If you want to change any IDs, you can use this script:
from scservo_sdk import * # Uses SC Servo SDK library
# Create serial connection. Change the device to your device name.
connection = PortHandler('/dev/ttyUSB0')
# Initialize the sms_sts library
device = sms_sts(connection)
# Set this to whatever you want
new_id = 50
# Call the custom SetID() method that we wrote earlier
device.SetID(id, new_id)ClosingThese smart serial bus servos are more expensive than traditional hobby servos, but they can do so much more. I only went through some basic setup that I found troublesome to figure out, but I would encourage you to look through the SDK code yourself to see what else you can do (and how to actually move the servos).
I hope this tutorial closed some documentation gaps and saved you some headache!





Comments