介绍
这是一个通过蓝牙和单片机控制汽车模型(特别是乐高的汽车模型)上的灯饰的C语言工程和安卓App
可以较为逼真地模拟现实的灯光控制
工程使用的硬件:
- STC12C5A60S2 最小系统板
- ATK-HC05 主从一体蓝牙模块
单片机和蓝牙模块通过串口通讯
目前需要16个IO口,具体绑定的IO口可详见工程
手机安装串口软件,蓝牙连接模块即可发送指令控制
指令详见工程
可以控制前照灯、雾灯、转向灯、刹车灯、倒车灯及其他灯光
预留五个位置扩展灯位
实物示例
手机控制界面
项目地址
https://github.com/AntoniotheFuture/model-car-light-controller
代码片段
main.h
#ifndef __MAIN_H__
#define __MAIN_H__
#include <reg52.h>
//绑定车灯(STC12C5A60S2)
sbit L_Back = P0^0;//示廓灯
sbit L_Head = P0^1;//小灯
sbit L_Head_Big = P0^2;//大灯
sbit L_Left = P0^3;//左转灯
sbit L_Right = P0^4;//右转灯
sbit L_Break = P0^5;//刹车灯
sbit L_Re = P0^6;//倒车灯
sbit L_Front_Fog = P0^7;//前雾灯
sbit L_Back_Fog = P2^7;//后雾灯
sbit L_Ex0 = P2^6;//额外灯0
sbit L_Ex1 = P2^5;//额外灯1
sbit L_Ex2 = P2^4;//额外灯2
sbit L_Ex3 = P2^3;//额外灯3
sbit L_Ex4 = P2^2;//额外灯4
sbit L_Ex5 = P2^1;//额外灯5
sbit L_Buzzer = P2^0;//蜂鸣器
//函数或者变量声明
extern void Delay_ms(unsigned int n);
extern void BTinit();
#endif
//****************************************************
//****************************************************
//车灯控制主函数
//****************************************************
//****************************************************
//Author 梁小蜗
#include "main.h"
//****************************************************
//主函数
//****************************************************
//低电平表示通路
unsigned int twinkleOn = 0;//闪烁标志,0.5s
unsigned int diOn = 0;//蜂鸣器间隔,1s
unsigned int lightMode = 0;//车辆开灯模式,0=关闭,1=示廓灯,2=前照灯
unsigned int fogMode = 0;//车辆雾灯模式,0=关闭,1=前雾灯,2=前后雾灯
unsigned int bigLight = 0;//大灯开启标志,0=关闭,1=开启
unsigned int turnLightOn = 0; //转向标志,0=关闭,1=左转,2=右转
bit breakOn = 0;//刹车
bit backOn = 0;//倒车
bit dfOn = 0;//双闪
bit BuzzerOn = 0;//蜂鸣器状态(工程车需单独控制)
//****************************************************
//MS延时函数(12M晶振下测试)
//****************************************************
void Delay_ms(unsigned int n)
{
unsigned int i,j;
for(i=0;i<n;i++)
for(j=0;j<500;j++);//110
}
#define uchar unsigned char
/******************************************************************/
/* 串口中断程序*/
/******************************************************************/
void UART_SER () interrupt 4
{
unsigned int n; //定义临时变量
uchar receive_data;
if(RI) //判断是接收中断产生
{
RI=0; //标志位清零
n=SBUF; //读入缓冲区的值
receive_data=SBUF;
if(receive_data == '1'){
//L_Ex5 = 0;
}
switch(n)
{
case 0: lightMode = 0; break; //关闭灯光
case 1: lightMode = 1; break; //示廓灯
case 2: lightMode = 2; break; //前照灯
case 3: fogMode = 0; break; //关闭雾灯
case 4: fogMode = 1; break; //打开前雾灯
case 5: fogMode = 2; break; //打开前后雾灯
case 6: bigLight = 0; break; //关闭大灯
case 7: bigLight = 1; break; //打开大灯
case 8: breakOn = 0; break; //关闭刹车灯
case 9: breakOn = 1; break; //打开刹车灯
case 10: backOn = 0; break; //关闭倒车
case 11: backOn = 1; break; //打开倒车
case 12: turnLightOn = 0;break; //关闭转向灯
case 13: turnLightOn = 1;break; //左转
case 14: turnLightOn = 2;break; //右转
case 15: dfOn = 0;break; //关闭双闪
case 16: dfOn = 1;break; //打开双闪
case 17: BuzzerOn = 0;break; //关闭蜂鸣器
case 18: BuzzerOn = 1;break; //打开蜂鸣器
case 19: L_Ex0 = 1;break; //关闭额外灯0
case 20: L_Ex0 = 0;break; //打开额外灯0
case 21: L_Ex1 = 1;break;
case 22: L_Ex1 = 0;break;
case 23: L_Ex2 = 1;break;
case 24: L_Ex2 = 0;break;
case 25: L_Ex3 = 1;break;
case 26: L_Ex3 = 0;break;
case 27: L_Ex4 = 1;break;
case 28: L_Ex4 = 0;break;
case 29: L_Ex5 = 1;break;
case 30: L_Ex5 = 0;break;
}
//判断灯光
switch(lightMode)
{
case 0:
L_Back = 1;
L_Head = 1;
break;
case 1:
L_Back = 0;
L_Head = 1;
break;
case 2:
L_Back = 0;
L_Head = 0;
break;
}
switch(fogMode)
{
case 0:
L_Front_Fog = 1;
L_Back_Fog = 1;
break;
case 1:
L_Front_Fog = 0;
L_Back_Fog = 1;
break;
case 2:
L_Front_Fog = 0;
L_Back_Fog = 0;
break;
}
L_Head_Big = ~bigLight;
if(breakOn || backOn){
L_Break = 0;
}else{
L_Break = 1;
}
L_Re = ~backOn;
}
}
//蓝牙初始化
void BTinit(void)
{
SCON = 0x50; // SCON: 模式1, 8-bit UART, 使能接收
TMOD |= 0x20;
TH1=0xfd; //波特率9600 初值
TL1=0xfd;
TR1= 1;
EA = 1; //开总中断
ES= 1; //打开串口中断
}
void main()
{
L_Back = 1;
L_Head = 1;
L_Head_Big = 1;
L_Left = 1;
L_Right = 1;
L_Break = 1;
L_Re = 1;
L_Front_Fog = 1;
L_Back_Fog = 1;
L_Ex0 = 1;
L_Ex1 = 1;
L_Ex2 = 1;
L_Ex3 = 1;
L_Ex4 = 1;
L_Ex5 = 1;
L_Buzzer = 1;
BTinit();
while(1)
{
//判断双闪
if(dfOn)
{
L_Left = twinkleOn;
L_Right = twinkleOn;
}else
{
L_Left = 1;
L_Right = 1;
switch(turnLightOn){
case 1:
L_Left = twinkleOn;
break;
case 2:
L_Right = twinkleOn;
break;
}
}
//判断蜂鸣器
if(backOn || BuzzerOn){
L_Buzzer = diOn > 1;
}
if(!backOn && !BuzzerOn){
L_Buzzer = 1;
}
Delay_ms(500); //延时0.5s
twinkleOn = ~twinkleOn;
diOn ++;
if(diOn >= 3){
diOn = 0;
}
}
}
MainActivity.java
package com.nonemin.carlightcontroller;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import com.google.android.material.snackbar.Snackbar;
import androidx.appcompat.app.AppCompatActivity;
import android.provider.Settings;
import android.util.Log;
import android.view.View;
import androidx.navigation.NavController;
import androidx.navigation.Navigation;
import androidx.navigation.ui.AppBarConfiguration;
import androidx.navigation.ui.NavigationUI;
import com.nonemin.carlightcontroller.databinding.ActivityMainBinding;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import java.io.IOException;
import java.io.OutputStream;
import java.util.UUID;
import cc.liyongzhi.bluetoothselector.BluetoothConnectCallback;
import cc.liyongzhi.bluetoothselector.MedBluetooth;
public class MainActivity extends AppCompatActivity {
private AppBarConfiguration appBarConfiguration;
private ActivityMainBinding binding;
private String TAG = "LUM: ";
//private TextView buttonConnect, buttonLed;
private BluetoothAdapter mbluetoothAdapter;
private String bluetoothDeviceMacAddress = ""; //Bluetooth module physical address
private BluetoothDevice bluetoothDevice = null; // Connected Bluetooth device
private BluetoothSocket btSocket = null; // Bluetooth communication socket
private final static String MY_UUID = "00001101-0000-1000-8000-00805F9B34FB"; // SPP service UUID number
private final static int RESULT_CODE = 100;
boolean isConnected;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
setSupportActionBar(binding.toolbar);
NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment_content_main);
appBarConfiguration = new AppBarConfiguration.Builder(navController.getGraph()).build();
NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration);
binding.fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
}
});
mbluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); //get the default bluetooth adapter
if(mbluetoothAdapter == null){
Toast.makeText(this,"这个设备没有蓝牙",Toast.LENGTH_LONG).show();
//finish();
}
IntentFilter filter = new IntentFilter();
filter.addAction(BluetoothDevice.ACTION_FOUND); //Bluetooth search
filter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED);
filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
registerReceiver(mReceiver, filter);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
if (id == R.id.action_connect) {
Log.i(TAG, "check link");
//Jump directly to the Bluetooth settings interface
if(!isConnected){
if (!mbluetoothAdapter.isEnabled()){
startActivityForResult(new Intent(Settings.ACTION_BLUETOOTH_SETTINGS), RESULT_CODE);
} else {
//以下为调用本库,输入为Context、BluetoothConnectCallback
MedBluetooth.connectBluetooth(this, new BluetoothConnectCallback() {
@Override
// 连接成功或失败后调用。
public void connected(BluetoothSocket socket, BluetoothDevice device, Exception e) {
if (e != null) {
//连接失败
isConnected = false;
} else {
//输出为获得的socket,可以自行存到全局变量里进行数据输入输出操作。
//device为本次连接的设备,可调用 device.getAddress() 获得mac地址。
//e 为错误。
btSocket = socket;
bluetoothDeviceMacAddress = device.getAddress();
bluetoothDevice = device;
isConnected = true;
//connectBtSocket();
}
}
@Override
// 连接断开后调用
public void disconnected() {
// 原理为通过捕获系统的广播而调用的。
isConnected = false;
}
});
}
} else {
disconnect();
}
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
public boolean onSupportNavigateUp() {
NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment_content_main);
return NavigationUI.navigateUp(navController, appBarConfiguration)
|| super.onSupportNavigateUp();
}
public void send(int command) {
if (btSocket == null || !btSocket.isConnected()) {
Toast.makeText(this, "请先连接蓝牙", Toast.LENGTH_SHORT).show();
return;
}
try {
if (btSocket != null) {
OutputStream os = btSocket.getOutputStream(); //Bluetooth connection output stream
os.write(command);
} else {
Toast.makeText(this, "请先连接蓝牙", Toast.LENGTH_SHORT).show();
}
} catch (IOException e) {
e.printStackTrace();
}
}
private void connectBtSocket() {
// Get the handle of the Bluetooth device
bluetoothDevice = mbluetoothAdapter.getRemoteDevice(bluetoothDeviceMacAddress);
//Turn off scanning before pairing
if (mbluetoothAdapter.isDiscovering()) {
mbluetoothAdapter.cancelDiscovery();
}
// Get the connected socket
try {
btSocket = bluetoothDevice.createRfcommSocketToServiceRecord(UUID.fromString(MY_UUID));
//btSocket.connect(); //Connection socket
} catch (IOException e) {
Toast.makeText(this, "Connection failed, can't get Socket!" + e, Toast.LENGTH_LONG).show();
e.printStackTrace();
}
if (btSocket.isConnected()) {
Log.i(TAG, "socket connected");
Toast.makeText(this, "connect success", Toast.LENGTH_SHORT).show();
//buttonConnect.setText("蓝牙已连接");
isConnected = true;
} else {
Log.i(TAG, "socket didn't connected");
Toast.makeText(this, "connect failed", Toast.LENGTH_SHORT).show();
isConnected = false;
}
}
private void disconnect() {
try {
if (btSocket != null) {
btSocket.close();
}
} catch (IOException e) {
}
//buttonConnect.setText("连接蓝牙");
isConnected = false;
}
// Help us find the physical address of the Bluetooth module that needs to be connected
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (BluetoothDevice.ACTION_FOUND.equals(action)) {
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
Log.i(TAG, "Searched Bluetooth device; device name: " + device.getName() + " device address: " + device.getAddress());
}
if(btSocket == null){
return;
}
if (BluetoothDevice.ACTION_ACL_CONNECTED.equals(action)) {
Log.i(TAG, "ACTION_ACL_CONNECTED");
if (btSocket.isConnected()) {
//buttonConnect.setText("蓝牙已连接");
isConnected = true;
}
}
if (BluetoothDevice.ACTION_ACL_DISCONNECTED.equals(action)) {
Log.i(TAG, "ACTION_ACL_CONNECTED");
if (btSocket.isConnected()) {
//buttonConnect.setText("连接蓝牙");
isConnected = false;
}
}
}
};
}
FirstFragment.java
package com.nonemin.carlightcontroller;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.fragment.app.Fragment;
import com.nonemin.carlightcontroller.databinding.FragmentFirstBinding;
public class FirstFragment extends Fragment {
private FragmentFirstBinding binding;
//记录状态
int lm,fm;
boolean blon = false; //大灯状态
boolean breakon = false; //刹车状态
boolean ReOn = false; //倒车状态
boolean Lon = false; //左转开启状态
boolean warning = false;//警示灯开启状态
boolean Ron = false; //右转开启状态
boolean beeon = false;//蜂鸣器状态
boolean l1on = false;
boolean l2on = false;
boolean l3on = false;
boolean l4on = false;
boolean l5on = false;
MainActivity mainActivity;// = (MainActivity) getActivity();
@Override
public View onCreateView(
LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState
) {
binding = FragmentFirstBinding.inflate(inflater, container, false);
//buttonConnect = binding.TBTStatus;
return binding.getRoot();
}
public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mainActivity = (MainActivity) getActivity();
binding.btnLm0.setOnClickListener(btnClick);
binding.btnLm1.setOnClickListener(btnClick);
binding.btnLm2.setOnClickListener(btnClick);
binding.btnFm0.setOnClickListener(btnClick);
binding.btnFm1.setOnClickListener(btnClick);
binding.btnFm2.setOnClickListener(btnClick);
binding.btnBlon.setOnClickListener(btnClick);
binding.btnBl.setOnClickListener(btnClick);
binding.btnRl.setOnClickListener(btnClick);
binding.btnL.setOnClickListener(btnClick);
binding.btnR.setOnClickListener(btnClick);
binding.btnW.setOnClickListener(btnClick);
binding.btnBee.setOnClickListener(btnClick);
binding.btnL1.setOnClickListener(btnClick);
binding.btnL2.setOnClickListener(btnClick);
binding.btnL3.setOnClickListener(btnClick);
binding.btnL4.setOnClickListener(btnClick);
binding.btnL5.setOnClickListener(btnClick);
}
View.OnClickListener btnClick = new View.OnClickListener() {
@RequiresApi(api = Build.VERSION_CODES.M)
@Override
public void onClick(View v) {
Log.i("btn",v.getId() + "");
switch (v.getId()){
case R.id.btn_lm0:send(0);lm = 0;break;
case R.id.btn_lm1:send(1);lm = 1;break;
case R.id.btn_lm2:send(2);lm = 2;break;
case R.id.btn_fm0:send(3);fm = 0;break;
case R.id.btn_fm1:send(4);fm = 1;break;
case R.id.btn_fm2:send(5);fm = 2;break;
//case R.id.btn_bloff:send(6);break;
case R.id.btn_blon:
if(blon){
send(6);
blon = false;
}else{
send(7);
blon = true;
}
break;
case R.id.btn_bl:
if(breakon){
send(8);
breakon = false;
}else{
send(9);
breakon = true;
}
break;
case R.id.btn_rl:
if(ReOn){
send(10);
ReOn = false;
}else{
send(11);
ReOn = true;
}
break;
case R.id.btn_l:
if(Lon){
send(12);
Lon = false;
}else{
send(13);
Lon = true;
}
break;
case R.id.btn_r:
if(Ron){
send(12);
Ron = false;
}else{
send(14);
Ron = true;
}
break;
case R.id.btn_w:
if(warning){
send(15);
warning = false;
}else{
send(16);
warning = true;
}
break;
case R.id.btn_bee:
if(beeon){
send(17);
beeon = false;
}else{
send(18);
beeon = true;
}
break;
case R.id.btn_l1:
if(l1on){
send(19);
l1on = false;
}else{
send(20);
l1on = true;
}
break;
case R.id.btn_l2:
if(l2on){
send(21);
l2on = false;
}else{
send(22);
l2on = true;
}
break;
case R.id.btn_l3:
if(l3on){
send(23);
l3on = false;
}else{
send(24);
l3on = true;
}
break;
case R.id.btn_l4:
if(l4on){
send(25);
l4on = false;
}else{
send(26);
l4on = true;
}
break;
case R.id.btn_l5:
if(l5on){
send(27);
l5on = false;
}else{
send(28);
l5on = true;
}
break;
}
updateButton();
}
};
@Override
public void onDestroyView() {
super.onDestroyView();
binding = null;
}
public void send(int command) {
mainActivity.send(command);
}
//更新按钮显示
@RequiresApi(api = Build.VERSION_CODES.M)
private void updateButton(){
Drawable btn = new ColorDrawable(Color.argb(80,3,218,197));
AlphaAnimation alphaAnimation1 = new AlphaAnimation(0.1f, 1.0f);
alphaAnimation1.setDuration(500);
alphaAnimation1.setRepeatCount(Animation.INFINITE);
alphaAnimation1.setRepeatMode(Animation.RESTART);
alphaAnimation1.setStartTime(500);
//view.setAnimation(alphaAnimation1);
//alphaAnimation1.start();
if(lm == 0){
binding.btnLm1.setForeground(null);
binding.btnLm2.setForeground(null);
}
if(lm == 1){
binding.btnLm1.setForeground(btn);
binding.btnLm2.setForeground(null);
}
if(lm == 2){
binding.btnLm1.setForeground(null);
binding.btnLm2.setForeground(btn);
}
if(fm == 0){
binding.btnFm1.setForeground(null);
binding.btnFm2.setForeground(null);
}
if(fm == 1){
binding.btnFm1.setForeground(btn);
binding.btnFm2.setForeground(null);
}
if(fm == 2){
binding.btnFm1.setForeground(null);
binding.btnFm2.setForeground(btn);
}
binding.btnBlon.setForeground(blon ? btn : null);
binding.btnBl.setForeground(breakon ? btn : null);
binding.btnRl.setForeground(ReOn ? btn : null);
binding.btnL.setForeground(Lon ? btn : null);
binding.btnW.setForeground(warning ? btn : null);
binding.btnR.setForeground(Ron ? btn : null);
binding.btnBee.setForeground(beeon ? btn : null);
binding.btnL1.setForeground(l1on ? btn : null);
binding.btnL2.setForeground(l2on ? btn : null);
binding.btnL3.setForeground(l3on ? btn : null);
binding.btnL4.setForeground(l4on ? btn : null);
binding.btnL5.setForeground(l5on ? btn : null);
}
}