Building an STM32 Smart Desktop Robotic Dog from Scratch

Introduction:

In early February or March, the author replicated a simple desktop pet - the STM32 desktop robotic dog based on online tutorials. Although it is a robotic dog, the author has finally found some free time to open source the relevant code and design. Interested readers can give it a try.

Main Content:

Hardware Design Section

The author redesigned a stable performance, voice recognition and output, OLED human-computer interaction, rechargeable four-legged desktop robot based on the tutorial, using Jialichuang EDA to design the schematic and corresponding PCB files. The voice module, OLED display module, SG90 servo module, and Bluetooth module are integrated onto a single circuit board, using asynchronous full-duplex serial communication for data exchange with the Bluetooth module, and IIC communication with the 0.96-inch OLED display. A lithium battery provides a stable power supply for the microcontroller and other modules. The designed schematic is shown below.

Building an STM32 Smart Desktop Robotic Dog from Scratch

The overall PCB of the system is shown below. To ensure the stability of signal transmission and improve the system’s anti-interference capability, as well as to reduce signal transmission distortion, the author used ground plane processing for all signal traces. When the area of the ground wire is small, the impedance of the ground wire increases, which can easily lead to a high voltage difference in the power noise loop, thereby affecting other signals in the circuit. Therefore, this design adopts a double-sided copper layout scheme to further increase the area of the entire GND, allowing for a more uniform distribution of current on the ground wire, which helps reduce noise and interference propagation. Each copper area separated by signal lines is also equipped with several vias to enhance its current-carrying capacity. Additionally, to minimize the correlation interference between signals that could cause distortion during channel transmission, the system places the IIC communication OLED display at the edge, while the Bluetooth module with the useful antenna is placed below the board. To avoid shielding effects on the antenna signal from the copper layer below the antenna operating in the high-frequency band, no copper is laid under the module.

Building an STM32 Smart Desktop Robotic Dog from Scratch

Program Design Section

The overall program of the system is not difficult. Essentially, it involves initializing each module one by one after the system starts, ensuring that each component works properly. Next, the OLED display will show the current expression of the robot, intuitively reflecting the robot’s emotions or status. Subsequently, the voice module begins to recognize the user’s voice commands, parsing the user’s instructions and needs. Based on the results of voice recognition, the robot will perform corresponding actions according to the instructions, thus completing interaction with the user. The user inputs voice signals through a microphone, and the SU-03T1 module converts the voice signals into electrical signals and processes them digitally. Next, the module uses its built-in voice recognition algorithm to analyze the digitized voice signals, extract key features, and compare them with voice templates in the database. The recognition process includes multiple stages such as voice signal preprocessing, feature extraction, and pattern matching. Then, the module converts the comparison results into text information and prints it to the STM32F103C8T6 microcontroller via serial port.

This is the action part of the program.

#include "stm32f10x.h"                  // Device header
#include "Servo.h"
#include "Delay.h"
#include "BlueTooth.h"

/* Servo positions      1        2            3        4  */    // Servos act as legs, values in parentheses indicate the direction of leg movement, 0 means forward, 90 degrees means standing
#define Chongfunumber 2  // Number of repetitions for actions
uint16_t PAnumbers = Chongfunumber; // Number of repetitions for actions
uint16_t TiaoTurn = 0;
uint16_t TiaoTurn2 = 0;

void Action_relaxed_getdowm(void) {
    Servo_Angle1(20);
    Servo_Angle2(20);
    Delay_ms(80);
    Servo_Angle3(160);
    Servo_Angle4(160);
}

void Action_upright(void) // Stand
{
    Servo_Angle1(90);
    Servo_Angle2(90);
    Delay_ms(80);
    Servo_Angle3(90);
    Servo_Angle4(90);
    if (WeiBa == 1) {
        Action_Mode = 9;
    }
}

void Action_upright2(void) // Stand
{
    Servo_Angle3(90);
    Servo_Angle4(90);
    Delay_ms(80);
    Servo_Angle1(90);
    Servo_Angle2(90);
    if (WeiBa == 1) {
        Action_Mode = 9;
    }
}

void Action_getdowm(void) // Lie down
{
    Servo_Angle1(20);
    Servo_Angle2(20);
    Delay_ms(80);
    Servo_Angle3(20);
    Servo_Angle4(20);
    if (WeiBa == 1) {
        Action_Mode = 9;
    }
}

void Action_sit(void) // Sit down
{
    Servo_Angle1(90);
    Servo_Angle2(90);
    Delay_ms(80);
    Servo_Angle3(20);
    Servo_Angle4(20);
    if (WeiBa == 1) {
        Action_Mode = 9;
    }
}

// Author is Sngels_wyh, available only on Douyin and Bilibili
void Action_advance(void) // Move forward
{
    while (Action_Mode == 4) {
        PAnumbers = Chongfunumber;
        while ((PAnumbers || Sustainedmove) && Action_Mode == 4) {
            Servo_Angle2(45);
            Servo_Angle3(45);
            Delay_ms(SpeedDelay);
            if (Action_Mode != 4) break;
            Servo_Angle1(135);
            Servo_Angle4(135);
            Delay_ms(SpeedDelay);
            if (Action_Mode != 4) break;
            Servo_Angle2(90);
            Servo_Angle3(90);
            Delay_ms(SpeedDelay);
            if (Action_Mode != 4) break;
            Servo_Angle1(90);
            Servo_Angle4(90);
            Delay_ms(SpeedDelay);
            if (Action_Mode != 4) break;
            Servo_Angle1(45);
            Servo_Angle4(45);
            Delay_ms(SpeedDelay);
            if (Action_Mode != 4) break;
            Servo_Angle2(135);
            Servo_Angle3(135);
            Delay_ms(SpeedDelay);
            if (Action_Mode != 4) break;
            Servo_Angle1(90);
            Servo_Angle4(90);
            Delay_ms(SpeedDelay);
            if (Action_Mode != 4) break;
            Servo_Angle2(90);
            Servo_Angle3(90);
            Delay_ms(SpeedDelay);
            if (Action_Mode != 4) break;
            PAnumbers--;
        }
        if (Sustainedmove != 1 && Action_Mode == 4)
            Action_Mode = 2;
    }
}

void Action_back(void) // Move backward
{
    while (Action_Mode == 5) {
        PAnumbers = Chongfunumber;
        while ((PAnumbers || Sustainedmove) && Action_Mode == 5) {
            Servo_Angle2(135);
            Servo_Angle3(135);
            Delay_ms(SpeedDelay);
            if (Action_Mode != 5) break;
            Servo_Angle1(45);
            Servo_Angle4(45);
            Delay_ms(SpeedDelay);
            if (Action_Mode != 5) break;
            Servo_Angle2(90);
            Servo_Angle3(90);
            Delay_ms(SpeedDelay);
            if (Action_Mode != 5) break;
            Servo_Angle1(90);
            Servo_Angle4(90);
            Delay_ms(SpeedDelay);
            if (Action_Mode != 5) break;
            Servo_Angle1(135);
            Servo_Angle4(135);
            Delay_ms(SpeedDelay);
            if (Action_Mode != 5) break;
            Servo_Angle2(45);
            Servo_Angle3(45);
            Delay_ms(SpeedDelay);
            if (Action_Mode != 5) break;
            Servo_Angle1(90);
            Servo_Angle4(90);
            Delay_ms(SpeedDelay);
            if (Action_Mode != 5) break;
            Servo_Angle2(90);
            Servo_Angle3(90);
            Delay_ms(SpeedDelay);
            if (Action_Mode != 5) break;
            PAnumbers--;
        }
        if (Sustainedmove != 1 && Action_Mode == 5)
            Action_Mode = 2;
    }
}

void Action_Lrotation(void) // Rotate left
{
    while (Action_Mode == 6) {
        PAnumbers = Chongfunumber;
        PAnumbers = PAnumbers + Chongfunumber;
        while ((PAnumbers || Sustainedmove) && Action_Mode == 6) {
            Servo_Angle2(45);
            Servo_Angle3(135);
            Delay_ms(SpeedDelay);
            if (Action_Mode != 6) break;
            Servo_Angle1(45);
            Servo_Angle4(135);
            Delay_ms(SpeedDelay);
            if (Action_Mode != 6) break;
            Servo_Angle2(90);
            Servo_Angle3(90);
            Delay_ms(SpeedDelay);
            if (Action_Mode != 6) break;
            Servo_Angle1(90);
            Servo_Angle4(90);
            Delay_ms(SpeedDelay);
            if (Action_Mode != 6) break;
            PAnumbers--;
        }
        if (Sustainedmove != 1 && Action_Mode == 6)
            Action_Mode = 2;
    }
}

void Action_Rrotation(void) // Rotate right
{
    while (Action_Mode == 7) {
        PAnumbers = Chongfunumber;
        PAnumbers = PAnumbers + Chongfunumber;
        while ((PAnumbers || Sustainedmove) && Action_Mode == 7) {
            Servo_Angle1(45);
            Servo_Angle4(135);
            Delay_ms(SpeedDelay);
            if (Action_Mode != 7) break;
            Servo_Angle2(45);
            Servo_Angle3(135);
            Delay_ms(SpeedDelay);
            if (Action_Mode != 7) break;
            Servo_Angle1(90);
            Servo_Angle4(90);
            Delay_ms(SpeedDelay);
            if (Action_Mode != 7) break;
            Servo_Angle2(90);
            Servo_Angle3(90);
            Delay_ms(SpeedDelay);
            if (Action_Mode != 7) break;
            PAnumbers--;
        }
        if (Sustainedmove != 1 && Action_Mode == 7)
            Action_Mode = 2;
    }
}

void Action_Swing(void) // Swing
{
    while (Action_Mode == 8) {
        for (uint8_t i = 30; i < 150; i++) {
            Servo_Angle1(i);
            Servo_Angle2(i);
            Servo_Angle3(i);
            Servo_Angle4(i);
            Delay_ms(SwingDelay);
            if (Action_Mode != 8) break;
        }
        for (uint8_t i = 150; i > 30; i--) {
            Servo_Angle1(i);
            Servo_Angle2(i);
            Servo_Angle3(i);
            Servo_Angle4(i);
            Delay_ms(SwingDelay);
            if (Action_Mode != 8) break;
        }
        if (Action_Mode != 8) break;
    }
}

void Action_SwingTail(void) // Swing tail
{
    Delay_ms(60);
    while (Action_Mode == 9) {
        for (uint8_t i = 30; i < 150; i++) {
            WServo_Angle(i);
            Delay_ms(SwingDelay);
            if (Action_Mode != 9) break;
        }
        for (uint8_t i = 150; i > 30; i--) {
            WServo_Angle(i);
            Delay_ms(SwingDelay);
            if (Action_Mode != 9) break;
        }
        if (Action_Mode != 9) break;
    }
    Delay_ms(100);
}

void Action_JumpU(void) // Jump forward
{
    if (TiaoTurn == 0) {
        Servo_Angle1(140);
        Servo_Angle4(35);
        Delay_ms(SpeedDelay);
        Servo_Angle2(140);
        Servo_Angle3(35);
        Delay_ms(SpeedDelay + 80);
        Action_Mode = 2;
        TiaoTurn = 1;
    } else {
        Servo_Angle2(140);
        Servo_Angle3(35);
        Delay_ms(SpeedDelay);
        Servo_Angle1(140);
        Servo_Angle4(35);
        Delay_ms(SpeedDelay + 80);
        Action_Mode = 2;
        TiaoTurn = 0;
    }
}

void Action_JumpD(void) // Jump backward
{
    if (TiaoTurn2 == 0) {
        Servo_Angle4(35);
        Servo_Angle1(140);
        Delay_ms(SpeedDelay);
        Servo_Angle3(35);
        Servo_Angle2(140);
        Delay_ms(SpeedDelay);
        Action_Mode = 12;
        TiaoTurn2 = 1;
    } else {
        Servo_Angle3(35);
        Servo_Angle2(140);
        Delay_ms(SpeedDelay);
        Servo_Angle4(35);
        Servo_Angle1(140);
        Delay_ms(SpeedDelay);
        Action_Mode = 12;
        TiaoTurn2 = 0;
    }
}

void Action_Hello(void) {
    Servo_Angle3(20);
    Servo_Angle4(45);
    Delay_ms(80);
    Servo_Angle1(90);
    while (Action_Mode == 13) {
        if (Action_Mode != 13) break;
        for (int i = 0; i <= 45; i++) {
            if (Action_Mode != 13) break;
            Servo_Angle2(i);
            Delay_ms(SwingDelay);
        }
        for (int i = 45; i > 0; i--) {
            if (Action_Mode != 13) break;
            Servo_Angle2(i);
            Delay_ms(SwingDelay);
        }
        if (Action_Mode != 13) break;
    }
}

void Action_stretch(void) // Stretch
{
    Servo_Angle3(90);
    Servo_Angle4(90);
    Delay_ms(80);
    for (int i = 90; i > 10; i--) {
        Servo_Angle1(i);
        Servo_Angle2(i);
        if (Action_Mode != 14) break;
        Delay_ms(15);
    }
    for (int i = 10; i < 90; i++) {
        Servo_Angle1(i);
        Servo_Angle2(i);
        if (Action_Mode != 14) break;
        Delay_ms(15);
    }
    for (int i = 90; i < 170; i++) {
        Servo_Angle3(i);
        Servo_Angle4(i);
        if (Action_Mode != 14) break;
        Delay_ms(15);
    }
    for (int i = 170; i > 90; i--) {
        Servo_Angle3(i);
        Servo_Angle4(i);
        if (Action_Mode != 14) break;
        Delay_ms(15);
    }
    if (Action_Mode == 14) Action_Mode = 15;
}

void Action_Lstretch(void) // Rear leg stretch
{
    int breakvalue = 1;
    int temp = 3;
    while (breakvalue) {
        Servo_Angle1(90);
        Servo_Angle2(20);
        Delay_ms(60);
        Servo_Angle4(110);
        for (int i = 90; i < 180; i++) {
            if (Action_Mode != 15) break;
            Servo_Angle3(i);
            Delay_ms(6);
        }
        while (temp && Action_Mode == 15) {
            for (int i = 180; i > 150; i--) {
                if (Action_Mode != 15) break;
                Servo_Angle3(i);
                Delay_ms(15);
            }
            temp--;
        }
        if (Action_Mode != 15) break;
        Delay_ms(100);
        Servo_Angle1(90);
        Servo_Angle2(90);
        if (Action_Mode != 15) break;
        Delay_ms(80);
        Servo_Angle3(90);
        Servo_Angle4(90);
        Delay_ms(100);
        if (Action_Mode != 15) break;
        temp = 3;
        Servo_Angle2(90);
        Servo_Angle1(20);
        if (Action_Mode != 15) break;
        Delay_ms(60);
        Servo_Angle3(110);
        for (int i = 90; i < 180; i++) {
            if (Action_Mode != 15) break;
            Servo_Angle4(i);
            Delay_ms(6);
        }
        while (temp && Action_Mode == 15) {
            for (int i = 180; i > 150; i--) {
                if (Action_Mode != 15) break;
                Servo_Angle4(i);
                Delay_ms(15);
            }
            temp--;
        }
        if (Action_Mode == 15) Action_Mode = 2;
        breakvalue = 0;
    }
}

This is the expression management part of the code.

#include "stm32f10x.h"      
#include "OLED.h"
#include "BlueTooth.h"

// Implement expression changes, adjust after entering interrupt
void Face_Config(void) {
    if (Face_Mode == 0) {
        OLED_Clear();
        OLED_ShowImage(0, 0, 128, 64, Face_sleep); // Sleeping
        OLED_Update();
    } else if (Face_Mode == 1) {
        OLED_Clear();
        OLED_ShowImage(0, 0, 128, 64, Face_stare); // Staring
        OLED_Update();
    } else if (Face_Mode == 2) {
        OLED_Clear();
        OLED_ShowImage(0, 0, 128, 64, Face_happy); // Happy
        OLED_Update();
    } else if (Face_Mode == 3) {
        OLED_Clear();
        OLED_ShowImage(0, 0, 128, 64, Face_mania); // Excited
        OLED_Update();
    } else if (Face_Mode == 4) {
        OLED_Clear();
        OLED_ShowImage(0, 0, 128, 64, Face_very_happy); // Very happy
        OLED_Update();
    } else if (Face_Mode == 5) {
        OLED_Clear();
        OLED_ShowImage(0, 0, 128, 64, Face_eyes); // Eyes
        OLED_Update();
    } else if (Face_Mode == 6) {
        OLED_Clear();
        OLED_ShowImage(0, 0, 128, 64, Face_hello); // Greeting
        OLED_Update();
    }
}

This is the Bluetooth connection part of the code.

#include "stm32f10x.h"                  // Device header
#include "PWM.h"
#include "PetAction.h"
#include "Face_Config.h"

uint16_t AllLed = 1;  // Turn on lights
uint16_t BreatheLed = 0; // Turn on breathing light
uint16_t Sustainedmove = 0; // Continuous movement

uint16_t Action_Mode = 0;
uint16_t SpeedDelay = 200;
uint16_t SwingDelay = 6;
uint16_t Face_Mode = 0;
uint8_t WeiBa = 0;

void BlueTooth_Init(void) {
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); // Enable GPIOA clock
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); // Enable USART clock
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); // Enable GPIOB clock
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE); // Enable USART clock

    // Voice
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // Multiplex push-pull output mode
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; // Default PA9 is USART1_TX multiplex
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &amp;GPIO_InitStructure);
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; // Multiplex pull-up input mode
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; // Default PA10 is USART1_RX multiplex
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &amp;GPIO_InitStructure);

    // Bluetooth
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // Multiplex push-pull output mode
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; // Default PB10 is USART3_TX multiplex
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &amp;GPIO_InitStructure);
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; // Multiplex pull-up input mode
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11; // Default PB11 is USART3_RX multiplex
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &amp;GPIO_InitStructure);

    USART_InitTypeDef UASRT_InitStructure; // USART initialization
    UASRT_InitStructure.USART_BaudRate = 9600; // Baud rate 9600
    UASRT_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; // No hardware flow control
    UASRT_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; // Both receive and send enabled
    UASRT_InitStructure.USART_Parity = USART_Parity_No; // No parity
    UASRT_InitStructure.USART_StopBits = USART_StopBits_1; // 1 stop bit
    UASRT_InitStructure.USART_WordLength = USART_WordLength_8b; // 8-bit word length
    USART_Init(USART1, &amp;UASRT_InitStructure);
    USART_Init(USART3, &amp;UASRT_InitStructure);
    USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); // Voice receive interrupt configuration
    USART_ITConfig(USART3, USART_IT_RXNE, ENABLE); // Bluetooth receive interrupt configuration

    // Interrupt
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // Group 2

    // Voice interrupt configuration
    NVIC_InitTypeDef NVIC_InitStructure;
    NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; // Specific channel
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; // Enable channel
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; // Preemptive priority 1
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; // Response priority 1
    NVIC_Init(&amp;NVIC_InitStructure);

    // Bluetooth interrupt configuration
    NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn; // Specific channel
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; // Enable channel
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; // Preemptive priority 2
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; // Response priority 1
    NVIC_Init(&amp;NVIC_InitStructure);

    USART_Cmd(USART1, ENABLE); // Enable USART1
    USART_Cmd(USART3, ENABLE); // Enable USART3
}

void USART1_IRQHandler(void) {
    if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET) // If received
    {
        Sustainedmove = 0; // Turn off continuous movement
        if (USART_ReceiveData(USART1) == 0x29) // Relaxed lie down
        {
            Face_Mode = 0;
            Face_Config();
            Action_Mode = 0;
        }
        else if (USART_ReceiveData(USART1) == 0x30) // Squat
        {
            Face_Mode = 1;
            Face_Config();
            Action_Mode = 1;
        }
        else if (USART_ReceiveData(USART1) == 0x31) // Stand
        {
            Face_Mode = 5;
            Face_Config();
            Action_Mode = 2;
        }
        else if (USART_ReceiveData(USART1) == 0x32) // Lie down
        {
            Face_Mode = 1;
            Face_Config();
            Action_Mode = 3;
        }
        else if (USART_ReceiveData(USART1) == 0x33) // Move forward
        {
            Face_Mode = 2;
            Face_Config();
            Action_Mode = 4;
        }
        else if (USART_ReceiveData(USART1) == 0x34) // Move backward
        {
            Face_Mode = 2;
            Face_Config();
            Action_Mode = 5;
        }
        else if (USART_ReceiveData(USART1) == 0x35) // Turn left
        {
            Face_Mode = 2;
            Face_Config();
            Action_Mode = 6;
        }
        else if (USART_ReceiveData(USART1) == 0x36) // Turn right
        {
            Face_Mode = 2;
            Face_Config();
            Action_Mode = 7;
        }
        else if (USART_ReceiveData(USART1) == 0x37) // Swing
        {
            Face_Mode = 4;
            Face_Config();
            Action_Mode = 8;
        }
        else if (USART_ReceiveData(USART1) == 0x38) // Reduce movement delay, increase speed
        {
            if (SpeedDelay == 120) {
                Face_Mode = 3;
                Face_Config();
            }
            if (SpeedDelay > 100)
                SpeedDelay -= 20;
            else {
                Face_Mode = 2;
                Face_Config();
                SpeedDelay = 200;
            }
        }
        else if (USART_ReceiveData(USART1) == 0x39) // Reduce swing delay, increase swing speed
        {
            if (SwingDelay == 4) {
                Face_Mode = 3;
                Face_Config();
            }
            if (SwingDelay > 3)
                SwingDelay--;
            else {
                Face_Mode = 4;
                Face_Config();
                SwingDelay = 9;
            }
        }
        else if (USART_ReceiveData(USART1) == 0x40) // Swing tail
        {
            (WeiBa == 0) ? (WeiBa = 1) : (WeiBa = 0);
            Face_Mode = 1;
            Face_Config();
            Action_Mode = 9;
        }
        else if (USART_ReceiveData(USART1) == 0x41) // Jump forward
        {
            Face_Mode = 2;
            Face_Config();
            Action_Mode = 10;
        }
        else if (USART_ReceiveData(USART1) == 0x42) // Jump backward
        {
            Face_Mode = 2;
            Face_Config();
            Action_Mode = 11;
        }
        else if (USART_ReceiveData(USART1) == 0x43) // Greet
        {
            Face_Mode = 6;
            Face_Config();
            Action_Mode = 13;
        }
        else if (USART_ReceiveData(USART1) == 0x44) // Turn on lights
        {
            AllLed = 1;
        }
        else if (USART_ReceiveData(USART1) == 0x45) // Turn off lights
        {
            AllLed = 0;
        }
        else if (USART_ReceiveData(USART1) == 0x46) // Turn on breathing light
        {
            BreatheLed = 1;
        }
        else if (USART_ReceiveData(USART1) == 0x47) // Turn off breathing light
        {
            BreatheLed = 0;
        }
        else if (USART_ReceiveData(USART1) == 0x48) // Stretch
        {
            Face_Mode = 6;
            Face_Config();
            Action_Mode = 14;
        }
        else if (USART_ReceiveData(USART1) == 0x49) // Stretch
        {
            Face_Mode = 6;
            Face_Config();
            Action_Mode = 15;
        }
        USART_ClearITPendingBit(USART1, USART_IT_RXNE);
    }
}

void USART3_IRQHandler(void) {
    if (USART_GetITStatus(USART3, USART_IT_RXNE) == SET) // If received
    {
        Sustainedmove = 1; // Turn on continuous movement
        if (USART_ReceiveData(USART3) == 0x29) // Relaxed lie down
        {
            Face_Mode = 0;
            Face_Config();
            Action_Mode = 0;
        }
        else if (USART_ReceiveData(USART3) == 0x30) // Squat
        {
            Face_Mode = 1;
            Face_Config();
            Action_Mode = 1;
        }
        else if (USART_ReceiveData(USART3) == 0x31) // Stand
        {
            Face_Mode = 5;
            Face_Config();
            Action_Mode = 2;
        }
        else if (USART_ReceiveData(USART3) == 0x32) // Lie down
        {
            Face_Mode = 1;
            Face_Config();
            Action_Mode = 3;
        }
        else if (USART_ReceiveData(USART3) == 0x33) // Move forward
        {
            Face_Mode = 2;
            Face_Config();
            Action_Mode = 4;
        }
        else if (USART_ReceiveData(USART3) == 0x34) // Move backward
        {
            Face_Mode = 2;
            Face_Config();
            Action_Mode = 5;
        }
        else if (USART_ReceiveData(USART3) == 0x35) // Turn left
        {
            Face_Mode = 2;
            Face_Config();
            Action_Mode = 6;
        }
        else if (USART_ReceiveData(USART3) == 0x36) // Turn right
        {
            Face_Mode = 2;
            Face_Config();
            Action_Mode = 7;
        }
        else if (USART_ReceiveData(USART3) == 0x37) // Swing
        {
            Face_Mode = 4;
            Face_Config();
            Action_Mode = 8;
        }
        else if (USART_ReceiveData(USART3) == 0x38) // Reduce movement delay, increase speed
        {
            if (SpeedDelay == 120) {
                Face_Mode = 3;
                Face_Config();
            }
            if (SpeedDelay > 100)
                SpeedDelay -= 20;
            else {
                Face_Mode = 2;
                Face_Config();
                SpeedDelay = 200;
            }
        }
        else if (USART_ReceiveData(USART3) == 0x39) // Reduce swing delay, increase swing speed
        {
            if (SwingDelay == 4) {
                Face_Mode = 3;
                Face_Config();
            }
            if (SwingDelay > 3)
                SwingDelay--;
            else {
                Face_Mode = 4;
                Face_Config();
                SwingDelay = 9;
            }
        }
        else if (USART_ReceiveData(USART3) == 0x40) // Swing tail
        {
            (WeiBa == 0) ? (WeiBa = 1) : (WeiBa = 0);
            Face_Mode = 1;
            Face_Config();
            Action_Mode = 9;
        }
        else if (USART_ReceiveData(USART3) == 0x41) // Jump forward
        {
            Face_Mode = 2;
            Face_Config();
            Action_Mode = 10;
        }
        else if (USART_ReceiveData(USART3) == 0x42) // Jump backward
        {
            Face_Mode = 2;
            Face_Config();
            Action_Mode = 11;
        }
        else if (USART_ReceiveData(USART3) == 0x43) // Greet
        {
            Face_Mode = 6;
            Face_Config();
            Action_Mode = 13;
        }
        else if (USART_ReceiveData(USART3) == 0x44) // Turn on lights
        {
            AllLed = 1;
        }
        else if (USART_ReceiveData(USART3) == 0x45) // Turn off lights
        {
            AllLed = 0;
        }
        else if (USART_ReceiveData(USART3) == 0x46) // Turn on breathing light
        {
            BreatheLed = 1;
        }
        else if (USART_ReceiveData(USART3) == 0x47) // Turn off breathing light
        {
            BreatheLed = 0;
        }
        else if (USART_ReceiveData(USART3) == 0x48) // Stretch
        {
            Face_Mode = 6;
            Face_Config();
            Action_Mode = 14;
        }
        else if (USART_ReceiveData(USART3) == 0x49) // Stretch
        {
            Face_Mode = 6;
            Face_Config();
            Action_Mode = 15;
        }
        USART_ClearITPendingBit(USART3, USART_IT_RXNE);
    }
}

Final Display Effect

After assembly, the overall robot is shown below. To enhance user experience, the robot’s appearance is designed to resemble a pet cat, further improving the user’s companionship experience. The robot consists of four SG90 servos for the legs, plus one SG90 servo simulating a tail that can swing. The lithium battery is placed at the bottom of the robot, and to facilitate subsequent code burning and function debugging, all corresponding pins are designed on the back of the robot for easy maintenance and upgrades.

Building an STM32 Smart Desktop Robotic Dog from Scratch

When the robot recognizes the user’s voice, the voice recognition module mounted on its main control board identifies the current user’s semantics and then transmits the corresponding command to the microcontroller for the appropriate action. When the user says the wake word “Xiao Zhi”, the robot updates the OLED display, changes its facial expression, and completes the startup process, informing the user that it can now respond to commands.

Building an STM32 Smart Desktop Robotic Dog from Scratch

When the user issues a turning command, the robot controls the two SG90 servos on the left to rotate forward and the two SG90 servos on the right to rotate backward. By using differential actions of the servos, it simulates joint movements to achieve the robot’s turning function. During the turning process, the robot updates the OLED display to show a new expression to distinguish it from other states. In this state, the robot will not execute the next voice command until the turning is completed, ensuring the integrity of the action.

Building an STM32 Smart Desktop Robotic Dog from ScratchBuilding an STM32 Smart Desktop Robotic Dog from ScratchBuilding an STM32 Smart Desktop Robotic Dog from ScratchBuilding an STM32 Smart Desktop Robotic Dog from Scratch

Building an STM32 Smart Desktop Robotic Dog from Scratch

Building an STM32 Smart Desktop Robotic Dog from Scratch

Leave a Comment