Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Implementing a Voice-Controlled Smart Car with C51 Microcontroller

Tech 1

This project is built on the STC89C52RC microcontroller, utilizing PWM for speed control. It features voice-controlled mode switching for line tracking, following, obstacle avoidence, and remote control via Bluetooth/Wi-Fi/4G. The system also includes speed measurement with OLED display and data transmission to a host computer.

Motor Driver Module (L9110s, later upgraded to L298N with dual 18650 batteries)

Connecting VCC and GND powers the module, indicated by its LED. The following logic controls motor direction (Note: actual wiring may require adjustments based on practical testing):

  • Setting IA1 high and IB1 low causes the motor connected to [OA1, OB1] to rotate forward.
  • Setting IA1 low and IB1 high causes the motor connected to [OA1, OB1] to rotate backward.
  • Similar logic applies to IA2/IB2 for the second motor.

Crucial Wiring Note: The microcontroller must have a seperate GND connection to the power supply.

Basic Motor Control

Adjust motor wire connections to ensure both wheels spin forward.

#include "reg52.h"

sbit MotorR_A = P3^2;
sbit MotorR_B = P3^3;
sbit MotorL_A = P3^4;
sbit MotorL_B = P3^5;

void main()
{
    MotorL_A = 1;
    MotorL_B = 0;
    MotorR_A = 1;
    MotorR_B = 0;
}

Implementing Car Movement

1. Direction Control Functions

void moveForward(){
    MotorL_A = 1; MotorL_B = 0;
    MotorR_A = 1; MotorR_B = 0;
}
void moveBackward(){
    MotorL_A = 0; MotorL_B = 1;
    MotorR_A = 0; MotorR_B = 1;
}
void turnLeft(){
    MotorL_A = 1; MotorL_B = 1;
    MotorR_A = 1; MotorR_B = 0;
}
void turnRight(){
    MotorL_A = 1; MotorL_B = 0;
    MotorR_A = 1; MotorR_B = 1;
}
void haltCar(){
    MotorL_A = 1; MotorL_B = 1;
    MotorR_A = 1; MotorR_B = 1;
}

2. RF Remote Control Using simple GPIO reading to interpret RF module outputs.

#include "reg52.h"
#include "motor.h"

sbit RF_D0 = P2^4;
sbit RF_D1 = P2^5;
sbit RF_D2 = P2^6;
sbit RF_D2 = P2^7;

void main()
{
    unsigned char command = 0;
    while(1){
        if(RF_D0)       command = 1;
        else if(RF_D1)  command = 2;
        else if(RF_D2)  command = 3;
        else if(RF_D3)  command = 4;
        else            command = 0;
        switch(command){
            case 1: moveForward(); break;
            case 2: moveBackward(); break;
            case 3: turnLeft(); break;
            case 4: turnRight(); break;
            case 0: haltCar(); break;
        }
    }
}

Bluetooth Control via UART

1. UART Initialization and Communication

// uart.c
#include "reg52.h"

sfr AUXR = 0x8E;
char bluetoothCmd = 's';

void initUART(void) // 9600bps @ 11.0592MHz
{
    AUXR = 0x01;
    SCON = 0x50;
    TMOD &= 0x0F;
    TMOD |= 0x20;
    TH1 = TL1 = 0xFD;
    TR1 = 1;
    EA = ES = 1;
}

void sendUARTChar(unsigned char dat)
{
    SBUF = dat;
    while(!TI);
    TI = 0;
}

char fetchCommand(){
    return bluetoothCmd;
}

void handleUART() interrupt 4
{
    unsigned char receivedByte;
    if(RI){
        RI = 0;
        receivedByte = SBUF;
        bluetoothCmd = receivedByte;
        sendUARTChar('A'); // Acknowledge
    }
}

2. Motor Control Based on UART Command

// motor_control.c
void executeCmd(char cmd)
{
    switch(cmd){
        case 'f': moveForward(); break;
        case 'b': moveBackward(); break;
        case 'l': turnLeft(); break;
        case 'r': turnRight(); break;
        case 's': haltCar(); break;
    }
}

3. Momentary Control via Bluetooth Adjust the Bluetooth app to send commands every 0.2s while a button is pressed. The interrupt handler executes the movement for 200ms.

void handleUART() interrupt 4
{
    unsigned char cmdByte;
    if(RI){
        RI = 0;
        cmdByte = SBUF;
        bluetoothCmd = cmdByte;
        executeCmd(bluetoothCmd);
        delayMs(200); // Move for 200ms
    }
}

The main loop keeps the car stopped when no command is received.

void main()
{
    initUART();
    while(1){
        haltCar();
    }
}

PWM-Based Speed Control

The principle involves varying the duty cycle of the signal sent to the motor drivers within a fixed period (e.g., 20ms). A higher duty cycle (more time active) results in higher average power and faster speed.

Testing PWM Speed

#include "reg52.h"

sbit MotorR_A = P3^2;
sbit MotorR_B = P3^3;
sbit MotorL_A = P3^4;
sbit MotorL_B = P3^5;

int pwmDuty = 40; // Range 0-40
int timerTicks = 0;

void configTimer0() // 500us interval
{
    TMOD = 0x01;
    TH0 = 0xFE; TL0 = 0x33;
    TR0 = 1;
    ET0 = EA = 1;
}

void main()
{
    pwmDuty = 40;
    timerTicks = 0;
    configTimer0();
    while(1){
        // Test different speeds
        pwmDuty = 40; // Fast
        delayMs(2000);
        pwmDuty = 20; // Slow
        delayMs(2000);
        pwmDuty = 0;  // Stop
        delayMs(2000);
    }
}

void timer0_ISR() interrupt 1
{
    timerTicks++;
    TH0 = 0xFE; TL0 = 0x33; // Reload
    if(timerTicks < pwmDuty){
        moveForward();
    } else {
        haltCar();
    }
    if(timerTicks == 40){ // 20ms period
        timerTicks = 0;
    }
}

Independent Wheel Speed Control Using two separate PWM variables and two timers (or one timer with two counters) for independent control.

int leftWheelSpeed = 30;
int rightWheelSpeed = 30;
int leftTickCnt = 0;
int rightTickCnt = 0;

void configTimer0_1() // For left and right wheels
{
    // Timer0 for left wheel
    TMOD = 0x01;
    TH0 = 0xFE; TL0 = 0x33;
    TR0 = ET0 = 1;
    // Timer1 for right wheel
    TMOD |= 0x10;
    TH1 = 0xFE; TL1 = 0x33;
    TR1 = ET1 = 1;
    EA = 1;
}

void timer0_ISR() interrupt 1 // Left wheel control
{
    leftTickCnt++;
    TH0 = 0xFE; TL0 = 0x33;
    if(leftTickCnt < leftWheelSpeed){
        MotorL_A = 1; MotorL_B = 0;
    } else {
        MotorL_A = 1; MotorL_B = 1;
    }
    if(leftTickCnt == 40) leftTickCnt = 0;
}

void timer1_ISR() interrupt 3 // Right wheel control
{
    rightTickCnt++;
    TH1 = 0xFE; TL1 = 0x33;
    if(rightTickCnt < rightWheelSpeed){
        MotorR_A = 1; MotorR_B = 0;
    } else {
        MotorR_A = 1; MotorR_B = 1;
    }
    if(rightTickCnt == 40) rightTickCnt = 0;
}

Infrared Line Tracking and Object Following

The TCRT5000 sensor emits infrared light. When no object is detected, the light is not reflected back, and the digital output (D0) remains high. When a object (like a black line for tracking or a surface for following) reflects sufficient IR light back, the output goes low.

Integration Principle: For line tracking, sensors are placed under the car. A high output over a white surface and a low output over a black line guide the car's steering logic to stay on the line. For object following, sensors face forward, and the car adjusts its position to maintain a specific sensor reading pattern.

Combined Logic Example:

// Define sensor pins
sbit IRSensor_L = P1^0;
sbit IRSensor_R = P1^1;

void lineTrackingMode()
{
    // Simple two-sensor line tracking
    if(!IRSensor_L && IRSensor_R){ // Left sensor on line, turn right
        turnRight();
    } else if(IRSensor_L && !IRSensor_R){ // Right sensor on line, turn left
        turnLeft();
    } else if(!IRSensor_L && !IRSensor_R){ // Both on line, go forward
        moveForward();
    } else { // Both off line, stop or search
        haltCar();
    }
}

void followMode()
{
    // Simple following: aim to have both sensors triggered equally
    if(IRSensor_L && !IRSensor_R){
        // Object is more to the right, turn slightly right
        // Adjust right wheel speed lower
        rightWheelSpeed = 15;
        leftWheelSpeed = 30;
    } else if(!IRSensor_L && IRSensor_R){
        // Object is more to the left
        leftWheelSpeed = 15;
        rightWheelSpeed = 30;
    } else {
        // Centered, move forward
        leftWheelSpeed = rightWheelSpeed = 30;
        moveForward();
    }
}

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.