As a cyborg, how can I not have my own car? (doge)
I originally wanted to make a balancing car, but after seeing so many tutorials, I couldn't help but follow along and make a flying 4-wheeler. (My younger brother said he didn't like two wheels and cried)
# Project Introduction
##### **This project is a winter vacation training camp project for the LiChuang development board. After learning it, I have mastered the following:**
>* I have a certain understanding of common motor drivers and drew the circuit myself to realize the function;
>* I mastered the principle of tracking circuit and realized the tracking function of the car;
>* I mastered the distance measurement principle of the SR04 ultrasonic module and completed the distance measurement function to achieve obstacle avoidance;
>* The power supply part of the entire car uses 3 18650 batteries in parallel and realizes the charging function;
>* I use the cheap and commonly used 0.96IIC interface OLED screen for display, recognize a monochrome GUI library, and successfully transplant it to display a better-looking interface;
>* I use the Bluetooth module HC05 for wireless control to realize the mobile phone APP to control the car through Bluetooth;
>* I use the WIFI module ESP8266 for LAN control to realize the mobile phone APP to control the car through WIFI;
>* I use the 2.4G module NRF24L01 for joystick control and design a remote control stick. The joystick controls the car through NRF;
>* Use 3 photoresistors, placed in 3 directions, to achieve the light-chasing mode (that is, turn on the side that is brighter)
. A voice broadcast function is also added. I saw it on the open source platform and really wanted to implement this function. I tried to design it according to the data sheet, and it passed the test in one try, which is great. But the sound is too stiff and the cost is high.
# Hardware Implementation
#####
In order to fix the battery firmly on the car, I chose a SMD type battery box, which is a bit expensive. .
![图片.png]

If possible, I suggest using the following one, which can also be fixed on the board. But it is a plug-in. If the circuit board is very compact and there is no place for a through hole, it is better not to use it.
![图片.png]

**Because 5V and 3.3V and 8V of the motor are needed, and it is powered by a 3.7V battery. So I have three ways to power it.
1. Battery voltage -> boost -> 5V**
![图片.png]

**2. Battery voltage -> boost -> 8V**
The motor I bought before was 6V 300 rpm, so why did I use 8V power supply? Because I calculated the resistance distribution, I didn't use much 24K resistor, so I put it in. The actual measurement was about 7.8V, and there was no problem with the motor.
![图片.png]

![图片.png]

**3. 5V -> 3.3V**
Directly use the boosted 5V and then step it down to 3.3V to power some modules. (I don't like this solution very much, the components are too big, but when I was learning hardware, I bought a lot of materials for this solution)
![图片.png]

**4. Power switch**
The car must have a switch function, right? Originally, the switch had a long press to start circuit, but I thought that if I did a long press to start, I would probably break the buttons when debugging the car, so I gave up the idea of long press to start.
![图片.png]

**5. Battery Charging**
The old TC4056 is used, and it is charged via TYPE-C.
![图片.png]

The power supply part is finished! !
##### Control part
The control part mostly uses modules, including Bluetooth control, WIFI control, and NRF control. It also controls the screen and voice broadcast.
**1. Motor Control**
The DRV8833 is used for driving, which is very easy to use, but 4 motors require 8 PWMs. The VM pin is the motor power supply pin. It is recommended to add a large capacitor to prevent the motor from stealing too much power.
![图片.png]

**2. WIFI, Bluetooth and Screen Control**
This WIFI module is also a big power consumer. I was cheated by it once, so I gave the WiFi module a 1117 power supply separately. The communication method of the screen is IIC. I use the software IIC method, so the pins are placed randomly. I also wrote about how to port the 0.96 screen code for GD32F450, see the link: [LiChuang Liangshanpai GD32F450ZGT6--Porting 4-pin 0.96-inch OLED display](https://blog.csdn.net/qq_51930953/article/details/128484797)
The WIFI module uses ESP8266, and the Bluetooth module uses HC05 (I forgot to connect a mobile phone connection success indication pin here for the Bluetooth module, which should be added).
![picture.png]

**3. NRF control**
The NRF module is the NRF24L01 module purchased from Zeyao Technology. Their module information is relatively rich. The one I bought can be ported as long as I know SPI.
![picture.png]

![picture.png]

**4. Voice broadcast**
It is controlled through the serial port. For example, if a string of characters "嘉立创天长地久" is sent, it will broadcast the voice "嘉立创天长地久" through a specific frame format.
This drawing is a bit messy. Here, 100nF and 10uF are added to VDD, which is said to improve the sound quality. It may be that there is a problem with my PCB layout. The sound can be understood, but it is very hoarse.
![图片.png]

**5. Other controls**
I simulated the buzzer as a car horn;
the left and right turn signals are replaced by LEDs;
Regarding the power gating of the voice broadcast, my idea is that some people prefer quietness, so I made a power supply that can control the voice broadcast circuit through software. If the power is turned off, it can't broadcast.
![图片.png]

##### Collection part The
collection part includes temperature and humidity collection, power collection, illuminance collection, ultrasonic ranging collection, and 5-way tracking collection;
**1. Power collection and temperature and humidity collection**
Power collection is something I thought of when I just learned hardware. According to Ohm's law, the voltage at two points is 3.3V at most, and it is collected and converted to the actual power.
Later I found that many big guys use multi-channel voltage division like 10K to read the corresponding proportion of electricity (I am another example of being complacent, crying),
but I have already perfected the code for this electricity, so it doesn’t matter.
In terms of temperature and humidity, I originally wanted to use a small, high-precision temperature and humidity module such as SHT30, but when I saw its price, I chose and chose, looked and looked, and finally bought DHT11 (I was really poor doge).
Temperature and humidity also suffered losses. I thought that the data port could be pulled up by the internal resistor, but in actual use, it was found that it was best to add a 4.7K pull-up resistor externally, otherwise the read data would be inaccurate, which is also explained in the data manual.
![图片.png]

![图片.png]

**2. Ultrasonic and photosensitive acquisition**
The photosensitive is intended to be used for light chasing design. Put 3 photosensitives in 3 directions respectively, and run to whichever direction is brighter (actually, I want to practice PID more).
The ultrasonic wave uses the commonly used SR04 module. I won’t talk about the principle. It is very detailed on Baidu.
![图片.png]

**3. Tracking circuit**
Tracking is the change of high and low levels realized by voltage comparison. Collecting these changes will know whether it is currently walking on a specific track.
![图片.png]

I won’t talk about PCB. It’s not certain who teaches whom. . I can only say that I still have a lot of room for improvement, haha
# Software Implementation
##### 1. The Bluetooth control
uses the HC05 Bluetooth module. You need to configure the module before using it. (There is information in the attachment)
Press and hold the button on the module or pull the EN pin high. At this time, the light flashes slowly, and the HC-05 enters the AT command mode. The default baud rate is 38400; this mode is called the original mode. In the original mode, it is always in the AT command mode. Remember!! Every instruction must be added
, otherwise the command will not be recognized. You can send: AT to test whether it returns OK
! [Picture.png]

The most important thing is to set the mode to slave control, that is, wait for the mobile phone to connect to the Bluetooth of our Bluetooth module, which is mainly controlled by the mobile phone. **Send: AT+ROLE0**
![图片.png]

I also changed the baud rate to 115200, **Send: AT+UART=115200,0,0**
![图片.png]

You can also change the Bluetooth name, Send: **AT+NAME=Smart Car**
![图片.png]

If you find that your mobile phone APP cannot connect to your Bluetooth module, it should be a problem of incorrect pairing password. Most APPs have a Bluetooth pairing password of 1234,
so send: **AT+PSWD=1234**
You can also send **AT+PSWD?** to query the password
![图片.png]

The configuration is completed. Turn off the power of the Bluetooth module and then turn it on again. Its name is Smart Car, and the communication baud rate is 115200. And when the mobile phone is successfully connected, the flashing light on the module will stay on, indicating a successful connection.
###### Take a look at the code I implemented:
I send specific commands on my mobile phone to control the movement of the car```
/
************************************************************
* Function name: WIFI_control
* Function description: Perform corresponding control according to the command sent by the mobile phone
* Parameter: cmd=command sent by the mobile phone (see the instruction table for specific commands)
* Return value: None
* Remarks: The command is common to the Bluetooth module
* Instruction table
[Received data] [Function]
0x00 Stop
0x01 Forward
0X02 Backward 0X03
Turn left
0X04 Turn right
0X05 Left turn light on and then press to turn off
0X06 Right turn light on and then press to turn off
0X07 Speed increase
0X08 Speed decrease
0X09 Horn sound and then press to turn off
***************************************************************/
void WIFI_control(unsigned char cmd)
{
switch( cmd )
{
case 0x00://Stop
MOTOR_stop();
break;
case 0x01://
Motor_forward(CAR_SPEED, CAR_SPEED);
break;
case 0x02://
Motor_back(CAR_SPEED, CAR_SPEED);
break;
case 0x03://Turn left
MOTOR_left(CAR_SPEED, CAR_SPEED);
break;
case 0x04://Turn right
MOTOR_right(CAR_SPEED, CAR_SPEED);
break;
case 0x05://Turn left light on and then press to turn it off
Turn_Light_Con(LEFT_LED_FLAG=!LEFT_LED_FLAG, SET);
break;
case 0x06://Turn right light on and then press to turn it off
Turn_Light_Con(SET, RIGHT_LED_FLAG=!RIGHT_LED_FLAG);
break;
case 0x07://Speed increase
CAR_SPEED = CAR_SPEED + 500;
if( CAR_SPEED >= 7999 ) CAR_SPEED = 7999;
break;
case 0x08://speed reduction
CAR_SPEED = CAR_SPEED - 500;
if( CAR_SPEED <= 500 ) CAR_SPEED = 500;
break;
case 0x09://speaker control
BEEP_FLAG = !BEEP_FLAG;
if( BEEP_FLAG == 1 )
{
BEEP_ON();
}
else
{
BEEP_OFF();
}
break;
default:break;
}
}
```
##### 2. WIFI control
First, we need to control the WIFI module to turn on WIFI, so that we can control it by connecting to its WIFI.
```
/****************************************************************
* Function name: ESP12_Send_Cmd
* Function description: Send a command to the WIFI module and check whether the WIFI module returns the desired data
* Parameters:
* [cmd=sent AT command ack=desired response waitms=wait time for response cnt=how many times to wait for response]
* Return value: 1=got the desired response 0=did not get the desired response
* Remarks: None
************************************************************/
char ESP12_Send_Cmd(char *cmd,char *ack,unsigned int waitms,unsigned char cnt)
{
UART4_send_String((unsigned char*)cmd);//Send AT command to WIFI modulewhile
(cnt--)
{
delay_1ms(waitms);
//Serial port interrupts receiving wifi responseif
(U4RX_FLAG)
{
U4RX_FLAG = 0;
U4RX_LEN = 0;
if(strstr((char*)U4RX_BUFF,ack)!=NULL)//Receive the desired data
{
return 1;
}
memset(U4RX_BUFF,0,sizeof(U4RX_BUFF));//Clear the receive buffer
}
}
U4RX_FLAG = 0;//Clear the serial port data
flagU4RX_LEN = 0;//Clear the receive buffer array lengthreturn
0;
}
/****************************************************************
* Function name: ESP12_AP_Init
* Function Description: Set the WIFI module to AP mode, that is, turn on the hotspot for the mobile phone to connect
* Type parameter: None
* Return value: None
* Remarks: IP=196.168.4.1 Port=5000 If you want to change the WIFI name and password, please modify the following parameters
* WIFI_SSID
* WIFI_PASS
*****************************************************************/
void ESP12_AP_Init(void)
{
char buff[200];
UART4_Init(115200);
//Send AT and wait for it to return OK. Wait for 10ms. If it does not return OK, continue to wait. Wait for 3 times in total.
ESP12_Send_Cmd("AT
", "OK",10,3);
//Send AT+CWMODE=2 and wait for it to return OK. Wait for 30ms. If it does not return OK, continue to wait. Wait for 3 times in total.
ESP12_Send_Cmd("AT+CWMODE=2
","OK",30,3); //Configure WIFI AP mode
sprintf(buff, "AT+CWSAP="%s","%s",11,4
",WIFI_SSID, WIFI_PASS);
ESP12_Send_Cmd(buff,"OK",30,3); //Set wifi account and password
ESP12_Send_Cmd("AT+RST
","ready",800,3); //Restart
ESP12_Send_Cmd("AT+CIPMUX=1
","OK",50,3); //Open multiple connections
ESP12_Send_Cmd("AT+CIPSERVER=1,5000
","OK",50,3); //Start the server and set the port number
printf("ESP12_AP_Init succeed!
");
}
```
Here it is also determined whether there is a mobile phone connected. Only when there is a mobile phone connected can it be controlled, which greatly increases the CPU's working efficiency.
```
//DISCONNECTED In AP mode, the mobile phone disconnected the WIFI connection.
//DIST_STA_IP After turning on AP mode, a mobile phone is connected.
//0,CONNECT Device 0 is connected successfully.
//+IPD,0,4: Just received 4 bytes of data from device 0: just now
char WIFI_Mode(void)
{
char ret = 0;
//No mobile phone is connected
if( ConnectFlag == 0 )
{
ret = 0;
if( U4RX_FLAG == 1 )//Received WIFI data
{
U4RX_FLAG = 0;
//Is there a device connected
if( strstr((char*)U4RX_BUFF, "CONNECT") != NULL )
{
printf("Mobile phone is connected
");
ConnectFlag = 1;
}
//Clear the serial port receive
bufferClear_U4RX_BUFF();
}
}
// If a mobile phone is connectedif
( ConnectFlag == 1 )
{
ret = 1;
if( U4RX_FLAG == 1 )//Received WIFI data
{
U4RX_FLAG = 0;
//Judge whether the mobile phone is disconnected from the WIFI connectionif
( strstr((char*)U4RX_BUFF, "DISCONNECTED") != NULL )
{
printf("Disconnected
");
BEEP = 0;//Buzzer
offTurn_Light_Con(SET, SET);//Left and right lights
offConnectFlag = 0;
}
//Determine what key is currently pressed (determine what data the current mobile phone sends)
WIFI_control( Get_WIFIAPP_Data() );
//Clear the serial port receive buffer and wait for the next control command to arriveClear_U4RX_BUFF
();
}
}
return ret;
}
```
Then the control aspect is the same as Bluetooth, so I won't post it.
##### 3. Motor control
For motor control, for the convenience of wiring, I chose the PWM control pins closest to the motor control pins, and used them respectively```
PA15
--TIM1-CH0--PWML11 JTDI Left wheel
PB4 --TIM2-CH0--PWML12 NJTRST
PB3 --TIM1-CH1--PWML21 JTDO
PB6 --TIM3-CH0--PWML22
PC7 --TIM7-CH1 --PWMR11 Right wheel
PC6 --TIM7-CH0 --PWMR12
PA7 --TIM13-CH0--PWMR21
PA6 --TIM12_CH0--PWMR22
```
Many timers are wasted, but they are enough.
For the general code, see my previous article, link: [LiChuang Liangshan School GD32F450ZGT6--Timer 3-PWM-4 channel output](https://blog.csdn.net/qq_51930953/article/details/127473463)
Although the article only writes 4 channels and it is the same timer, the specific initialization method is the same, you can also download my source code in the attachment.
##### 4. Voice broadcast
Voice broadcast control, as long as the serial port is configured, and then the data is sent according to the command frame format required by the data manual, the broadcast function can be realized.
![图片.png]

![图片.png]

Specific implementation of command frame encapsulation and sending code
* * *
```
/************************************************************
* Function name: SYN6288_Send_Cmd
* Function description: Send command to SYN6288
* Type parameter:
* [CmdType=command word] Available parameters:
* -0x01 Speech synthesis command
* -0x31 Set baud rate (default 9600)
* -0x02 Stop synthesis command
* -0x03 Pause synthesis command
* -0x04 Resume synthesis command
* -0x21 Chip status query command
* -0x88 Chip enters low power mode
* 【CmdPar=command parameter】 Available parameters:
* -When the decimal value of the high 5 bits of the byte is 0, it means no background music is added
* -When the decimal value of the high 5 bits of the byte is 1~15, it means the number of the selected background music
* -When the decimal value of the low 3 bits of the byte is 0~3, and the command word is a speech synthesis command, it means setting the text to BG2312 format, GBK format, BIG5 format, and UNICODE format respectively; * -When
the decimal value of the low 3 bits of the byte is 0~2, and the command word is to set the baud rate, it means setting the baud rate to 9600, 19200, and 38400 respectively; *
【text=broadcast text】
* Return value: 0=send successfully
* Note:
* After receiving the control command frame, the chip will send a byte of status feedback to the host computer. The host computer can judge the current working status of the chip based on this feedback
* Initialization successful return 0X4A
* Received correct command frame and returned 0x41
* Received unrecognizable command frame and returned 0x45
* Chip broadcasting status and returned 0x4E
* Chip idle status and returned 0x4F
**********************************************************/
unsigned char SYN6288_Send_Cmd(u8 CmdType, u8 CmdPar, u8 *text)
{
unsigned char frame_header = 0XFD; //Frame
headerunsigned int Text_Len = strlen((const char*)text);//Length of the text to be sentunsigned
int Data_Len = Text_Len + 3; //Data area length; 3 = frame header, frame tail and XOR checkunsigned
char Xor_Check = 0; //XOR check
storageunsigned char Send_Buff[210]; //Command frame to be sent, the maximum length of the command frame is 206
bytesu8 i = 0;
Send_Buff[0] = frame_header; //frame header
Send_Buff[1] = Data_Len>>8; //high bit first
Send_Buff[2] = Data_Len&0x00ff; //low bit first
Send_Buff[3] = CmdType; //command word
Send_Buff[4] = CmdPar; //command data
sprintf((char*)Send_Buff+5, "%s", text );
//send data
for( i = 0; i < Text_Len+5; i++ )
{
Xor_Check = Xor_Check ^ Send_Buff[i];//XOR check each data and save it
USART1_Send_Bit( Send_Buff[i] );//send data
}
USART1_Send_Bit( Xor_Check );//send the last bit: XOR check data
return 0;
}
```
>If you find that the content it broadcasts is inconsistent with the content you sent, please make sure that the encoding format of the file you sent the command to is ANSI encoding format
>Open it with .txt text and then save it as ANSI format
! [picture.png]

##### 5. Light chasing mode
The light chasing mode uses PID and randomly adopts an approximate parameter.
* * *
```
float Kp = 800, Ki=0, Kd =5;//PID parameters
float P = 0, I = 0, D = 0, PID_value = 0;
float error = 0, previous_error = 0;
static int initial_motor_speed = 1000;//Basic speed
//PID calculation
void calc_pid(void)
{
P=error; //Current error
I=I+error; //Error accumulation
D=error-previous_error; //The error between the current error and the previous error
PID_value=(Kp*P)+(Ki*I)+(Kd*D);
previous_error=error;//Update previous error
}
//Motor action
void motorsWrite(int speedL,int speedR)
{
if(speedR > 0)
{
Right_forward(speedR);//The right two wheels move forward
}
else
{
Right_Back(-speedR);//The right two wheels move backward
}
if(speedL > 0)
{
Left_Back(speedL); //The left two wheels move forward
}
else
{
Left_Back(-speedL);//Left two wheels backwards
}
}
//Numerical limit and control
void motor_cortrol(void)
{
//Basic speed + PID value
int left_motor_speed = initial_motor_speed+PID_value;
int right_motor_speed = initial_motor_speed-PID_value;
//Left wheel limitif
(left_motor_speed <= -8000)
{
left_motor_speed = -8000;
}
if(left_motor_speed >= 8000)
{ left_motor_speed = 8000
;
}
//Right wheel limitif
(right_motor_speed <= -8000)
{
right_motor_speed = -8000;
}
if(right_motor_speed >= 8000)
{
right_motor_speed = 8000;
}
//If the light intensity is not the highest in the middleif
( error != 0 )
{
//Because of too much debugging, the speed of the left wheel is inconsistent and needs to be adjusted manuallyif
( left_motor_speed < 0 )
{
left_motor_speed = left_motor_speed - 2000;
}
else
{
left_motor_speed = left_motor_speed + 2000;
}
motorsWrite(left_motor_speed,right_motor_speed);
}
else//The illumination in the middle is the highest, so stop the car
{
motorsWrite(0,0);
}
}
//Error acquisitionvoid
error_estimate(void)
{
unsigned int left = Get_Liium_Val(GM_LEFT);//Read the ADC value after the left photosensitivity
filterunsigned int middle = Get_Liium_Val(GM_MIDDLE);//Read the ADC value after the middle photosensitivity
filterunsigned int right = Get_Liium_Val(GM_RIGHT);//Read the ADC value after the right photosensitivity filter
//Set errorif
( (left > middle) && (left > right) )//The left side is the brightest
{
error = -5;
}
if( (middle > left) && (middle > right) )//The middle side is the brightest
{
error = 0;
}
if( (right > left) && (right > middle) )////The right side is the brightest
{
error = 5;
}
}
//Following light modevoid
follow_light(void)
{
//Get
errorerror_estimate();
//Calculate PID valuecalc_pid
();
//Control
motormotor_cortrol();
}
```