android 蓝牙 单片机 显示波形

更新时间:2023-08-20 12:02:01 阅读量: 高等教育 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

通过单片机连接的蓝牙模块发送数据,手机也是通过蓝牙接收,并在屏幕上完成绘画。

(希望可以上传成功)

写在前面:

最近一个月,自己尝试了开发一款手机应用,通过单片机的串口

连接蓝牙模块发送数据,然后在手机端通过手机蓝牙收取数据,

并将数据显示出来,其实这就是一个串口通信要做的事情,但是

我要做的除此之外,还要将数据通过图像画出来。即是要求单片

机通过AD采集模拟信号把转换出来的数字量通过蓝牙发送,在

手机端将采集到的波形画出来。此文主要涉及蓝牙操作,画图,

等。也给爱好单片机的一份参考。关于单片机连接的蓝牙是一个

淘宝上买的模块,推荐个网址:

我是在这里买的,其中店家会给资料说明。

By ways (没有抄袭任何人的原作,只是查找资料,看别人程序,

自己开发,很大一部分来自androidsdk/doc/index.xml,因为自己

深感网上资料冗杂,在此整理,借助百度文库的话,让每个人能

有平等提升自己的机会,希望像我的android启蒙老师mars一样

提供给大家免费的资料,讨厌那些需要积分的网站,比如程序员

联合开发网,比如CSDN,因为没有积分,好几个看上的资源都

不能下载。个人微博:/u/2698171342)

通过单片机连接的蓝牙模块发送数据,手机也是通过蓝牙接收,并在屏幕上完成绘画。

程序目前问题:我承认自己现阶段的程序存在bug,但已经基本

完成,关于最后的退出处理,会抛出异常,这个我还没解决,不

过貌似应该是没有结束线程出现的问题;还有关于程序我几乎全

部使用全局变量,因为比较简单,便于操作,所以,呵呵,但是

我知道这样肯定不好;另外我本想用几个不同的activity和

service但是在其中的通信中用到广播,消息队列等,但我一直没

有收到数据,所以最后不得已全部在一个activity中实现,这个

表示初学也有很大困难,本例为我后者程序,也就是版本二吧。

我会改善这些问题,因为我希望自己可以追求完美。

先展示结果:

图一是最后的图,图二只是演示可以画出正弦波,其中按钮开启

蓝牙是直接默认连接一个设备,按钮sin是开始接受蓝牙数据并

画图,图示中由于没有开蓝牙,故一直接收数据一直为0 所以

是画出的一条直线,关于清除,是因为在画布下方还有一个

TextView空间使用来显示接受到的数据的。

通过单片机连接的蓝牙模块发送数据,手机也是通过蓝牙接收,并在屏幕上完成绘画。

图一 图二

关于程序:

其中的注释我已经写的很清楚

在manifest中,添加蓝牙权限:

</application>

<uses-permission

android:name="android.permission.BLUETOOTH_ADMIN" />

</manifest>

在main.xml:

<?xml version="1.0" encoding="utf-8"?>

通过单片机连接的蓝牙模块发送数据,手机也是通过蓝牙接收,并在屏幕上完成绘画。

xmlns:android="/apk/res/android"

android:layout_width="fill_parent"

android:layout_height="fill_parent"

android:orientation="vertical" >

<TextView

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:text="@string/hello" />

<LinearLayout

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:orientation="horizontal"> <Button

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:id="@+id/button"

android:text="@string/button" />

<Button

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:id="@+id/sin"

android:text="@string/sin" />

<Button

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:id="@+id/clear"

android:text="@string/clear" />

</LinearLayout>

<SurfaceView

android:id="@+id/show"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

/>

<TextView

android:id="@+id/myview"

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:text="@string/myview" />

/>

通过单片机连接的蓝牙模块发送数据,手机也是通过蓝牙接收,并在屏幕上完成绘画。

在strings.xml:

<?xml version="1.0" encoding="utf-8"?>

<resources>

<string name="hello">Hello World, OscilloscopeActivity!</string>

<string name="app_name">Oscilloscope</string>

<string name="button">开启蓝牙</string>

<string name="myview">参数显示:</string>

<string name="sin">sin</string>

<string name="buttonstart">搜索</string>

<string name="buttonselect">选择设备</string>

<string name="buttonsend">发送</string>

<string name="input">please input</string>

<string name="clear">清除</string>

</resources>

在Oscilloscope.java

package com.ways;

import java.io.IOException;

import java.io.InputStream;

import java.io.OutputStream;

import java.util.Iterator;

import java.util.Set;

import java.util.Timer;

通过单片机连接的蓝牙模块发送数据,手机也是通过蓝牙接收,并在屏幕上完成绘画。

import java.util.TimerTask;

import java.util.UUID;

import android.app.Activity;

import android.bluetooth.BluetoothAdapter;

import android.bluetooth.BluetoothDevice;

import android.bluetooth.BluetoothSocket;

import android.content.Intent;

import android.graphics.Canvas;

import android.graphics.Color;

import android.graphics.Paint;

import android.graphics.Rect;

import android.os.Bundle;

import android.os.Handler;

import android.os.Message;

import android.view.Menu;

import android.view.MenuItem;

import android.view.SurfaceHolder;

import android.view.SurfaceView;

import android.view.View;

import android.view.SurfaceHolder.Callback;

import android.view.View.OnClickListener;

通过单片机连接的蓝牙模块发送数据,手机也是通过蓝牙接收,并在屏幕上完成绘画。

import android.widget.Button;

import android.widget.TextView;

public class Oscilloscope extends Activity {

/** Called when the activity is first created. */

final int HEIGHT=320; //设置画图范围高度

final int WIDTH=450; //画图范围宽度

final int X_OFFSET = 5; //x轴(原点)起始位置偏移画图范围一点

private int cx = X_OFFSET; //实时x的坐标

int centerY = HEIGHT /2; //y轴的位置

TextView myview = null; //画布下方显示获取数据的地方

final UUID uuid =

UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");

//uuid 此为单片机蓝牙模块用

//还有其他的uuid,这个可以再百度查到,暂不清楚其中的差别

final BluetoothAdapter mBluetoothAdapter =

BluetoothAdapter.getDefaultAdapter();

//获取本手机的蓝牙适配器

static int REQUEST_ENABLE_BT = 1; // 一个常量而已,开启蓝牙时使用

BluetoothSocket socket = null; //用于数据传输的socket

int READ = 1; //一个常量,用于传输数据消息队列的

通过单片机连接的蓝牙模块发送数据,手机也是通过蓝牙接收,并在屏幕上完成绘画。

识别字

public ConnectedThread thread = null; //连接蓝牙设备线程

static int temp = 0; //临时变量用于保存接收到的数据

private SurfaceHolder holder = null; //画图使用,可以控制一个

SurfaceView

private Paint paint = null; //画笔

SurfaceView surface = null; //

Timer timer = new Timer(); //一个时间控制的对象,用于控制实时的

x的坐标,

//使其递增,类似于示波器从前到后扫描

TimerTask task = null; //时间控制对象的一个任务

/* 关于画图类的几点说明

* SurfaceView 是View的继承类,这个视图里

* 内嵌了一个专门用于绘制的Surface。可以控制这个Surface的格式和尺

寸。

* SurfaceView控制这个Surface的绘制位置。

*

* 实现过程:继承SurfaceView并实现SurfaceHolder.Callback接口------>

* SurfaceView.getHolder()获得SurfaceHolder对象

----->SurfaceHolder.addCallback(callback)

* 添加回调函数----->surfaceHolder.lockCanvas()获得Canvas对象并锁

通过单片机连接的蓝牙模块发送数据,手机也是通过蓝牙接收,并在屏幕上完成绘画。

定画布------>

* Canvas绘画------->SurfaceHolder.unlockCanvasAndPost(Canvas

canvas)结束锁定画图,

* 并提交改变,将图形显示。

*

* 这里用到了一个类SurfaceHolder,可以把它当成surface的控制器,

* 用来操纵surface。处理它的Canvas上画的效果和动画,控制表面,大

小,像素等

*

* 其中有几个常用的方法,锁定画布,结束锁定画布

* */

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(yout.main);

myview = (TextView)findViewById(R.id.myview); //获取控件对象

Button bluetooth =(Button)findViewById(R.id.button);

Button sin =(Button)findViewById(R.id.sin);

Button clear =(Button)findViewById(R.id.clear);

surface = (SurfaceView)findViewById(R.id.show);

//初始化SurfaceHolder对象

通过单片机连接的蓝牙模块发送数据,手机也是通过蓝牙接收,并在屏幕上完成绘画。

holder = surface.getHolder();

holder.setFixedSize(WIDTH+50, HEIGHT+100); //设置画布大小,要

比实际的绘图位置大一点

paint = new Paint();

paint.setColor(Color.GREEN); //画波形的颜色是绿色的,区别于坐标

轴黑色

paint.setStrokeWidth(3);

bluetooth.setOnClickListener(new MyButtonListener());

//添加按钮监听器 开启蓝牙 开启连接通信线程

clear.setOnClickListener(new MyButtonClearListener());

//添加按钮监听器 清除TextView内容

holder.addCallback(new Callback() { //按照上面注释,添加回调函数

public void surfaceChanged(SurfaceHolder holder,int format,int

width,int height){

drawBack(holder);

//如果没有这句话,会使得在开始运行程序,整个屏幕没有白

色的画布出现

//直到按下按键,因为在按键中有对drawBack(SurfaceHolder

holder)的调用

}

public void surfaceCreated(SurfaceHolder holder) {

通过单片机连接的蓝牙模块发送数据,手机也是通过蓝牙接收,并在屏幕上完成绘画。

// TODO Auto-generated method stub

}

public void surfaceDestroyed(SurfaceHolder holder) {

// TODO Auto-generated method stub

}

});

//添加按钮监听器 开启画图线程

sin.setOnClickListener(new OnClickListener(){

public void onClick(View v) { } // TODO Auto-generated method stub new DrawThread().start(); //线程启动

});

}

public void onClick(View v) { class MyButtonListener implements OnClickListener{

通过单片机连接的蓝牙模块发送数据,手机也是通过蓝牙接收,并在屏幕上完成绘画。

// TODO Auto-generated method stub //如果没有打开蓝牙,此时打开蓝牙 if (!mBluetoothAdapter.isEnabled()) { enableBtIntent = new Intent

Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);

startActivityForResult(enableBtIntent,

REQUEST_ENABLE_BT);

} //此处我已经知道我对应的蓝牙模块的地址,所以省去扫描,配对的

过程,

//如果从头开始的话,需要添加很多内容。 /* * 这是获得在手机中已经存储的已经配对过的蓝牙信息 * Set<BluetoothDevice>devices =

mBluetoothAdapter.getBondedDevices();

iterator.next();

System.out.println(bluetoothdevice.getAddress()); myview.append( "\n"+bluetoothdevice.getAddress()); //这里就是获取地址 当然这里又不止一个信息,所有配对的信息if(devices.size()>0){ for(Iterator iterator = devices.iterator();iterator.hasNext();){ BluetoothDevice bluetoothdevice = (BluetoothDevice)

通过单片机连接的蓝牙模块发送数据,手机也是通过蓝牙接收,并在屏幕上完成绘画。

都有,

* 你要选择蓝牙模块的地址,其实地址只是一个说法,它实际

上是一个虚拟通道,

}*/

BluetoothDevice device = * 我也不太清楚,就称之为地址了 }

mBluetoothAdapter.getRemoteDevice("20:13:03:18:10:09");

try { socket = device.createRfcommSocketToServiceRecord(uuid);

//建立连接

BluetoothAdapter.getDefaultAdapter().cancelDiscovery(); //取消搜索蓝牙设备,不写也罢,因为我压根没搜索 socket.connect(); //建立连接,如果连接成功,此时蓝牙模块中的显示当前连接状

态的指示灯就不会闪烁了,

//此时意味着连接成功了 } catch (IOException e) { } thread = new ConnectedThread(socket); //开启通信的线程 thread.start(); e.printStackTrace();

通过单片机连接的蓝牙模块发送数据,手机也是通过蓝牙接收,并在屏幕上完成绘画。

}

}

} class MyButtonClearListener implements OnClickListener{ public void onClick(View v) { } // TODO Auto-generated method stub myview.setText(""); Handler handler = new Handler() { //这是处理消息队列的Handler对象 @Override public void handleMessage(Message msg) { //处理消息 if (msg.what==READ) { String str = (String)msg.obj; //类型转化 myview.append(" "+str); //显示在画布下方的TextView中

通过单片机连接的蓝牙模块发送数据,手机也是通过蓝牙接收,并在屏幕上完成绘画。

}; /* } } super.handleMessage(msg); * 关于此类,因为我的手机端只需要从蓝牙读取数据,不需要发送,所以

就不必实现输出流

* 其实相比起来,输出流更简单 * */ private class ConnectedThread extends Thread { private final BluetoothSocket mmSocket;

private final InputStream mmInStream;

private final OutputStream mmOutStream;

//构造函数

public ConnectedThread(BluetoothSocket socket) {

mmSocket = socket;

InputStream tmpIn = null;

OutputStream tmpOut = null;

通过单片机连接的蓝牙模块发送数据,手机也是通过蓝牙接收,并在屏幕上完成绘画。

// Get the input and output streams, using temp objects because

// member streams are final

try {

tmpIn = socket.getInputStream(); //获取输入流

tmpOut = socket.getOutputStream(); //获取输出流

} catch (IOException e) { }

mmInStream = tmpIn;

mmOutStream = tmpOut;

}

public void run() {

byte[] buffer = new byte[1024]; // buffer store for the stream

int bytes; // bytes returned from read()

// Keep listening to the InputStream until an exception occurs

while (true) {

try {

// Read from the InputStream

bytes = mmInStream.read(buffer); //bytes数组返回值,为

buffer数组的长度

// Send the obtained bytes to the UI activity

通过单片机连接的蓝牙模块发送数据,手机也是通过蓝牙接收,并在屏幕上完成绘画。

String str = new String(buffer);

temp = byteToInt(buffer); //用一个函数实现类型转化,

从byte到int

handler.obtainMessage(READ, bytes, -1, str)

.sendToTarget(); //压入消息队列

} catch (Exception e) {

System.out.print("read error");

break;

}

}

}

}

public void run() { // TODO Auto-generated method stub drawBack(holder); //画出背景和坐标轴 //绘图线程,实时获取temp 数值即是y值 public class DrawThread extends Thread {

if(task != null){

通过单片机连接的蓝牙模块发送数据,手机也是通过蓝牙接收,并在屏幕上完成绘画。

task.cancel();

}

task = new TimerTask() { //新建任务

@Override

public void run() {

//获取每一次实时的y坐标值

//如果不注释,则是画出正弦波

//int cy = centerY -(int)(100 * Math.sin((cx -5) *2 *

Math.PI/150));

int cy = centerY + temp; //实时获取的temp数值,因为对于

画布来说

//最左上角是原点,所以我要到y值,需要从画布中间开始

计数

Canvas

Rect(cx,cy-2,cx+2,cy+2));

//锁定画布,只对其中Rect(cx,cy-2,cx+2,cy+2)这块区域

做改变,减小工程量

canvas.drawPoint(cx, cy, paint); //打点 canvas = holder.lockCanvas(new

cx++; //cx 自增, 就类似于随时间轴的图形 holder.unlockCanvasAndPost(canvas); //解锁画布

if(cx >=WIDTH){

通过单片机连接的蓝牙模块发送数据,手机也是通过蓝牙接收,并在屏幕上完成绘画。

cx=5; //如果画满则从头开始画 drawBack(holder); //画满之后,清除原来的图像,从新开始

}

}

};

timer.schedule(task, 0,1); //隔1ms被执行一次该循环任务画出图形

//简单一点就是1ms画出一个点,然后依次下去

} }

//设置画布背景色,设置XY轴的位置

private void drawBack(SurfaceHolder holder){

Canvas canvas = holder.lockCanvas(); //锁定画布

//绘制白色背景

canvas.drawColor(Color.WHITE);

Paint p = new Paint();

p.setColor(Color.BLACK);

p.setStrokeWidth(2);

通过单片机连接的蓝牙模块发送数据,手机也是通过蓝牙接收,并在屏幕上完成绘画。

//绘制坐标轴

canvas.drawLine(X_OFFSET, centerY, WIDTH, centerY, p); //绘制X轴 前四个参数是起始坐标

canvas.drawLine(X_OFFSET, 20, X_OFFSET, HEIGHT, p); //绘制Y轴 前四个参数是起始坐标

holder.unlockCanvasAndPost(canvas); //结束锁定 显示在屏幕上 holder.lockCanvas(new Rect(0,0,0,0)); //锁定局部区域,其余地方不做改变

holder.unlockCanvasAndPost(canvas);

}

//数据转化,从byte到int

/*

* 其中 1byte=8bit,int = 4 byte,

* 一般单片机比如c51 8位的 MSP430 16位 所以我只需要用到后两个byte就ok

* */

public static int byteToInt(byte[] b){

} return (((int)b[0])+((int)b[1])*256);

通过单片机连接的蓝牙模块发送数据,手机也是通过蓝牙接收,并在屏幕上完成绘画。

public boolean onCreateOptionsMenu(Menu menu) {

// TODO Auto-generated method stub menu.add(0,1, 1, "exit");// 添加menu菜单一个item //第一个参数是菜单所在组的名字,组的id,第二个是item的id ,第三个是item

}

//当按下菜单时,选择其中一个item会调用下函数 //最后一个是item显示的内容。 return super.onCreateOptionsMenu(menu);

@Override public boolean onMenuItemSelected(int featureId, MenuItem item) { } // TODO Auto-generated method stub finish(); return super.onMenuItemSelected(featureId, item);

@Override

protected void onStop() { // TODO Auto-generated method stub super.onStop();

本文来源:https://www.bwwdw.com/article/yv3j.html

Top