专案动机(1/2)

.现今社会中,各种交通运输载具方便了人类的生活,缩小了地域的差异性,当中车辆是人们日常生活中最频繁接触到的一部分。
.车辆使人们的行动更加便利,也因此道路上行驶的车辆越来越多。
.路况的复杂性也因此添加,行车安全的问题日趋重要。

.道路上危急事故的产生,除了是因为驾驶人本身道路安全观念不足造成,也可能是因为驾驶人本身对车辆的控制力不够而造成危急。

专案动机(2/2)

.在驾驶人不具备职业车手之技能与意识的前提下。要维持对车体良好安定的控制。最稳妥的条件就是保持安全稳定的车速。以避免不可抗力之意外发生。

.因此驾驶人须要能随时迅速的得知车辆的实际车速,而非单纯凭藉人体对速度的感觉来操控车辆。
.考虑到二轮机车通常不具备挡风玻璃来实装抬头显示器(HUD)以显示车速,本专题将实作能显示即时车速的智慧安全帽,降低视线转移至仪表板以及身体重心变化而发生危急的机会。

系统功能

1.以行动装置得到GPS定位数据并算出即时车速
2.于安全帽中显示即时车速
3.车速超过70km时发出危急警报

硬体清单

1.四分之三安全帽 * 1
2.Arduino UNO * 1
3.蓝芽模组(HC-06从机) *1
4.Android手机 * 1
5.74HC595串列式七段显示器模组 * 1
6.蜂鸣器 *1
7.USB(Type A)母接头 *1
8.镜面反射膜 *1
9.各种线材与废物利用

GPS測速

.利用行动装置的GPS定位依据车辆行驶中不断变化位置,持续得到某两个点的位置数据以及两点之间的时间差来计算出行车速度数据。(採用Android
API)

.在卫星讯号稳定、车辆稳定行驶、路面斜率不大的情况下,GPS所測得速度差点儿等于真实速度。

GPS測速与仪表板之误差

.在实际測试中,车辆稳定行驶的情况下,当车速维持30~40km/h时。測试用机车之仪表板所显示速度比GPS定位測得之车速平均高7 
km/h左右。

.原因:

         依据台湾交通部车辆安全检測基准,以无载条件(空车重+驾驶员+必要仪器)且依下列速率測试。指示速率必须永不少于真实速率且速率计标度盘指示之速率(V1)与真实速率(V2)间应满足关系:

          0 <= ( V1 - V2 ) <= ( V2 / 10) +4 km/h

.故车速越快,在同意范围内的误差就可能越大。



即时车速显示

.行动装置上之Android程式计算出的车速通过蓝芽模组将数据传送给Arduino。
.採用74HC595串列式七段显示器模组显示数据,于安全帽内(考虑到防雨)投射速度数据于反射膜以反射给眼睛。

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQveWVoam9yZGFu/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" width="302" height="160" alt="">

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQveWVoam9yZGFu/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" width="214" height="167" alt="">

上方图示为HC-06蓝芽模组(左)、74HC595串列式七段显示器模组(右)




上图为安全帽内部效果展示

反射投影

.当物体距离眼睛太近。便无法准确使物体在视网膜上清晰成像。
.为了能清楚看到物体。必须延长物距。
.安全帽内空间有限,镜面反射成像是简单有效的方案。

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQveWVoam9yZGFu/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="">

透过反射投影,在安全帽有限空间内理想视距最大可达到两倍

系统架构

装置电路图

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQveWVoam9yZGFu/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="">

遭遇问题

.因为安全帽挡风镜非平面。若将反射膜直接贴于挡风镜上。以反射式投影会使影像看起来不平整。
.解决方案:

         为反射膜制作独立基座,使反射膜保持平整,并与视线尽可能保持垂直。

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQveWVoam9yZGFu/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="">

装置组装完毕图

实际展示影片

Youtube:

http://youtu.be/qU20tuYm1ZA

优酷:

http://v.youku.com/v_show/id_XNzI5MzE2Njcy.html

(因为反射膜为半透明非全反射,会透光。因此从外部所看到七段显示器的数字会是颠倒的。)

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

源代碼

最后附上本专案所用到的源码,project我就不打包了。以下已经差点儿包含所有的东西



Android 行动装置端主程序

package com.helmethud;

import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.UUID; import android.app.Activity;
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.location.Criteria;
import android.location.GpsSatellite;
import android.location.GpsStatus;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.location.LocationProvider;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast; public class HelmetHud extends Activity { // 除錯用標記
private static final String TAG = "HELMETHUDCLIENT";
private static final boolean D = true; // Bluetooth相關變量
private BluetoothAdapter mBluetoothAdapter = null;
private BluetoothSocket btSocket = null;
private OutputStream outStream = null;
// Well known SPP UUID (will *probably* map to
// RFCOMM channel 1 (default) if not in use);
// see comments in onResume().
private static final UUID MY_UUID = UUID
.fromString("00001101-0000-1000-8000-00805F9B34FB"); // ==> hardcode bluetooth server's MAC address here <==
// private static String address = "98:D3:31:40:03:34";
private boolean BTIsConnected = false; // 藍芽是否已連線flag
private boolean hasFirstMatch = false; // 藍芽是否已經過第一次配對flag
private boolean ReceiverIsRegisted = false; // BroadcastReceiver是否已註册flag // 線程HANDLE相關變量
private Handler connectBTHandler = new Handler();
private Handler sendBTMsgHandler = new Handler();
private Handler sendBTTestMsgHandler = new Handler();
private Handler hudThreadHandler = new Handler();
private Handler getSpeedThreadHandler = new Handler();
Handler msgHandler; // UI相關變量
private Button btnDiscovery, btnConnect, btnSend, btnStartHud;
private Spinner spinner;
private ArrayAdapter<String> arrayAdapterDeviceName;
private static final String[][] devicesStr = { { "Plz select device...", "" } };
private List<String> allDevices, allDevicesAddress;
private int selectedAddress = 0; // GPS定位相關變量
final String SERVERNAME = Context.LOCATION_SERVICE;
LocationManager locationManager;
float speed = 0, lastSpeed = 0; // 車速
int satelliteCount = 0; // 當前衛星個數
Date gpdDate = new Date();
StringBuilder tempBuilder = new StringBuilder();
TextView tvGPSState, tvGPSInfo;
private static final int STATIC_CONTINUED_TIME = 6000; // 判斷靜止持續時間(毫秒) EditText et1; // 测試用輸入框 private boolean IsTesting = false; // 程序執行是否為發送测試訊息
int sum = 0; // SendMsgThread傳送藍芽訊息線程執行總次數
private static final int BT_RECONNECT_LIMIT_TIME = 300; // 線程最大允許執行次數;值訂為300以防止低階設備效能不足而出錯
private static final int NEED_BT_RECONNECT_DEVICE_MAX_SDK = 13; // 须要又一次連線的裝置Android_SDK_API最大版本号 /** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); setContentView(R.layout.activity_helmet_hud);
setTitle("Not connected"); et1 = (EditText) findViewById(R.id.editText1);
btnDiscovery = (Button) findViewById(R.id.btnDiscovery);
btnConnect = (Button) findViewById(R.id.btnConnect);
btnSend = (Button) findViewById(R.id.btnSend);
spinner = (Spinner) findViewById(R.id.spinner1);
allDevices = new ArrayList<String>();
for (int i = 0; i < devicesStr.length; i++) {
allDevices.add(devicesStr[i][0]);
}
allDevicesAddress = new ArrayList<String>();
for (int i = 0; i < devicesStr.length; i++) {
allDevicesAddress.add(devicesStr[i][1]);
}
/* new ArrayAdapter物件並將allDevices傳入 */
arrayAdapterDeviceName = new ArrayAdapter<String>(this,
android.R.layout.simple_spinner_item, allDevices);
arrayAdapterDeviceName
.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner.setAdapter(arrayAdapterDeviceName);
btnStartHud = (Button) findViewById(R.id.btnStartHud);
// btnStartHud.setEnabled(false); //测試時保持可用
tvGPSState = (TextView) findViewById(R.id.tvGPSState);
tvGPSInfo = (TextView) findViewById(R.id.tvGPSInfo); btnDiscovery.setOnClickListener(new OnClickListener() { @Override
public void onClick(View arg0) {
// TODO Auto-generated method stub
if (mBluetoothAdapter.isDiscovering()) {
mBluetoothAdapter.cancelDiscovery();
Log.e(TAG, "Cancel discovery");
} mBluetoothAdapter.startDiscovery(); // 开始搜尋裝置 }
}); btnConnect.setOnClickListener(new OnClickListener() { @Override
public void onClick(View v) {
// TODO Auto-generated method stub
connectBTHandler.post(ConnectBTThread);
}
}); btnSend.setOnClickListener(new OnClickListener() { @Override
public void onClick(View arg0) {
// TODO Auto-generated method stub
IsTesting = true;
sendBTTestMsgHandler.post(SendBTTestMsgThread);
}
}); spinner.setOnItemSelectedListener(new OnItemSelectedListener() { @Override
public void onItemSelected(AdapterView<?> parent, View view,
int position, long id) {
// TODO Auto-generated method stub
parent.setVisibility(View.VISIBLE);
selectedAddress = position;
} @Override
public void onNothingSelected(AdapterView<?> arg0) {
// TODO Auto-generated method stub }
}); btnStartHud.setOnClickListener(new OnClickListener() { @Override
public void onClick(View v) {
// TODO Auto-generated method stub
IsTesting = false;
hudThreadHandler.post(HudThread);
btnStartHud.setEnabled(false);
}
}); msgHandler = new Handler() { @Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (msg.what == 0) { // 結束發送测試訊息線程並又一次連接藍芽以重置(避免發送訊息過多而當機)
sendBTTestMsgHandler.removeCallbacks(SendBTTestMsgThread);
Log.e(TAG, "removecallbacks!");
// 關閉藍芽通道
if (outStream != null) {
try {
outStream.flush();
outStream = null;
} catch (IOException e) {
Log.e(TAG,
"ON PAUSE: Couldn't flush output stream.",
e);
}
}
try {
if (BTIsConnected) {
BTIsConnected = false;
btSocket.close();
btSocket = null;
}
} catch (IOException e2) {
Log.e(TAG, "ON PAUSE: Unable to close socket.", e2);
}
// 又一次連線藍芽
sum = 0;
Log.e(TAG, "Reconnecting...");
connectBTHandler.post(ConnectBTThread);
} else if (msg.what == 1) { // 開啟發送测試訊息線程
Log.e(TAG, "post");
sendBTTestMsgHandler.post(SendBTTestMsgThread);
} else if (msg.what == 2) { // 又一次連線藍芽
Log.e(TAG, "Reconnecting...");
connectBTHandler.post(ConnectBTThread);
} }
}; // TODO: Bluetooth Initial
if (D)
Log.e(TAG, "+++ ON CREATE +++"); mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (mBluetoothAdapter == null) {
Toast.makeText(this, "Bluetooth is not available.",
Toast.LENGTH_LONG).show();
finish();
return;
} if (!mBluetoothAdapter.isEnabled()) {
Toast.makeText(this,
"Please enable your BT and re-run this program.",
Toast.LENGTH_LONG).show();
finish();
return;
} if (D)
Log.e(TAG, "+++ DONE IN ON CREATE, GOT LOCAL BT ADAPTER +++"); // 註册一個BroadcastReceiver,等等會用來接收搜尋到裝置的消息
IntentFilter filter = new IntentFilter();
filter.addAction(BluetoothDevice.ACTION_FOUND);
filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
filter.addAction(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);
filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
registerReceiver(mReceiver, filter);
ReceiverIsRegisted = true;
} private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
BluetoothDevice device = null;
// 當收尋到裝置時
if (BluetoothDevice.ACTION_FOUND.equals(intent.getAction())) {
Log.e(TAG, "Discovered");
// 取得藍芽裝置這個物件
device = intent
.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
// 取得裝置名稱字串
String newDeviceName = device.getName().toString();
// 取得裝置MAC地址
String newDeviceAddress = device.getAddress().toString();
// 先比较加入的值是否已存在。不存在才可加入入Spinner選單
for (int i = 0; i < arrayAdapterDeviceName.getCount(); i++) {
if (newDeviceName.equals(arrayAdapterDeviceName.getItem(i))) {
return;
}
}
if (!newDeviceName.equals("")) {
arrayAdapterDeviceName.add(newDeviceName); // 將值加入到adapter
allDevicesAddress.add(newDeviceAddress); // 將地址加入到ArrayList
arrayAdapterDeviceName.notifyDataSetChanged();
}
} else if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(intent
.getAction())) {
device = intent
.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
switch (device.getBondState()) {
case BluetoothDevice.BOND_BONDING:
Log.e(TAG, "Bluetooth device is pairing...");
break;
case BluetoothDevice.BOND_BONDED:
Log.e(TAG, "Pairing complete");
break;
case BluetoothDevice.BOND_NONE:
Log.e(TAG, "Cancel paring");
break;
default:
break;
}
}
}
}; Runnable ConnectBTThread = new Runnable() { @Override
public void run() {
// TODO Auto-generated method stub // Blocking connect, for a simple client nothing else can
// happen until a successful connection is made, so we
// don't care if it blocks.
if (selectedAddress == 0) {
return;
} // 停止搜尋以減少資源消耗
if (mBluetoothAdapter.isDiscovering()) {
mBluetoothAdapter.cancelDiscovery();
Log.e(TAG, "Cancel discovery");
} // 連結到該裝置
BluetoothDevice device = mBluetoothAdapter
.getRemoteDevice(allDevicesAddress.get(selectedAddress)
.toString());
try {
btSocket = device.createRfcommSocketToServiceRecord(MY_UUID);
} catch (IOException e) {
// TODO Auto-generated catch block
Log.e(TAG, "Connect: Socket creation failed.", e);
}
try {
btSocket.connect();
BTIsConnected = true;
Log.e(TAG,
"ON RESUME: BT connection established, data transfer link open.");
} catch (IOException e) {
try {
btSocket.close();
BTIsConnected = false;
} catch (IOException e2) {
Log.e(TAG,
"ON RESUME: Unable to close socket during connection failure",
e2);
}
}
Log.e(TAG, "BTisConnected = " + Boolean.toString(BTIsConnected));
Message msg = msgHandler.obtainMessage();
if (!BTIsConnected) {
btnSend.setEnabled(false);
btnStartHud.setEnabled(false);
setTitle("Not connected");
Log.e(TAG, "Try reconnect");
msg.what = 2;
msgHandler.sendMessage(msg); // 又一次連線
} else {
btnSend.setEnabled(true);
btnStartHud.setEnabled(true);
setTitle("Connected");
// Create a data stream so we can talk to server.
if (D)
Log.e(TAG, "+ ABOUT TO SAY SOMETHING TO SERVER +");
if (IsTesting && hasFirstMatch) {
msg.what = 1;
msgHandler.sendMessage(msg); // 已又一次連線。恢復發送测試訊息
}
hasFirstMatch = true;
}
}
}; Runnable SendBTMsgThread = new Runnable() { @Override
public void run() {
sum++;
// Check if BT connection was established.
if (!BTIsConnected) {
return;
} try {
outStream = btSocket.getOutputStream();
} catch (IOException e) {
Log.e(TAG, "ON RESUME: Output stream creation failed.", e);
BTIsConnected = false;
setTitle("Not connected");
} // Send msg start symbol
String messageStart = "S";
byte[] msgBufferStart = messageStart.getBytes(); try {
outStream.write(msgBufferStart);
} catch (IOException e) {
Log.e(TAG, "ON RESUME: Exception during write.", e);
BTIsConnected = false;
setTitle("Not connected");
} // send msg
String message = Integer.toString((int) (speed));
// String message=et1.getText().toString();
byte[] msgBuffer = message.getBytes(); try {
outStream.write(msgBuffer);
} catch (IOException e) {
Log.e(TAG, "ON RESUME: Exception during write.", e);
BTIsConnected = false;
setTitle("Not connected");
} // Send msg end symbol
String messageEnd = "E";
byte[] msgBufferEnd = messageEnd.getBytes(); try {
outStream.write(msgBufferEnd);
} catch (IOException e) {
Log.e(TAG, "ON RESUME: Exception during write.", e);
BTIsConnected = false;
setTitle("Not connected");
}
Message msg = msgHandler.obtainMessage();
if ((android.os.Build.VERSION.SDK_INT < NEED_BT_RECONNECT_DEVICE_MAX_SDK)
&& (sum > BT_RECONNECT_LIMIT_TIME)) {
msg.what = 0;
msgHandler.sendMessage(msg); // 又一次連線以確保藍芽通道連線順暢,不重連可能會當機
} Log.e(TAG, "sendThread: " + sum + " times.");
}
}; Runnable SendBTTestMsgThread = new Runnable() { @Override
public void run() {
sum++; // Check if BT connection was established.
if (!BTIsConnected) {
return;
} try {
outStream = btSocket.getOutputStream();
} catch (IOException e) {
Log.e(TAG, "ON RESUME: Output stream creation failed.", e);
BTIsConnected = false;
setTitle("Not connected");
} // Send msg start symbol
String messageStart = "S";
byte[] msgBufferStart = messageStart.getBytes(); try {
outStream.write(msgBufferStart);
} catch (IOException e) {
Log.e(TAG, "ON RESUME: Exception during write.", e);
BTIsConnected = false;
setTitle("Not connected");
} // send msg
String message = et1.getText().toString();
byte[] msgBuffer = message.getBytes(); try {
outStream.write(msgBuffer);
} catch (IOException e) {
Log.e(TAG, "ON RESUME: Exception during write.", e);
BTIsConnected = false;
setTitle("Not connected");
} // Send msg end symbol
String messageEnd = "E";
byte[] msgBufferEnd = messageEnd.getBytes(); try {
outStream.write(msgBufferEnd);
} catch (IOException e) {
Log.e(TAG, "ON RESUME: Exception during write.", e);
BTIsConnected = false;
setTitle("Not connected");
}
// 下面為循環發送訊息测試。若仅仅發送一次則不须要
// Message msg = msgHandler.obtainMessage();
//
// if ((android.os.Build.VERSION.SDK_INT <
// NEED_BT_RECONNECT_DEVICE_MAX_SDK)
// && sum > BT_RECONNECT_LIMIT_TIME) {
// msg.what = 0;
// msgHandler.sendMessage(msg); // 又一次連線以確保藍芽通道連線順暢,不重連可能會當機
// } else {
// msg.what = 1;
// msgHandler.sendMessage(msg);
// }
// Log.e(TAG, "sendThread: " + sum + " times.");
}
}; Runnable HudThread = new Runnable() { @Override
public void run() {
// TODO Auto-generated method stub
initControl();
}
}; private void initControl() {
// GPS啟動
locationManager = (LocationManager) this.getSystemService(SERVERNAME);
if (getGpsStates()) { getLocation();
} else {
Toast.makeText(this,
"Please enable your GPS, and re-run this program. ",
Toast.LENGTH_LONG).show();
btnStartHud.setEnabled(true);
hudThreadHandler.removeCallbacks(HudThread);
}
} // 打開GPS
public boolean getGpsStates() { if (locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
return true;
} else
return false;
} private void getLocation() {
try {
Criteria criteria = new Criteria();
criteria.setAccuracy(Criteria.ACCURACY_FINE); // 高精度
criteria.setAltitudeRequired(false);
criteria.setBearingRequired(false);
criteria.setCostAllowed(true);
criteria.setPowerRequirement(Criteria.POWER_LOW); // 低功耗 // String provider = locationManager.getBestProvider(criteria,
// true); // 獲取GPS信息 // 設置監聽器,自動更新的最小時間為間隔(N*1000)秒或最小位移變化超過N米
locationManager.requestLocationUpdates(
LocationManager.GPS_PROVIDER, 1 * 1000, (float) 0.5,
locationListener);
getSpeedThreadHandler.post(GetSpeedThread); // 改用GetSpeedThread來得到速度 locationManager.addGpsStatusListener(gpsstatusListener); } catch (Exception e) { }
} // 移動判定線程
Runnable GetSpeedThread = new Runnable() { @Override
public void run() {
// TODO Auto-generated method stub
if (speed == lastSpeed) { //若和五秒前的速度一樣,則視為靜止。速度為零
speed = 0;
tvGPSInfo.setText("GPS Info" + "\n" + " 速度 = " + speed
+ "km/h" + "\n Thread Sum = " + sum);
if (BTIsConnected) {
sendBTMsgHandler.post(SendBTMsgThread);
} else {
setTitle("Not connected");
}
} else {
lastSpeed = speed;
}
getSpeedThreadHandler.postDelayed(GetSpeedThread, STATIC_CONTINUED_TIME);
} }; private final LocationListener locationListener = new LocationListener() { public void onLocationChanged(Location location) {
speed = (float) (location.getSpeed() * 3.6);// 速度
if (speed < 3.0) { // 速度修正,若時速在3km/h內則視為0km/h。因為要位移0.85m以上,時速才會高於3.06km/h。0.85m相當於沒有動,視為是定位的誤差
speed = 0;
}
tvGPSInfo.setText("GPS Info" + "\n" + " 速度 = " + speed + "km/h"
+ "\n Thread Sum = " + sum);
if (BTIsConnected) {
sendBTMsgHandler.post(SendBTMsgThread);
} else {
setTitle("Not connected");
} } public void onProviderDisabled(String provider) {
// WriteDB(null);
} public void onProviderEnabled(String provider) {
} public void onStatusChanged(String provider, int status, Bundle extras) {
// "<locationListener>本地監聽狀態改變\n");
switch (status) { case LocationProvider.AVAILABLE:
// "<locationListener>有可用衛星\n");
break;
case LocationProvider.OUT_OF_SERVICE:
// "<locationListener>無可用衛星服務\n");
break;
case LocationProvider.TEMPORARILY_UNAVAILABLE:
// "<locationListener>暫時無可用衛星服務\n");
break;
} } }; private final GpsStatus.Listener gpsstatusListener = new GpsStatus.Listener() {
private GpsStatus gpsStatus; public void onGpsStatusChanged(int event) { gpsStatus = locationManager.getGpsStatus(null); switch (event) {
case GpsStatus.GPS_EVENT_FIRST_FIX:
// GPS時間
gpdDate = new Date(gpsStatus.getTimeToFirstFix());
break; case GpsStatus.GPS_EVENT_SATELLITE_STATUS:
// 得到全部收到的衛星的信息,包含 衛星的高度角、方位角、信噪比、和偽隨機號(衛星編號)
Iterable<GpsSatellite> allSatellites;
allSatellites = gpsStatus.getSatellites(); tempBuilder.delete(0, tempBuilder.length());
for (GpsSatellite gpsstate : allSatellites) {
tempBuilder.append("方位角 " + gpsstate.getAzimuth());
tempBuilder.append("\n");
tempBuilder.append("高度角 " + gpsstate.getElevation());
tempBuilder.append("\n");
tempBuilder.append(" 偽隨機號(衛星編號)" + gpsstate.getPrn());
tempBuilder.append("\n");
tempBuilder.append(" 信噪比 " + gpsstate.getSnr());
tempBuilder.append("\n");
} int maxSatellites = gpsStatus.getMaxSatellites(); Iterator<GpsSatellite> it = gpsStatus.getSatellites()
.iterator(); int satellite = 0; while (it.hasNext() && satellite <= maxSatellites) { // GpsSatellite s = it.next();
it.next();
satellite++; } // 計算衛星個數,可在此打印出衛星的其他信息 satelliteCount = satellite;
tempBuilder.append("當前衛星個數:" + satellite + "\n");
tvGPSState.setText("GPS State" + "\n" + " 當前衛星個數:"
+ satellite);
break; case GpsStatus.GPS_EVENT_STARTED:
// Event sent when the GPS system has started. // "<gpsstatusListener>开始信號GPS_EVENT_STARTED\n");
break; case GpsStatus.GPS_EVENT_STOPPED:
// Event sent when the GPS system has stopped.
// "<gpsstatusListener>結束信號GPS_EVENT_STOPPED\n");
break; default:
// "<gpsstatusListener>沒有獲取到狀態信息\n");
break;
}
}
}; @Override
public void onStart() {
super.onStart();
if (D)
Log.e(TAG, "++ ON START ++");
} @Override
public void onResume() {
super.onResume(); if (D) {
Log.e(TAG, "+ ON RESUME +");
Log.e(TAG, "+ ABOUT TO ATTEMPT CLIENT CONNECT +");
}
} @Override
public void onPause() {
super.onPause(); if (D)
Log.e(TAG, "- ON PAUSE -"); } @Override
public void onStop() {
super.onStop();
if (D)
Log.e(TAG, "-- ON STOP --");
} @Override
public void onDestroy() {
super.onDestroy();
if (D)
Log.e(TAG, "--- ON DESTROY ---"); if (outStream != null) {
try {
outStream.flush();
} catch (IOException e) {
Log.e(TAG, "ON PAUSE: Couldn't flush output stream.", e);
}
} if (mBluetoothAdapter.isDiscovering()) {
mBluetoothAdapter.cancelDiscovery();
Log.e(TAG, "Cancel discovery");
} try {
if (BTIsConnected) {
btSocket.close();
BTIsConnected = false;
} } catch (IOException e2) {
Log.e(TAG, "ON PAUSE: Unable to close socket.", e2);
} if (ReceiverIsRegisted) {
unregisterReceiver(mReceiver);
} removeListener();
} private void removeListener() {
try {
locationManager.removeUpdates(locationListener);
locationManager.removeGpsStatusListener(gpsstatusListener);
} catch (Exception e) { }
} @Override
public boolean onMenuItemSelected(int featureId, MenuItem item) {
// TODO Auto-generated method stub
return super.onMenuItemSelected(featureId, item);
} @Override
public boolean onCreateOptionsMenu(Menu menu) {
// TODO Auto-generated method stub
return super.onCreateOptionsMenu(menu);
} }

Android 行动装置端主介面Layout

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/LinearLayout1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".HelmetHud" > <LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content" > <Button
android:id="@+id/btnDiscovery"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.5"
android:text="Discovery" /> <Button
android:id="@+id/btnConnect"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.5"
android:text="Connect" /> </LinearLayout> <Spinner
android:id="@+id/spinner1"
android:layout_width="match_parent"
android:layout_height="wrap_content" /> <Button
android:id="@+id/btnStartHud"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="18dp"
android:text="Start HUD" /> <TextView
android:id="@+id/tvGPSState"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="GPS State"
android:textAppearance="?android:attr/textAppearanceLarge" /> <TextView
android:id="@+id/tvGPSInfo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="GPS Info"
android:textAppearance="? android:attr/textAppearanceLarge" /> <LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp" > <EditText
android:id="@+id/editText1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.5"
android:ems="10" > <requestFocus />
</EditText> <Button
android:id="@+id/btnSend"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.5"
android:text="SendTestMSG" /> </LinearLayout> </LinearLayout>

安全帽端设备Arduino主程序

#define CMD_MAXNUM 5
#define LED_DIGIT_NUM 8 int DIO_Pin = 8; //pin 14 on the 75HC595(DIO)
int RCK_Pin = 9; //pin 12 on the 75HC595(RCLK)
int SCK_Pin = 10; //pin 11 on the 75HC595(SCLK) // 蜂鳴器變量
int melody = 523;
int duration = 500; // 500 miliseconds unsigned char LED_0F[] =
{// 0 1 2 3 4 5 6 7 8 9
0xC0,0xCF,0x92,0x86,0x8D,0xA4,0xA0,0xCE,0x80,0x84
}; //顛倒後的字元
// 全局變量
unsigned char SEG[LED_DIGIT_NUM]; //用於LED的4位顯示緩存
char cmd[CMD_MAXNUM]; //藍芽接收的字串(車速) void SEG_OUT(unsigned char X)
{
unsigned char i;
for(i=LED_DIGIT_NUM;i>=1;i--)
{
if (X&0x80)
digitalWrite(DIO_Pin, HIGH);
else
digitalWrite(DIO_Pin, LOW);
X<<=1;
digitalWrite(SCK_Pin, LOW);
digitalWrite(SCK_Pin, HIGH);
}
} void DisplaySegment(void)
{
unsigned char led_table; // 查表指針
unsigned char i;
//顯示第1位
if(SEG[0]!=0)
{
led_table = LED_0F[SEG[0]];
i = led_table; SEG_OUT(i);
SEG_OUT(0x01); digitalWrite(RCK_Pin, LOW);
digitalWrite(RCK_Pin, HIGH);
}
//顯示第2位
if(SEG[0]!=0 || SEG[1]!=0)
{
led_table = LED_0F[SEG[1]];
i = led_table; SEG_OUT(i);
SEG_OUT(0x02); digitalWrite(RCK_Pin, LOW);
digitalWrite(RCK_Pin, HIGH);
} //顯示第3位
if(SEG[0]!=0 || SEG[1]!=0 || SEG[2]!=0)
{
led_table = LED_0F[SEG[2]];
i = led_table; SEG_OUT(i);
SEG_OUT(0x04); digitalWrite(RCK_Pin, LOW);
digitalWrite(RCK_Pin, HIGH);
} //顯示第4位 led_table = LED_0F[SEG[3]];
i = led_table; SEG_OUT(i);
SEG_OUT(0x08); digitalWrite(RCK_Pin, LOW);
digitalWrite(RCK_Pin, HIGH);
} // 設定整個四合一型七段顯示器想要顯示的數字
// 参數number的範圍應是0~9999
void setNumber(int number)
{
int n0, n1, n2, n3;
n3 = number / 1000;
number %= 1000;
n2 = number / 100;
number %= 100;
n1 = number / 10;
n0 = number % 10; // 每位數值清零
SEG[0]=0;
SEG[1]=0;
SEG[2]=0;
SEG[3]=0;
// 求出每個位數的值後,分別更新
// 因為要實現反射式HUD,所以位置要顛倒
SEG[0]=n3; //第一位字元
SEG[1]=n2; //第二位字元
SEG[2]=n1; //第三位字元
SEG[3]=n0; //第四位字元 DisplaySegment();
} void setup()
{
Serial.begin(9600); // 藍牙模組預設baud rate = 9600
pinMode(DIO_Pin, OUTPUT);
pinMode(RCK_Pin, OUTPUT);
pinMode(SCK_Pin, OUTPUT);
} void loop()
{ while (Serial.available()) //connected
{
memset(cmd, 0, CMD_MAXNUM);
if(Serial.read() == 'S')
{
for(int i=0;i<CMD_MAXNUM;i++)
{
cmd[i] = Serial.read();
if(cmd[i]=='E')
{
cmd[i]=0;
break;
}
else if(cmd[i]<48 || cmd[i]>57)
{
cmd[i]=0;
i--;
continue;
}
}
cmd[CMD_MAXNUM-1]='\0';
Serial.print(cmd);
Serial.print("\n");
}
} setNumber(atoi(cmd));
if(atoi(cmd)>=70)
{
// 在 pin7 上輸出聲音,每個音階響 1 秒
tone(7, melody, duration); }
}

[Arduino+Android] 自制土砲智能安全帽的更多相关文章

  1. Xamarin.Android之布局文件智能提示问题

    一.前言 看到有人问关于xamarin.android的布局没智能提示问题(VS 2015),当然,写布局这东西没提示这是一件相对痛苦的事 ,所以这里就提供一个解决的方案! 二.解决方案 想要智能提示 ...

  2. Xamarin Android布局文件没有智能提示

    Xamarin Android布局文件没有智能提示 在Visual Studio 2015中,Android项目的Main.axml文件没有智能提示,不便于布局文件的编写.解决办法:(1)从Xamar ...

  3. android静默安装和智能安装(转)

    Android 静默安装和智能安装的实现方法 http://blog.csdn.net/fuchaosz/article/details/51852442 Android静默安装实现方案,仿360手机 ...

  4. Android 仿土巴兔选择效果

    1,前两天在群里看到有人在讨论土巴兔的选择装修风格的效果,自己也想实现,果断百度一下,有些好的文章,就花了些时间来分析了下,先看看别人土巴兔原装的功能 2,可以看到,基本上可以使用一个vviewpag ...

  5. TPYBoard自制微信远程智能温湿度计

    智能时代一夜间什么都能远程了.创业者想着如何做智能产品,如何做远程控制.DIY爱好者也想着如何自制各种奇妙的工具.这里和大家一起学习制作一款廉价的智能温湿度计.说它廉价是因为共计花费不过40元,说它智 ...

  6. 为 NativeScript 项目添加 iOS / Android 平台 API 的智能感知

    使用 NativeScript ,我们可以很容易的调用平台的原生 API,在开发过程中,我们可以添加这些 API 的 d.ts 文件来提供智能感知,帮助我们更方便的构建媲美原生的 APP. 首先通过 ...

  7. 1000多块整个插板,arduino + android 蓝牙插板的实现--屌丝版

       需求描述 儿子有一堆充电玩具,基本上都是锂电池,经常插上去充电忘了到时拔下来,所以需要一块能设置接通时间的插板以保障电池的安全.   硬件设计: 首先需要一块插板,接着需要一个继电器,然后采用a ...

  8. Android自制浏览器WebView-android学习之旅(64)

    简单讲解如何使用WebView加载百度的网页 acticity代码 public class MainActivity extends Activity { private WebView webVi ...

  9. MTK Android SwitchPreference(设置-智能辅助-导航栏-导航栏可隐藏)

    1.界面布局文件 packages/apps/PrizeSettings/res/xml/navigation_bar_prize.xml ------------------------------ ...

随机推荐

  1. C#中的线程(三) 使用多线程

    第三部分:使用多线程 1.  单元模式和Windows Forms 单元模式线程是一个自动线程安全机制, 非常贴近于COM——Microsoft的遗留下的组件对象模型.尽管.NET最大地放弃摆脱了遗留 ...

  2. jdk1.8.0_101/bin下各文件解释

    appletviewer - Runs applets outside of a web browser. extcheck - Detects version conflicts between a ...

  3. 使用Powermock进行单元测试,以及常见问题的处理

    1. 引言 在进行单元测试时,经常遇到被测方法依赖外部对象和环境,如需要数据库连接,网络通信依赖等,需要进行大量的初始化工作,这时可以采用powermock+mockito对被测对象进行模拟,通过录放 ...

  4. bzoj1293: [SCOI2009]生日礼物

    单调队列 用一个堆维护目前每个颜色在里面的点,每回取出队首点,并更新答案.一旦哪个颜色的点都被用完,跳出循环. #include<cstdio> #include<algorithm ...

  5. 请用一句话概括JSONP

    服务器调用客户端的函数(即回调函数),在客户端就能拿到服务端传入的参数(即返回结果)

  6. NOI2002 荒岛野人

    这题其实黑书上有,只是我脑残的没想起来…… 其实就是拓展欧几里得算法 我参看的题解:http://www.cnblogs.com/Rinyo/archive/2012/11/25/2788373.ht ...

  7. 20款最优秀的JavaScript编辑器

    毫无疑问SublimeText,Notepad++,webstorm等,是市面上最主导的编辑器,但当然也有一些更多的JavaScript编辑器提供众多的特性和功能,方便和轻松自由的编码.本文整理了20 ...

  8. [POJ 1674] Sorting by Swapping

    Sorting by Swapping Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 9514   Accepted: 50 ...

  9. Linux sysfs device_attribute

    /*************************************************************************** * Linux sysfs device_at ...

  10. MySQL数据库的同步配置+MySql读写分离

    使用mysql主从复制的好处有: 1.采用主从服务器这种架构,稳定性得以提升.如果主服务器发生故障,我们可以使用从服务器来提供服务. 2.在主从服务器上分开处理用户的请求,可以提升数据处理效率. 3. ...