专案动机(1/2)
专案动机(2/2)
系统功能
硬体清单
GPS測速
GPS測速与仪表板之误差
依据台湾交通部车辆安全检測基准,以无载条件(空车重+驾驶员+必要仪器)且依下列速率測试。指示速率必须永不少于真实速率且速率计标度盘指示之速率(V1)与真实速率(V2)间应满足关系:
即时车速显示
反射投影
系统架构
装置电路图
遭遇问题
为反射膜制作独立基座,使反射膜保持平整,并与视线尽可能保持垂直。
装置组装完毕图
实际展示影片
Youtube:
优酷:
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);
}
}