轉載請註明出處。
這次是一個課程設計,利用單片機開發一個物聯網系統。我們利用了手機藍牙與單片機板子上的藍牙通信,通過 控制信號來控制單片機上led燈的亮滅和定時。
網上有很多的搜索藍牙的例程,大家可以自己去看,由於本次我們是與特定的設備連接,因此直接使用Mac地址連接,不在使用搜索功能,當然如果大家採用搜索到設備後在連接也可以。我們將藍牙連接和數據收發放在一個service中,由於藍牙socket讀是阻塞的,因此我們新開一個線程專門用於接收板子的信號。
在service的onCreate()方法中,我們連接指定的藍牙,並獲得其io流,在新開一個線程用於socket讀。
記得在mk文件中給該service添加intent-filter。
/**
* 服務初始化
*/
@Override
public void onCreate() {
// TODO 自動生成的方法存根
initBluetooth();
super.onCreate();
}
/**
* 初始化藍牙適配器
*/
public void initBluetooth(){
bluetoothAdapter=BluetoothAdapter.getDefaultAdapter();
if(bluetoothAdapter==null){//等於null時通知主界面,設備不支持藍牙
Intent intent = new Intent();
intent.setAction(Constants.ERROR);
sendBroadcast(intent);
}else {
if(!bluetoothAdapter.isEnabled())//藍牙未開啓時,開啓藍牙
{
bluetoothAdapter.enable();
}
connectDevice();
}
}
/**
* 鏈接設備
*/
private void connectDevice(){
device = bluetoothAdapter.getRemoteDevice(Constants.ADDRESS);//輸入要連接的藍牙的Mac地址,在說明書上可以查到
if(device==null){//爲空,連接失敗
Intent intent = new Intent();
intent.setAction(Constants.ERROR);
sendBroadcast(intent);
}else {
try {
socket = device.createRfcommSocketToServiceRecord(Constants.MY_UUID);//uuid,一般爲00001101-0000-1000-8000-00805F9B34FB
socket.connect();//獲得socket接口
inputStream = socket.getInputStream();//獲得輸入流,另起線程監聽輸入
receiveThread = new ReceiveThread(inputStream);
new Thread(receiveThread).start();
outputStream = socket.getOutputStream();
Intent intent = new Intent();//發送廣播,已連接
intent.setAction(Constants.CONNECTED);
sendBroadcast(intent);
} catch (IOException e) {
// TODO 自動生成的 catch 塊
e.printStackTrace();
}
}
}
/**
* 接收反饋信號的線程
* 判斷反饋信號,更新主界面ui
* @author qian ren
*
*/
class ReceiveThread implements Runnable{
InputStream in;
int msg;
public ReceiveThread(InputStream in){
this.in=in;
}
@Override
public void run() {
// TODO 自動生成的方法存根
while(true){
try {
msg=in.read();
System.out.println("msg: "+msg);
switch (msg) {
case 1://發送廣播,檯燈打開
Intent intentOn = new Intent();
intentOn.setAction(Constants.ON);
sendBroadcast(intentOn);
break;
case 2://發送廣播,檯燈關閉
Intent intentOff = new Intent();
intentOff.setAction(Constants.OFF);
sendBroadcast(intentOff);
break;
case 3://發送廣播,定時完成
Intent intentTimer = new Intent();
intentTimer.setAction(Constants.TIMER);
sendBroadcast(intentTimer);
break;
default:
break;
}
} catch (IOException e) {
// TODO 自動生成的 catch 塊
e.printStackTrace();
}
}
}
}
初始化完成後,在onStartCommond()方法中發送控制信號給板子。
public int onStartCommand(Intent intent, int flags, int startId) {
// TODO 自動生成的方法存根
String control;
control = intent.getStringExtra("control");
if(control.equals("on")){//打開臺燈
byte [] buffer = new byte[]{1,25,1,2};
try {
outputStream.write(buffer);
outputStream.flush();
System.out.println("on");
} catch (IOException e) {
// TODO 自動生成的 catch 塊
e.printStackTrace();
}
}else if (control.equals("off")) {//關閉檯燈
byte [] buffer = new byte[]{1,26,1,2};
try{
outputStream.write(buffer);
outputStream.flush();
System.out.println("off");
}catch(IOException e){
e.printStackTrace();
}
}else if (control.equals("timer")) {//定時
byte hour = (byte) intent.getIntExtra("hour",0);
byte minute = (byte) intent.getIntExtra("minute",1);
byte [] buffer =new byte[]{1,hour,minute,2};
try{
outputStream.write(buffer);
outputStream.flush();
System.out.println("timer");
}catch(IOException e){
e.printStackTrace();
}
}
return super.onStartCommand(intent, flags, startId);
}
現在,service已經寫完了,我們再來寫主界面,主界面有一個Switcher來轉換燈的開關狀態,並有一個按鈕可以跳轉到另一個activity實現定時。還有一個imageview通過不斷更換背景來展示燈的亮滅。如圖所示
下面是關燈開燈的邏輯實現,通過啓動service來實現信號的發送。
首先是連接設備:
/**
* 鏈接設備
*/
private void connectDevice() {
// TODO 自動生成的方法存根
Intent intent = new Intent();
intent.setAction(Constants.LAMP_SERVICE);
intent.putExtra("control", "connect");
startService(intent);
}
class SwitchChangedListener implements OnCheckedChangeListener{
@Override
public void onCheckedChanged(CompoundButton buttonView,
boolean isChecked) {
// TODO 自動生成的方法存根
if(isChecked){
Intent intent = new Intent();
intent.setAction(Constants.LAMP_SERVICE);
intent.putExtra("control", "on");
startService(intent);
}else {
Intent intent = new Intent();
intent.setAction(Constants.LAMP_SERVICE);
intent.putExtra("control", "off");
startService(intent);
}
}
}
發送消息後必然會接受到反饋,我們用broadcastreceiver來接受service收到的反饋信號。記得在oncreate方法中綁定receiver。
/**
* 更新界面的broadcastreceiver
* @author qian ren
*
*/
class UpdateUiReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
// TODO 自動生成的方法存根
String action = intent.getAction();
if(action.equals(Constants.CONNECTED)){
progressBar.setVisibility(View.INVISIBLE);
ToastDisplay("檯燈已經連接");
System.out.println("device has been connected");
}else if (action.equals(Constants.ERROR)) {
progressBar.setVisibility(View.INVISIBLE);
ToastDisplay("未連接到檯燈,請檢查設備");
System.out.println("device can't connect");
}else if (action.equals(Constants.ON)) {
background.setImageResource(R.drawable.lamp_on);
ToastDisplay("檯燈已經打開");
System.out.println("lamp on");
}else if (action.equals(Constants.OFF)) {
background.setImageResource(R.drawable.lamp_off);
ToastDisplay("檯燈已經關閉");
System.out.println("lamp off");
}else if (action.equals(Constants.TIMER)) {
background.setImageResource(R.drawable.lamp_on);
ToastDisplay("檯燈將在"+hour+"小時"+minute+"分後自動關閉 ");
System.out.println("timing has been finished");
}
}
}
定時功能在另一個activity中實現,通過點擊鬧鐘圖標跳到另一個activity。
定時功能比較簡單,使用timerpicker。
首先監聽timechange的回調方法得到定時的時間。
public void onTimeChanged(TimePicker view, int hourOfDay, int minute) {
// TODO 自動生成的方法存根
this.hour = hourOfDay;
this.minute = minute;
}
然後在點擊確定鍵時將信號發送給service。
public void onClick(View v) {
// TODO 自動生成的方法存根
if(hour==0){
hour=countDown.getCurrentHour();
}else if (minute==0) {
minute=countDown.getCurrentMinute();
}
Intent intent = new Intent();
intent.setAction(Constants.LAMP_SERVICE);
intent.putExtra("control", "timer");
intent.putExtra("hour", hour);
intent.putExtra("minute", minute);
startService(intent);
Intent intentBack = new Intent();
intentBack.putExtra("hour", hour);
intentBack.putExtra("minute", minute);
TimerActivity.this.setResult(Constants.RESPONSE,intentBack);
this.finish();
}
上面的setresult是爲了在主界面得到設置的時間,進而刷新UI,因此主界面可以使用startactivityforresult來啓動該定時界面。
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
// TODO 自動生成的方法存根
if(requestCode==Constants.REQUEST && resultCode ==Constants.RESPONSE){
hour = data.getIntExtra("hour", 0);
minute = data.getIntExtra("minute", 1);
}
}