移动端点对点获取WeMos D1上搭载的JY-901九轴振动加速度传感器加速度数据
1 介紹
暑假里老師給布置了個任務,希望能夠避開云端,使用移動端來獲取 WeMos D1 上傳感器的數據。
那么這里大致的思路就是:
- 通過串口獲取傳感器的數據;
- 在WeMos D1上開啟服務以供移動端訪問
- 編寫移動端APP
2 系統設計與實現
2.1 系統整體架構設計
本系統主要以WeMos D1為核心,在其上開啟Socket服務,將通過串口獲得的傳感器數據,提供給移動端。
2.2 WeMos D1端
俗話說便宜沒好貨,WeMos D1這塊板子就符合這個道理,在博主拿到這塊板子和傳感器的時候,就發現了一個問題——板子的硬件串口就只有一個,而且這個串口是另有用途的,所以不能用來獲取傳感器的數據,因此在這里我們需要用到軟串口。所幸的是,在Arduino IDE這個編程環境中,已經給我們提供了軟串口1的庫,我們只需要像硬件串口一樣對其他引腳進行操作便可。對軟串口不懂的朋友,可以看下注釋1的鏈接。
這里博主選用了引腳0和引腳16來作為軟串口的RX和TX,如圖:
為了讓移動端能夠點對點連接上WeMos D1,這里WeMos D1需要開啟AP模式,以使得移動端連接其WiFi構建局域網。
接著使用Arduino IDE中ESP8266的庫函數2來開啟Socket服務。不知道怎么開啟的,請看注釋2的鏈接。
2.3 移動端
移動端的構建非常簡單,就是一個很普通的Socket客戶端。
其布局如圖所示:
因為移動端博主還有其它用途,所以這個布局有點多余的功能,各位看客要寫的話,是不需要那么多的按鈕的。
這里IP和Port在代碼中已經有默認輸入,所以不輸入直接點擊采集數據即可。
3 代碼
3.1 獲取JY-901加速度數據
在購買JY-901九軸加速度傳感器時,店家隨了一份傳感器資料過來,其中有一份用Arduino UNOR3獲取JY-901串口數據的代碼,該代碼使用Arduino IDE進行編寫,首先看工程文件“JY901Serial.ino”:
#include <Wire.h> #include <JY901.h> /* Test on Uno R3. JY901 UnoR3 TX <---> 0(Rx) */ void setup() {Serial.begin(9600); }void loop() {//print received data. Data was received in serialEvent;Serial.print("Time:20");Serial.print(JY901.stcTime.ucYear);Serial.print("-");Serial.print(JY901.stcTime.ucMonth);Serial.print("-");Serial.print(JY901.stcTime.ucDay);Serial.print(" ");Serial.print(JY901.stcTime.ucHour);Serial.print(":");Serial.print(JY901.stcTime.ucMinute);Serial.print(":");Serial.println((float)JY901.stcTime.ucSecond+(float)JY901.stcTime.usMiliSecond/1000);Serial.print("Acc:");Serial.print((float)JY901.stcAcc.a[0]/32768*16);Serial.print(" ");Serial.print((float)JY901.stcAcc.a[1]/32768*16);Serial.print(" ");Serial.println((float)JY901.stcAcc.a[2]/32768*16);Serial.print("Gyro:");Serial.print((float)JY901.stcGyro.w[0]/32768*2000);Serial.print(" ");Serial.print((float)JY901.stcGyro.w[1]/32768*2000);Serial.print(" ");Serial.println((float)JY901.stcGyro.w[2]/32768*2000);Serial.print("Angle:");Serial.print((float)JY901.stcAngle.Angle[0]/32768*180);Serial.print(" ");Serial.print((float)JY901.stcAngle.Angle[1]/32768*180);Serial.print(" ");Serial.println((float)JY901.stcAngle.Angle[2]/32768*180);Serial.print("Mag:");Serial.print(JY901.stcMag.h[0]);Serial.print(" ");Serial.print(JY901.stcMag.h[1]);Serial.print(" ");Serial.println(JY901.stcMag.h[2]);Serial.print("Pressure:");Serial.print(JY901.stcPress.lPressure);Serial.print(" ");Serial.println((float)JY901.stcPress.lAltitude/100);Serial.print("DStatus:");Serial.print(JY901.stcDStatus.sDStatus[0]);Serial.print(" ");Serial.print(JY901.stcDStatus.sDStatus[1]);Serial.print(" ");Serial.print(JY901.stcDStatus.sDStatus[2]);Serial.print(" ");Serial.println(JY901.stcDStatus.sDStatus[3]);Serial.print("Longitude:");Serial.print(JY901.stcLonLat.lLon/10000000);Serial.print("Deg");Serial.print((double)(JY901.stcLonLat.lLon % 10000000)/1e5);Serial.print("m Lattitude:");Serial.print(JY901.stcLonLat.lLat/10000000);Serial.print("Deg");Serial.print((double)(JY901.stcLonLat.lLat % 10000000)/1e5);Serial.println("m");Serial.print("GPSHeight:");Serial.print((float)JY901.stcGPSV.sGPSHeight/10);Serial.print("m GPSYaw:");Serial.print((float)JY901.stcGPSV.sGPSYaw/10);Serial.print("Deg GPSV:");Serial.print((float)JY901.stcGPSV.lGPSVelocity/1000);Serial.println("km/h");Serial.println("");delay(500); } /* SerialEvent occurs whenever a new data comes in the hardware serial RX. This routine is run between each time loop() runs, so using delay inside loop can delay response. Multiple bytes of data may be available. */ void serialEvent() {while (Serial.available()) {JY901.CopeSerialData(Serial.read()); //Call JY901 data cope function} }閱讀這么一個工程文件,第一步先看setup函數,只有一個以波特率9600打開串口的操作,第二步看loop函數,以本博客要獲取的加速度數據為例:
Serial.print("Acc:");Serial.print((float)JY901.stcAcc.a[0]/32768*16);Serial.print(" ");Serial.print((float)JY901.stcAcc.a[1]/32768*16);Serial.print(" ");Serial.println((float)JY901.stcAcc.a[2]/32768*16);可看到loop函數中都是輸出對象JY901中數據的語句,由此推測JY901中存放的便是傳感器獲取到的數據。而后第三步看serialEvent函數,這個函數的作用是獲取串口數據,并用函數CopeSerialData對串口數據進行處理并存入對象JY901中,注釋中也提到,serialEvent函數在loop函數循環時,同步運行。
下面是函數CopeSerialData源碼:
因此只要能夠從串口讀到傳感器數據,并用CopeSerialData函數對串口數據進行處理,即可獲得需要的加速度數據。
3.2 在WeMos D1端啟動服務
WeMos D1端代碼解釋請看注釋,代碼如下:
#include<ESP8266WiFi.h> #include<SoftwareSerial.h>#define AP_SSID "ESP8266WiFi" #define AP_PSW "88888888" #define SERVER_MAX 1IPAddress ip(192,168,4,22); IPAddress gateway(192,168,4,9); IPAddress subnet(255,255,255,0); SoftwareSerial mySerial(0,16);//軟串口定義,引腳0表示RX,引腳16表示TXWiFiServer server(8000);//定義socket服務,端口8000 WiFiClient clients[SERVER_MAX];//管理與socket服務相連的客戶端 struct Acceleration {short acc[3];//因為只需要加速度傳感器,所以不必如傳感器資料里一樣定義一個類,只需定義一個代表加速度的結構體即可short Time; }acceleration;void setup() {mySerial.begin(9600);//開啟熱點,移動移動端連接WiFi,與WeMos D1構建局域網WiFi.mode(WIFI_AP);//修改模式為APWiFi.softAPConfig(ip, gateway, subnet);//設置AP相關網絡參數//啟動socket服務server.begin();server.setNoDelay(true);//關閉小包合并包功能,不會延時發送數據 }void loop() {uint8_t i;if(server.hasClient()){//如果有新客戶端連接,釋放客戶端列表中失效客戶端,并將新客戶端加入for(i=0;i<SERVER_MAX;i++){if(!clients[i]||!clients[i].connected()){if(clients[i]){clients[i].stop();}clients[i]=server.available();break;}}//若客戶端列表已滿,則拒絕新客戶端連接if(i==SERVER_MAX){WiFiClient _client=server.available();_client.stop();}}if(mySerial.available()){//讀取串口數據,并使用CopeSerialData函數進行處理if(CopeSerialData(mySerial.read())){//將獲取到的加速度數據經過處理,通過socket發送給移動端//在要發送的數據前后分別加上begin和end,以供移動端更容易的識別數據頭尾char sbuf0[10]="begin ",sbuf1[10],sbuf2[10],sbuf3[10],sbuf4[10]="end\n";//將加速度類型由浮點型轉化為字符串dtostrf((float)acceleration.acc[0]/32768*16,2,2,sbuf1);dtostrf((float)acceleration.acc[1]/32768*16,2,2,sbuf2);dtostrf((float)acceleration.acc[2]/32768*16,2,2,sbuf3);//將加速度數據依次發送給每一個連接的客戶端for(i=0;i<SERVER_MAX;i++) {if(clients[i]&&clients[i].connected()) {clients[i].write(sbuf0, 6);clients[i].write(sbuf1, Judge(sbuf1));clients[i].write(sbuf2, Judge(sbuf2));clients[i].write(sbuf3, Judge(sbuf3));clients[i].write(sbuf4, 4);delay(1);}}}} }bool CopeSerialData(unsigned char ucData){static unsigned char ucRxBuffer[250];static unsigned char ucRxCnt = 0;ucRxBuffer[ucRxCnt++]=ucData;//JY-901傳感器規定有效數據由0x55開始if(ucRxBuffer[0]!=0x55){ucRxCnt = 0;return false;}//規定每11個數據為一次有效數據if(ucRxCnt<11){return false;}else{switch(ucRxBuffer[1]){//只需要加速度數據,因此只需判斷0x51即可,若要獲取別的數據,請看上面完整的CopeSerialData函數源碼case 0x51:memcpy(&acceleration,&ucRxBuffer[2],8);break;}ucRxCnt=0;return true;} }int Judge(char str[]){//計算socket連接中發送消息的長度,并在消息后面補上一個空格int len;for(len=0;len<10;len++){if(str[len]=='-'||(str[len]>='0'&&str[len]<='9')||str[len]=='.'){continue;}else{break;}}str[len]=' ';return len+1; }3.3 移動端獲取數據
移動端最主要的就是一個socket連接問題,首先是socket建立:
protected void SocketConnect() {ip = inputIP.getText().toString();port = inputPort.getText().toString();//WeMos D1的IP地址和端口一開始就有默認值,但也可由用戶自行輸入if (!IsIPPortLegal()) {Toast.makeText(MainActivity.this, "填寫不正確,已使用默認IP和端口", Toast.LENGTH_SHORT).show();ip = "192.168.4.22";port = "8000";}//Android開發中socket連接必須要以線程的方式才可以進行Thread thread = new Thread("Connect") {@Overridepublic void run() {super.run();if (!socketStatus) {try {socket = new Socket(ip, Integer.parseInt(port));if (socket != null) {//socket建立成功inputStream = socket.getInputStream();outputStream = socket.getOutputStream();//socketStatus是一個標志,避免socket還未連接就用socket獲取數據的錯誤socketStatus = true;}} catch (IOException e) {//socket建立失敗e.printStackTrace();}}}};thread.start(); }接著就是利用socket獲取數據:
protected void SocketGetData() {//同樣要新建一個線程Thread thread = new Thread("GetData") {@Overridepublic void run() {super.run();//判斷socket是否已經連接while(!socketStatus){};if (socketStatus) {try {//獲取數據int len = 0;byte[] buf = new byte[1024];String tmp = "";int count = 0;while (getDataStatus && ((len = inputStream.read(buf)) != -1)) {String _tmp = new String(buf, 0, len);tmp += _tmp;count++;if (count == 10) {DealWithInputStream(tmp);count = 0;tmp = "";}}} catch (IOException e) {e.printStackTrace();}}}};thread.start(); }protected void DealWithInputStream(String tmp) {//按照與WeMos D1約定的格式,使用正則表達式提取加速度數據,并存入文件dataCenter中Pattern pattern = Pattern.compile("begin .* end");Matcher matcher = pattern.matcher(tmp);FileOutputStream out = null;BufferedWriter writer = null;try {out = openFileOutput("dataCenter", Context.MODE_APPEND);writer = new BufferedWriter(new OutputStreamWriter(out));while (matcher.find()) {String _tmp = "" + System.currentTimeMillis() + " " + matcher.group() + (dataStatus ? " 1\r\n" : " 0\r\n");Log.d("tag", _tmp);writer.write(_tmp);}} catch (IOException e) {e.printStackTrace();} finally {try {if (writer != null) {writer.close();}} catch (IOException e) {e.printStackTrace();}} }4 遇到的問題
- 一開始使用Micropython寫的代碼,但是它沒有軟串口的庫,IIC有關的例子又少,沒看懂,后來換了C語言,用Arduino IDE編寫代碼,果然這種硬件有關的東西還是C語言好用啊。
- 寫APP的時候,代碼明明沒錯,但是Socket卻總是建立失敗,而后排查問題發現,是沒有獲取權限的問題。
- 有天開機的時候電腦掛了【ps:神舟電腦傷不起…】,而后重裝電腦,將Android Studio重裝,導入之前的項目,發現怎么都運行不了,谷歌了好久都沒解決,后來靈機一動,新建了個項目,把代碼復制粘貼進去,運行成功。
- 在寫APP的時候數據持久化遇到了個問題,存下來的數據找不到了,谷歌后才知道是在data/data/包名/file文件夾下面
- 暫時想不起別的問題了。
- 各位看官有問題請留言。
軟串口通信——SoftwareSerial庫的使用 ??
博哥零基礎教你玩轉ESP8266 ??
總結
以上是生活随笔為你收集整理的移动端点对点获取WeMos D1上搭载的JY-901九轴振动加速度传感器加速度数据的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java中使用IO将文件切割后保存(Fi
- 下一篇: vos3000手机区号添加