# 訊號與系統報告
## 題目 - 光敏電阻或光感應器之應用
* 設計一個轉速計並以之控制馬達轉速
### Developer:
>[name=倪祺婷] [name=林建宏] [name=楊宗凱]
## 實驗器材
RPI3 :

L298N :

其他器材 :

| 項目 | 數量 | 項目 | 數量 |
| :---: | :---: | :---: | :---: |
| 麵包版供電模組|一個| L298N | 一個 |
| 18650電池 | 二個 | 電池盒 | 一個 |
| 馬達 | 一個 | 測速盤 | 一個 |
| RPI3 | 一台 | 7697 | 一個 |
| 光柵 | 二個 | 手機 | 一台 |
## 實驗步驟(流程圖)

* server利用Rpi來架設(MQTT),並提供wifi熱點,讓手機、7697得以接收及發送訊息
* 手機App連接Terminal 來接收、控制轉速 / 7967連接Terminal 來接收控制、回傳轉速
* 7697輸出pwm訊號來控制馬達轉速 (0-255)
* 7697利用測速盤(20格)和光柵回傳的資料來測得實際轉速並反饋
## 資料處理的方式 - 光閘測轉速


* 如圖所示,利用兩個光閘去測轉速,會得出三個數據,分別是
1. 兩個光閘本身的數據,
2. 光閘之間的數據
> [name=Miles]若不易擺放則只使用一個光柵
- 測轉速資料處理方式
1. 利用impulse去測馬達,藉此得到系統的全貌,從光柵得到數位資料及類比資料
2. 將光柵視為開關,利用7697的 Timer 及 外部中斷來測量轉速
3. 再利用7697的wifi傳給手機端
> [name=Miles] 1.轉盤20孔 每18度 Timer每1ms 計數一次
> 2.可以試試BLE
> 3. 類比模式只能測量透明度及調整感測器靈敏度
- 控制轉速資料處理方式
1. 若從手機端發出參考速度給7697
2. 7697將速度轉成pwm訊號給l298N模組去控制轉速
3. 對比接收的轉速和實際測得的轉速來處理誤差(反饋)
## 資料處理的方式 - 反饋

## 預計結果

* 可利用手機與7697溝通,且7697可將測得的轉速送給手機端來顯示,
手機端也可將所想要的轉速傳給7697去控制馬達轉速
## 參考資料
1. L298N 介紹 :
web.htjh.tp.edu.tw/B4/105-2robot/L298N馬達驅動模組介紹.pdf
2. 光柵 :
https://www.openimpulse.com/blog/products-page/product-category/motor-speed-encoder-sensor/
3. 穩壓模組, 馬達, L298N :
https://hackmd.io/s/HJkSu1FoZ
4. Timer :
https://docs.labs.mediatek.com/resource/linkit7697-arduino/en/developer-guide/timer
--------------------
# 結果呈現
利用
* 一個TT馬達
* 兩個光閘
* arduino
* 7697
* 手機APP
完成馬達轉速計
## 完成圖

## 流程圖

## 使用器材
| 項目 | 數量 | 項目 | 數量 |
| :---: | :---: | :---: | :---: |
| 麵包版供電模組|一個| L298N | 一個 |
| 18650電池 | 二個 | 電池盒 | 一個 |
| 馬達 | 一個 | 測速盤 | 一個 |
| arduino | 一台 | 7697 | 一個 |
| 光柵 | 二個 | 手機 | 一台 |
## 系統架構
- 利用arduino mega 來測轉速 (原先是打算由7697來測量)
因 7697 的interrupt 處理速度不夠快,導致光柵無法測量
所以將原本由7697測量的轉速交給arduino
- Arduino 利用Rx Tx將量測到的轉速傳給7697
- 7697 利用 BLE 傳現在的轉速給手機端
- 手機端可將想達到的轉速傳給7697,7697利用arduino量到的轉速來調整pwm
## 程式碼
### 7697程式碼
```
#include <SoftwareSerial.h>
SoftwareSerial mySerial(2, 11); // RX, TX
#include <LBLE.h>
#include <LBLEPeriphral.h>
LBLEService ledService("19B10010-E8F2-537E-4F6C-D104768A1214");
LBLECharacteristicString switchCharacteristic("19B10011-E8F2-537E-4F6C-D104768A1214", LBLE_READ | LBLE_WRITE);
String text="";
const int motorin1 = 16;
const int motorin2 = 17;
const int mul[4] = {1000,100,10,1};
int count = 3;
bool flag=false;
// speed is rpm
int required_speed = 810;
int now_speed = 0;
bool at_range=false;
int now_pwm = 160;
int boundarylarge = 100;
int boundarymid = 50;
int boundarysmall = 10;
int pwm_count=0;
void setup() {
Serial.begin(9600); //設定baud rate
pinMode(motorin1,OUTPUT);
pinMode(motorin2,OUTPUT);
analogWrite(motorin1,now_pwm);
analogWrite(motorin2,0);
while (!Serial) {
;
}
Serial.println("Goodnight moon!");
mySerial.begin(4800);
LBLE.begin();
while (!LBLE.ready()) {
delay(100);
}
Serial.println("BLE ready");
detect_bound();
Serial.print("Device Address = [");
Serial.print(LBLE.getDeviceAddress());
Serial.println("]");
LBLEAdvertisementData advertisement;
advertisement.configAsConnectableDevice("BLE MOTOR");
LBLEPeripheral.setName("BLE MOTOR");
ledService.addAttribute(switchCharacteristic);
LBLEPeripheral.addService(ledService);
LBLEPeripheral.begin();
LBLEPeripheral.advertise(advertisement);
}
void loop() {
if(mySerial.available() ) { // 有收到數值時,會回傳>0的值
char i = mySerial.read();
if(i=='.')
{
flag=true;
}
else
{
if(flag)
{
if(i<='9'&&i>='0')
{
now_speed += (int)(i-'0')*mul[count] ;
count--;
if(count==-1)
{
pwm_set();
send_data(now_speed);
// 傳送data給手機
count = 3; now_speed = 0;
flag=false;
}
}
}
}
}
LBLEPeripheral.connected();
//Serial.print("conected=");
//Serial.println(LBLEPeripheral.connected());
/*if (digitalRead(6)) // 斷線
{
Serial.println("disconnect all!");
LBLEPeripheral.disconnectAll();
}*/
if (switchCharacteristic.isWritten()) { // 接收到手機資料
text = switchCharacteristic.getValue();
required_speed = text.toInt();
at_range=false;
Serial.println(required_speed);
detect_bound();
}
}
//傳資料
void send_data(int data) {
switchCharacteristic.setValue(String(data));
LBLEPeripheral.notifyAll(switchCharacteristic);
}
void pwm_set() {
if(at_range)
{
if(abs(now_speed-required_speed)>boundarysmall)
{
++pwm_count;
}
else
{
pwm_count=0;
}
if(pwm_count>=3)
{
at_range=false;
}
}
else
{
int x=now_speed-required_speed;
int y = abs(x);
if(x>=0)
{
if(y>=boundarylarge)
{
now_pwm-=5;
}
else if (y>=boundarymid)
{
now_pwm-=2;
}
else if (y>=boundarysmall)
{
now_pwm-=1;
}
else
{
at_range=true;
}
}
else
{
if(y>=boundarylarge)
{
now_pwm+=5;
}
else if (y>=boundarymid)
{
now_pwm+=2;
}
else if (y>=boundarysmall)
{
now_pwm+=1;
}
else
{
at_range=true;
}
}
}
if(now_pwm<100)
now_pwm=100;
if(now_pwm>210)
now_pwm=210;
analogWrite(motorin1,now_pwm);
analogWrite(motorin2,0);
Serial.print("now_pwm : ");
Serial.println(now_pwm);
}
void detect_bound()
{
boundarysmall=(int)(5+(required_speed-700)/28\\\\\\\\
3.20);
boundarylarge=boundarysmall+100;
boundarymid=boundarysmall+50;
}
```
-----------------------
### Arduino程式碼
```
#include <SoftwareSerial.h>
SoftwareSerial mySerial(15,14);//RX TX
unsigned long count=0;
int counterPin = 2;
unsigned long time2;
unsigned long rpm;
unsigned int grid_num = 16;
void counter() {
count++;
}
void setup() {
Serial.begin(9600);
mySerial.begin(4800);
// pinMode(counterPin, INPUT);
attachInterrupt(0, counter, RISING);
count = 0;
rpm = 0;
time2 = 0;
}
void loop() {
if (millis() - time2 >= 1000){ /* 每秒更新 */
// 計算 rpm 時,停止計時
detachInterrupt(0);
// 偵測的格數count * (60 * 1000 / 一圈網格數20)/ 時間差)
rpm=60000*count/grid_num;
rpm = rpm/ (millis() - time2);
count = 0;
// 輸出至Console
Serial.print("RPM = ");
Serial.println(rpm,DEC);
for(int i=0;i<4;i++) {
int data = rpm%10;
mySerial.print(data);
rpm /= 10;
}
mySerial.print('.');;
//Restart the interrupt processing
time2 = millis();
attachInterrupt(0, counter, RISING);
}
}
```
----------------------
### APP程式碼
```
package org.hopto.pipe.ble_motor;
import android.Manifest;
import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothManager;
import android.bluetooth.le.BluetoothLeScanner;
import android.bluetooth.le.ScanCallback;
import android.bluetooth.le.ScanRecord;
import android.bluetooth.le.ScanResult;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Color;
import android.os.Build;
import android.os.Handler;
import android.os.ParcelUuid;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.ArrayAdapter;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity {
private BluetoothAdapter mBluetoothAdapter;
private static final int REQUEST_ENABLE_BT = 2;
private BluetoothManager mBluetoothManager;
private Handler mHandler;
private ArrayList<String> deviceName;
private ArrayList<BluetoothDevice> mBluetoothDevices=new ArrayList<BluetoothDevice>();
private static final int REQUEST_CODE_ACCESS_COARSE_LOCATION = 1;
private ListAdapter adapter;
private BluetoothLeScanner scanner;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ListView listview = (ListView) findViewById(R.id.list);
if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}
final BluetoothManager bluetoothManager =
(BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = bluetoothManager.getAdapter();
deviceName=new ArrayList<String>();
adapter = new ArrayAdapter(this,
android.R.layout.simple_list_item_1,deviceName) {
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = super.getView(position, convertView, parent);
TextView text = (TextView) view.findViewById(android.R.id.text1);
text.setTextColor(Color.WHITE);
return view;
}
};
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (ContextCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
if (ActivityCompat.shouldShowRequestPermissionRationale(this,
Manifest.permission.ACCESS_COARSE_LOCATION)) {
}
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.ACCESS_COARSE_LOCATION},
REQUEST_CODE_ACCESS_COARSE_LOCATION);
}
}
listview.setAdapter(adapter);
listview.setOnItemClickListener(onClickListView);
mHandler=new Handler();
scanner = mBluetoothAdapter.getBluetoothLeScanner();
}
public void startScanning(){
deviceName.clear();
mBluetoothDevices.clear();
((BaseAdapter)adapter).notifyDataSetChanged();
scanner = mBluetoothAdapter.getBluetoothLeScanner();
scanner.startScan(new MyScanCallback());
}
public void stopScanning()
{
scanner = mBluetoothAdapter.getBluetoothLeScanner();
scanner.stopScan(new MyScanCallback());
}
private AdapterView.OnItemClickListener onClickListView = new AdapterView.OnItemClickListener(){
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
// Toast 快顯功能 第三個參數 Toast.LENGTH_SHORT 2秒 LENGTH_LONG 5秒
Toast.makeText(MainActivity.this,"點選第 "+(position +1) +" 個 \n內容:"+parent.getAdapter().getItem(position), Toast.LENGTH_SHORT).show();
final BluetoothDevice mBluetoothDevice=mBluetoothDevices.get(position);
Log.d("device choose",mBluetoothDevice.toString());
Intent goControlIntent=new Intent(MainActivity.this,control.class);
//將device Name與address存到ControlActivity的DEVICE_NAME與ADDRESS,以供ControlActivity使用
goControlIntent.putExtra("name",mBluetoothDevice.getName());
goControlIntent.putExtra("address",mBluetoothDevice.getAddress());
stopScanning();
startActivity(goControlIntent);
}
};
public void scan_click(View view){
Toast.makeText(MainActivity.this,"Begin scan", Toast.LENGTH_SHORT).show();
startScanning();
}
public void stop_click(View view){
Toast.makeText(MainActivity.this,"Stop scan", Toast.LENGTH_SHORT).show();
stopScanning();
}
@Override
protected void onResume() {
super.onResume();
if(!mBluetoothAdapter.isEnabled()){
Intent intent=new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(intent,REQUEST_ENABLE_BT);
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if(REQUEST_ENABLE_BT==2 && resultCode== Activity.RESULT_CANCELED){
finish();
return;
}
super.onActivityResult(requestCode, resultCode, data);
}
public class MyScanCallback extends ScanCallback {
@Override
public void onScanResult(int callbackType, final ScanResult result) {
BluetoothDevice device = result.getDevice();
if(mBluetoothDevices.contains(device))
return;
mBluetoothDevices.add(device);
String deviceInfo = device.getName() + " - " + device.getAddress();
ScanRecord scanRecord = result.getScanRecord();
List<ParcelUuid> uuids = scanRecord.getServiceUuids();
if(uuids != null) {
for(int i = 0; i < uuids.size(); i++) {
deviceInfo += "\n" + uuids.get(i).toString();
}
}
final String text = deviceInfo;
Log.d("res",text );
deviceName.add(text);
((BaseAdapter)adapter).notifyDataSetChanged();
}
@Override
public void onBatchScanResults(List<ScanResult> results) {
//Do something with batch of results
}
@Override
public void onScanFailed(int errorCode) {
Log.d("error","Cannot scan");
Toast.makeText(MainActivity.this,"Cannot scan", Toast.LENGTH_SHORT).show();
}
}
}
```
-------------
### APP程式碼
```
package org.hopto.pipe.ble_motor;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothSocket;
import android.content.Context;
import android.content.Intent;
import android.net.NetworkInfo;
import android.os.Handler;
import android.os.Message;
import android.os.ParcelUuid;
import android.support.design.widget.TextInputLayout;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.text.TextPaint;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.Toast;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
public class control extends AppCompatActivity {
private String name;
private String address;
private BluetoothGatt mGatt = null ;
private BluetoothDevice device;
private BluetoothAdapter adapter;
private ArrayList< BluetoothGattService> service_list=new ArrayList< BluetoothGattService>();
private int state;
private ListAdapter listadapter;
private ArrayList<String> serviceName;
private boolean done;
private Handler mHandler ;
private ListView listview;
private TextInputLayout Input;
private Button returnlist;
private Button send;
private EditText textfield;
private BluetoothGattCharacteristic characteristic=null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_control);
listview= (ListView) findViewById(R.id.list1);
Intent intent = getIntent();
name=intent.getStringExtra("name");
address=intent.getStringExtra("address");
Input=( TextInputLayout) findViewById(R.id.motorinput);
returnlist=(Button) findViewById(R.id.re);
send=(Button)findViewById(R.id.send);
textfield=(EditText) findViewById(R.id.text_dis);
Input.setVisibility(View.INVISIBLE);
returnlist.setVisibility(View.INVISIBLE);
send.setVisibility(View.INVISIBLE);
textfield.setVisibility(View.INVISIBLE);
textfield.setKeyListener(null);
Log.d("name",name);
Log.d("address",address);
serviceName=new ArrayList<String>();
final BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(BLUETOOTH_SERVICE);
adapter = bluetoothManager.getAdapter();
listadapter = new ArrayAdapter(this,
android.R.layout.simple_list_item_1,serviceName);
device=adapter.getRemoteDevice(address);
listview.setAdapter(listadapter);
listview.setOnItemClickListener(onClickListView);
done=false;
connect_BLE();
mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
switch(msg.what){
case 1:
((BaseAdapter)listadapter).notifyDataSetChanged();
break;
case 2:
textfield.append(msg.getData().getString("text"));
break;
}
}
};
}
private void connect_BLE() {
ParcelUuid[] uuids = (ParcelUuid[]) device.getUuids();
mGatt = device.connectGatt( this , true , mGattCallback);
textfield.setText("");
Log.d("a",mGatt.toString());
}
private AdapterView.OnItemClickListener onClickListView = new AdapterView.OnItemClickListener(){
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
// Toast 快顯功能 第三個參數 Toast.LENGTH_SHORT 2秒 LENGTH_LONG 5秒
Toast.makeText(control.this,"點選第 "+(position +1) +" 個 \n內容:"+parent.getAdapter().getItem(position), Toast.LENGTH_SHORT).show();
characteristic=service_list.get(position).getCharacteristics().get(0);
mGatt.setCharacteristicNotification( characteristic,true);
mGatt.readCharacteristic(characteristic);
Input.setVisibility(View.VISIBLE);
returnlist.setVisibility(View.VISIBLE);
send.setVisibility(View.VISIBLE);
textfield.setVisibility(View.VISIBLE);
listview.setVisibility(View.INVISIBLE);
}
};
public void reclick(View view)
{
textfield.setText("");
mGatt.setCharacteristicNotification( characteristic,false);
Input.setVisibility(View.INVISIBLE);
returnlist.setVisibility(View.INVISIBLE);
send.setVisibility(View.INVISIBLE);
textfield.setVisibility(View.INVISIBLE);
listview.setVisibility(View.VISIBLE);
}
public void sendclick(View view) {
byte[] send1=null;
try{
send1 = Input.getEditText().getText().toString().getBytes("UTF-8");
}catch (Exception e)
{
}
if(send1!=null) {
Log.e("dataSend", new String(send1));
characteristic.setValue(send1);
boolean status = mGatt.writeCharacteristic(characteristic);
Log.e("dataSend", status + "");
mGatt.writeCharacteristic(characteristic);
}
}
private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
if (newState == BluetoothProfile.STATE_CONNECTED) {
Log.i("t", "Connected to GATT server.");
// Attempts to discover services after successful connection.
Log.i("t", "Attempting to start service discovery:" + gatt.discoverServices());
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
Log.i("t", "Disconnected from GATT server.");
String address = gatt.getDevice().getAddress();
}
}
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
boolean isGood = true;
for (int i = 0; i < gatt.getServices().size(); i++) {
BluetoothGattService bgs = gatt.getServices().get(i);
Log.w("t", "found service " + bgs.getUuid().toString());
Log.w("t", bgs.getCharacteristics().toString());
service_list.add(bgs);
String text = "found service " + Integer.toString(i + 1) + "\n" + bgs.getUuid().toString();
serviceName.add(text);
if (bgs.getCharacteristics().size() == 0)
isGood = false;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Message msg = new Message();
msg.what = 1;
mHandler.sendMessage(msg);
} else {
Log.w("t", "onServicesDiscovered received: " + status);
}
if(characteristic!=null)
mGatt.setCharacteristicNotification( characteristic,true);
}
@Override
public void onCharacteristicRead(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic, int status) {
Log.e("onCharacteristicRead中", "recieve" + new String(characteristic.getValue()));
Message msg = new Message();
msg.what = 2;
Bundle b=new Bundle();
b.putString("text","now speed "+new String(characteristic.getValue())+" (rmp) "+"\n");
msg.setData(b);
mHandler.sendMessage(msg);
}
@Override
public void onCharacteristicWrite(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic, int status) {//发送数据时调用
if (status == BluetoothGatt.GATT_SUCCESS) {//写入成功
Message msg = new Message();
msg.what = 2;
Bundle b=new Bundle();
b.putString("text","寫入成功\n");
msg.setData(b);
mHandler.sendMessage(msg);
} else if (status == BluetoothGatt.GATT_FAILURE) {
Log.e("onCharacteristicWrite中", "fail");
} else if (status == BluetoothGatt.GATT_WRITE_NOT_PERMITTED) {
Log.e("onCharacteristicWrite中", "no permission");
}
}
@Override
public void onCharacteristicChanged(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic) {// Characteristic 改变,数据接收会调用
Log.e("onCharacteristicChange中", "recieve" + new String(characteristic.getValue()));
Message msg = new Message();
msg.what = 2;
Bundle b=new Bundle();
b.putString("text","now speed "+new String(characteristic.getValue())+" (rmp) "+"\n");
msg.setData(b);
mHandler.sendMessage(msg);
}
};
}
```
--------
## 改進項目
- 更換測速盤格數(4格)
- 將TT馬達換成12V直流馬達
- 根據需求的速度去調整容許的誤差範圍
- 在速度的調整上增加誤差容許,以防速度不斷變化
- 獲得資料的方法
## 遇到的難題
- 網購測速盤未送達
- 光柵訊號問題 - 馬達沒轉arduino卻產生interrupt
- interrupt處理問題 - 測速盤格數過多、轉速過快
## 解決方法
1. 網購測速盤未送達
透過自製轉速盤,在解決該問題下,還可自行設定測速盤格數 (難題3)

2. 光柵訊號問題
礙於使用arduino和7697導致線路有些複雜,經確認後發現是共地的參考點所導致
4. interrupt處理問題
12V直流馬達的轉速比TT馬達還要快許多,所以在使用相同測速盤下,處理速度無法跟上,所以我們調低測速盤的格數,來降低interrupt產生的時間間隔
5. 馬達架設問題
馬達沒地方可以裝支架,只好利用L型支架把它夾住,然後架起來。
## 改進後的成品
