手机通过蓝牙和单片机通讯控制乐高车模的LED灯

介绍

这是一个通过蓝牙和单片机控制汽车模型(特别是乐高的汽车模型)上的灯饰的C语言工程和安卓App

可以较为逼真地模拟现实的灯光控制

工程使用的硬件:

  • STC12C5A60S2 最小系统板
  • ATK-HC05 主从一体蓝牙模块

单片机和蓝牙模块通过串口通讯

目前需要16个IO口,具体绑定的IO口可详见工程

手机安装串口软件,蓝牙连接模块即可发送指令控制

指令详见工程

可以控制前照灯、雾灯、转向灯、刹车灯、倒车灯及其他灯光

预留五个位置扩展灯位

实物示例

乐高科技组车模亮灯工程-拟真向-蓝牙控制

手机通过蓝牙和单片机通讯控制乐高车模的LED灯

手机控制界面

手机通过蓝牙和单片机通讯控制乐高车模的LED灯

项目地址

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);
    }




}

参考

蓝牙控制单片机LED 灯的亮灭

上一篇:Binding基础


下一篇:vue自定义指令----directive