移动端点对点获取WeMos D1上搭载的JY-901九轴振动加速度传感器加速度数据
1 介紹
暑假里老師給布置了個(gè)任務(wù),希望能夠避開云端,使用移動(dòng)端來獲取 WeMos D1 上傳感器的數(shù)據(jù)。
那么這里大致的思路就是:
- 通過串口獲取傳感器的數(shù)據(jù);
- 在WeMos D1上開啟服務(wù)以供移動(dòng)端訪問
- 編寫移動(dòng)端APP
2 系統(tǒng)設(shè)計(jì)與實(shí)現(xiàn)
2.1 系統(tǒng)整體架構(gòu)設(shè)計(jì)
本系統(tǒng)主要以WeMos D1為核心,在其上開啟Socket服務(wù),將通過串口獲得的傳感器數(shù)據(jù),提供給移動(dòng)端。
2.2 WeMos D1端
俗話說便宜沒好貨,WeMos D1這塊板子就符合這個(gè)道理,在博主拿到這塊板子和傳感器的時(shí)候,就發(fā)現(xiàn)了一個(gè)問題——板子的硬件串口就只有一個(gè),而且這個(gè)串口是另有用途的,所以不能用來獲取傳感器的數(shù)據(jù),因此在這里我們需要用到軟串口。所幸的是,在Arduino IDE這個(gè)編程環(huán)境中,已經(jīng)給我們提供了軟串口1的庫,我們只需要像硬件串口一樣對(duì)其他引腳進(jìn)行操作便可。對(duì)軟串口不懂的朋友,可以看下注釋1的鏈接。
這里博主選用了引腳0和引腳16來作為軟串口的RX和TX,如圖:
為了讓移動(dòng)端能夠點(diǎn)對(duì)點(diǎn)連接上WeMos D1,這里WeMos D1需要開啟AP模式,以使得移動(dòng)端連接其WiFi構(gòu)建局域網(wǎng)。
接著使用Arduino IDE中ESP8266的庫函數(shù)2來開啟Socket服務(wù)。不知道怎么開啟的,請(qǐng)看注釋2的鏈接。
2.3 移動(dòng)端
移動(dòng)端的構(gòu)建非常簡單,就是一個(gè)很普通的Socket客戶端。
其布局如圖所示:
因?yàn)橐苿?dòng)端博主還有其它用途,所以這個(gè)布局有點(diǎn)多余的功能,各位看客要寫的話,是不需要那么多的按鈕的。
這里IP和Port在代碼中已經(jīng)有默認(rèn)輸入,所以不輸入直接點(diǎn)擊采集數(shù)據(jù)即可。
3 代碼
3.1 獲取JY-901加速度數(shù)據(jù)
在購買JY-901九軸加速度傳感器時(shí),店家隨了一份傳感器資料過來,其中有一份用Arduino UNOR3獲取JY-901串口數(shù)據(jù)的代碼,該代碼使用Arduino IDE進(jìn)行編寫,首先看工程文件“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} }閱讀這么一個(gè)工程文件,第一步先看setup函數(shù),只有一個(gè)以波特率9600打開串口的操作,第二步看loop函數(shù),以本博客要獲取的加速度數(shù)據(jù)為例:
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函數(shù)中都是輸出對(duì)象JY901中數(shù)據(jù)的語句,由此推測JY901中存放的便是傳感器獲取到的數(shù)據(jù)。而后第三步看serialEvent函數(shù),這個(gè)函數(shù)的作用是獲取串口數(shù)據(jù),并用函數(shù)CopeSerialData對(duì)串口數(shù)據(jù)進(jìn)行處理并存入對(duì)象JY901中,注釋中也提到,serialEvent函數(shù)在loop函數(shù)循環(huán)時(shí),同步運(yùn)行。
下面是函數(shù)CopeSerialData源碼:
因此只要能夠從串口讀到傳感器數(shù)據(jù),并用CopeSerialData函數(shù)對(duì)串口數(shù)據(jù)進(jìn)行處理,即可獲得需要的加速度數(shù)據(jù)。
3.2 在WeMos D1端啟動(dòng)服務(wù)
WeMos D1端代碼解釋請(qǐng)看注釋,代碼如下:
#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服務(wù),端口8000 WiFiClient clients[SERVER_MAX];//管理與socket服務(wù)相連的客戶端 struct Acceleration {short acc[3];//因?yàn)橹恍枰铀俣葌鞲衅?#xff0c;所以不必如傳感器資料里一樣定義一個(gè)類,只需定義一個(gè)代表加速度的結(jié)構(gòu)體即可short Time; }acceleration;void setup() {mySerial.begin(9600);//開啟熱點(diǎn),移動(dòng)移動(dòng)端連接WiFi,與WeMos D1構(gòu)建局域網(wǎng)WiFi.mode(WIFI_AP);//修改模式為APWiFi.softAPConfig(ip, gateway, subnet);//設(shè)置AP相關(guān)網(wǎng)絡(luò)參數(shù)//啟動(dòng)socket服務(wù)server.begin();server.setNoDelay(true);//關(guān)閉小包合并包功能,不會(huì)延時(shí)發(fā)送數(shù)據(jù) }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()){//讀取串口數(shù)據(jù),并使用CopeSerialData函數(shù)進(jìn)行處理if(CopeSerialData(mySerial.read())){//將獲取到的加速度數(shù)據(jù)經(jīng)過處理,通過socket發(fā)送給移動(dòng)端//在要發(fā)送的數(shù)據(jù)前后分別加上begin和end,以供移動(dòng)端更容易的識(shí)別數(shù)據(jù)頭尾char sbuf0[10]="begin ",sbuf1[10],sbuf2[10],sbuf3[10],sbuf4[10]="end\n";//將加速度類型由浮點(diǎn)型轉(zhuǎ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);//將加速度數(shù)據(jù)依次發(fā)送給每一個(gè)連接的客戶端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傳感器規(guī)定有效數(shù)據(jù)由0x55開始if(ucRxBuffer[0]!=0x55){ucRxCnt = 0;return false;}//規(guī)定每11個(gè)數(shù)據(jù)為一次有效數(shù)據(jù)if(ucRxCnt<11){return false;}else{switch(ucRxBuffer[1]){//只需要加速度數(shù)據(jù),因此只需判斷0x51即可,若要獲取別的數(shù)據(jù),請(qǐng)看上面完整的CopeSerialData函數(shù)源碼case 0x51:memcpy(&acceleration,&ucRxBuffer[2],8);break;}ucRxCnt=0;return true;} }int Judge(char str[]){//計(jì)算socket連接中發(fā)送消息的長度,并在消息后面補(bǔ)上一個(gè)空格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 移動(dòng)端獲取數(shù)據(jù)
移動(dòng)端最主要的就是一個(gè)socket連接問題,首先是socket建立:
protected void SocketConnect() {ip = inputIP.getText().toString();port = inputPort.getText().toString();//WeMos D1的IP地址和端口一開始就有默認(rèn)值,但也可由用戶自行輸入if (!IsIPPortLegal()) {Toast.makeText(MainActivity.this, "填寫不正確,已使用默認(rèn)IP和端口", Toast.LENGTH_SHORT).show();ip = "192.168.4.22";port = "8000";}//Android開發(fā)中socket連接必須要以線程的方式才可以進(jìn)行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是一個(gè)標(biāo)志,避免socket還未連接就用socket獲取數(shù)據(jù)的錯(cuò)誤socketStatus = true;}} catch (IOException e) {//socket建立失敗e.printStackTrace();}}}};thread.start(); }接著就是利用socket獲取數(shù)據(jù):
protected void SocketGetData() {//同樣要新建一個(gè)線程Thread thread = new Thread("GetData") {@Overridepublic void run() {super.run();//判斷socket是否已經(jīng)連接while(!socketStatus){};if (socketStatus) {try {//獲取數(shù)據(jù)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約定的格式,使用正則表達(dá)式提取加速度數(shù)據(jù),并存入文件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有關(guān)的例子又少,沒看懂,后來換了C語言,用Arduino IDE編寫代碼,果然這種硬件有關(guān)的東西還是C語言好用啊。
- 寫APP的時(shí)候,代碼明明沒錯(cuò),但是Socket卻總是建立失敗,而后排查問題發(fā)現(xiàn),是沒有獲取權(quán)限的問題。
- 有天開機(jī)的時(shí)候電腦掛了【ps:神舟電腦傷不起…】,而后重裝電腦,將Android Studio重裝,導(dǎo)入之前的項(xiàng)目,發(fā)現(xiàn)怎么都運(yùn)行不了,谷歌了好久都沒解決,后來靈機(jī)一動(dòng),新建了個(gè)項(xiàng)目,把代碼復(fù)制粘貼進(jìn)去,運(yùn)行成功。
- 在寫APP的時(shí)候數(shù)據(jù)持久化遇到了個(gè)問題,存下來的數(shù)據(jù)找不到了,谷歌后才知道是在data/data/包名/file文件夾下面
- 暫時(shí)想不起別的問題了。
- 各位看官有問題請(qǐng)留言。
軟串口通信——SoftwareSerial庫的使用 ??
博哥零基礎(chǔ)教你玩轉(zhuǎn)ESP8266 ??
總結(jié)
以上是生活随笔為你收集整理的移动端点对点获取WeMos D1上搭载的JY-901九轴振动加速度传感器加速度数据的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java中使用IO将文件切割后保存(Fi
- 下一篇: vos3000手机区号添加