1. 程式人生 > >【嵌入式開發】利用ESP8266獲取附近WIFI資訊

【嵌入式開發】利用ESP8266獲取附近WIFI資訊

一、準備

  1. 一塊塊裝好AT韌體的8266晶片
  2. 一臺裝了linux的嵌入式開發板
  3. 將晶片與開發板進行連線

二、AT+CWLAP——掃描當前可用的 AP

通過傳送AT+CWLAP,可捕獲周圍的AP資訊,下面是檢視官方AT文件後得到的資訊:
這裡寫圖片描述
這裡寫圖片描述
這裡寫圖片描述

在超級終端中嘗試,其中新刷入的韌體要啟動WIFI模式,啟動指令:AT+CWMODE_DEF=3
這裡寫圖片描述

三、在主程式中實現UART通訊

由於我用的是改造過的韌體,波特率和官方的不一樣,所以在使用的時候請改一下波特率,在程式碼的巨集定義部分。

int main(int argc, char *argv[])  
{  

    int
fd, res; struct termios oldtio, newtio; char ch; char buf[256] = {0}; printf("Start...\n"); //-----------開啟uart裝置檔案------------------ fd = open(UART_DEVICE, O_RDWR|O_NOCTTY);//沒有設定O_NONBLOCK,所以這裡read和write是阻塞操作 if (fd < 0) { perror(UART_DEVICE); exit
(1); } else printf("Open %s successfully\n", UART_DEVICE); //-----------設定操作引數----------------------- tcgetattr(fd, &oldtio);//獲取當前操作模式引數 memset(&newtio, 0, sizeof(newtio)); //波特率 資料位=8 使能資料接收 newtio.c_cflag = BAUDRATE|CS8|CLOCAL|CREAD; newtio.c_iflag = IGNPAR; //newtio.c_oflag = OPOST | OLCUC; //
/* 設定為正規模式 */ //newtio.c_lflag = ICANON; tcflush(fd, TCIFLUSH);//清空輸入緩衝區和輸出緩衝區 tcsetattr(fd, TCSANOW, &newtio);//設定新的操作引數 //------------向urat傳送資料------------------- char writeBuf[32] = "AT+CWLAP\r\n"; res = write(fd, writeBuf, 32); printf("Writing: %s\n", writeBuf); //-------------從uart接收資料------------------- string allReturnBuf = ""; string s_buf; printf("Reading...\n"); while(1) { res = read(fd, buf, 64);//程式將在這裡掛起,直到從uart接收到資料(阻塞操作) if (res == 0) continue; buf[res] = '\0'; // printf("res = %d, buf = %s\n", res, buf);//將uart接收到的字元打印出來 // 由於每次最多隻能拿32位資料,所以要一直獲取直到指令返回資訊全部取回 s_buf = buf; allReturnBuf += s_buf; // 判斷AT返回命令是否到頭 if (isEnd(allReturnBuf)) { // for (int i = 0; i < allReturnBuf.size(); i++) { // if ('\n' == allReturnBuf[i]) cout << "\\n"; // else if ('\0' == allReturnBuf[i]) cout << "\\0"; // else if ('\r' == allReturnBuf[i]) cout << "\\r"; // else cout << allReturnBuf[i]; // } cout << "response:\n" << allReturnBuf; cout << endl; displayRes(selectWifiInfo(allReturnBuf)); break; } } //------------關閉uart裝置檔案,恢復原先引數-------- close(fd); printf("Close %s\n", UART_DEVICE); tcsetattr(fd, TCSANOW, &oldtio); //恢復原先的設定 return 0; }

四、實現對命令返回尾部的判斷

由於每次通過UART收到的資訊是固定位數的,例如在我的是每次接受32位,所以要不斷收集返回資訊並判斷,直到判斷出是指令返回結尾。

// 通過判斷字串的最後是否為 ERROR\r\n 或 OK\r\n 來判斷命令輸出是否結束
bool isEnd(string &str) {
    if (str.size() < 4) return false;
    string last4(str, str.size() - 4, 4);
    if ("OK\r\n" == last4) {
        cout << "AT response OK!" << endl;
        return true;
    }

    if (str.size() >= 7) {
        string last7(str, str.size() - 7, 7);
        if ("ERROR\r\n" == last7) {
            cout << "AT response ERROR!" << endl;
            return true;
        }
    }
}

五、對返回資訊進行處理

下面函式用於分隔字串

//split a string by a pattern
vector<string> split(string str, string pattern) {
  vector<string> ret;
  if(pattern.empty()) return ret;
  size_t start=0,index=str.find_first_of(pattern,0);
  while(index!=str.npos) {
    if(start!=index)
      ret.push_back(str.substr(start,index-start));
    start=index+1;
    index=str.find_first_of(pattern,start);
  }
  if(!str.substr(start).empty())
    ret.push_back(str.substr(start));
  return ret;
}

// 用於篩選通過 AT+CWLAP 取回的資訊
vector< vector<string> > selectWifiInfo(string &str) {
    /* AT+CWLAP 命令獲取格式
        +CWLAP:<ecn>,<ssid>,<rssi>,<mac>,<channel>,<freq offset>,
            <freq cali>,<pairwise_cipher>,<group_cipher>,<bgn>,<wps>

       AT+CWLAP 獲取樣例
         +CWLAP:(4,"MastarB",-74,"30:fc:68:bf:c5:cd",1,9,0,4,4,7,1)

       AT+CWLAP 引數說明
        <ecn>:加密方式
        <ssid>:字串引數,AP 的 SSID
        <rssi>:訊號強度
        <mac>:字串引數,AP 的 MAC 地址
        <channel>:通道號
        <freq offset>:AP 頻偏,單位:kHz。此數值除以 2.4,可得到 ppm 值
        <freq cali>:頻偏校準值
    */
    vector<string> lines = split(str, "\n"); //lines of response

    // 通過長度將有用資訊篩選
    vector<string>::iterator iter = lines.begin();
    while(iter != lines.end()) {
        if ((*iter).size() <= 15) {
            lines.erase(iter);
        } else {
            iter++;
        }
    }

    vector< vector<string> > result;
    // 裁剪有用資訊
    for (int i = 0; i < lines.size(); i++) {
        string info = string(lines[i].begin(), lines[i].end() - 1);
        result.push_back(split(info, ","));
    }

    return result;
}

六、將RSSI轉換成距離

// 將RSSI轉換成距離
float rssiTodis(int RSSI) {
    float iu, distance;
    iu = (float)(RSSI - A) / (float)N;  
    distance = pow(10, iu);
    return distance;
}

七、完整程式碼

/*
    Author: [email protected]
    Date: 2018.03.24

    Used for getting the rssi of the wifi nearby from 8266. 
*/
#include <sys/types.h>  
#include <iterator>
#include <sys/stat.h>  
#include <fcntl.h>  
#include <termios.h>  
#include <stdio.h> 
#include <memory.h>
#include <stdlib.h>
#include <unistd.h>
#include <string>
#include <vector>
#include <iostream>
#include <iomanip>
#include <math.h>
#include <sstream>

#define BAUDRATE        B38400 
#define UART_DEVICE     "/dev/ttyS3"    
#define FALSE  -1  
#define TRUE   0
#define N 40                //N = 10 * n ,其中n為環境衰減因子,3.25-4.5
#define A 51            //接收機和發射機間隔1m時的訊號強度

using namespace std;

// my functions
vector<string> split(string str, string pattern);
bool isEnd(string &str);
vector< vector<string> > selectWifiInfo(string &str);
float rssiTodis(int RSSI);
void displayRes(vector< vector<string> > result);



int main(int argc, char *argv[])  
{  

    int    fd, res;  
    struct termios  oldtio, newtio;  
    char  ch;  
    char buf[256] = {0};  

    printf("Start...\n");   
//-----------開啟uart裝置檔案------------------  
    fd = open(UART_DEVICE, O_RDWR|O_NOCTTY);//沒有設定O_NONBLOCK,所以這裡read和write是阻塞操作  
    if (fd < 0) {  
        perror(UART_DEVICE);  
        exit(1);  
    }  
    else  
        printf("Open %s successfully\n", UART_DEVICE);  

//-----------設定操作引數-----------------------    
    tcgetattr(fd, &oldtio);//獲取當前操作模式引數  
    memset(&newtio, 0, sizeof(newtio));  

    //波特率 資料位=8 使能資料接收   
    newtio.c_cflag = BAUDRATE|CS8|CLOCAL|CREAD;  
    newtio.c_iflag = IGNPAR;   
    //newtio.c_oflag = OPOST | OLCUC; //  
    /* 設定為正規模式 */  
    //newtio.c_lflag = ICANON;  

    tcflush(fd, TCIFLUSH);//清空輸入緩衝區和輸出緩衝區  
    tcsetattr(fd, TCSANOW, &newtio);//設定新的操作引數  

//------------向urat傳送資料------------------- 
    char writeBuf[32] = "AT+CWLAP\r\n"; 
    res = write(fd, writeBuf, 32);  
    printf("Writing: %s\n", writeBuf); 

//-------------從uart接收資料-------------------
    string allReturnBuf = "";
    string s_buf;
    printf("Reading...\n"); 
    while(1) {  
        res = read(fd, buf, 64);//程式將在這裡掛起,直到從uart接收到資料(阻塞操作)  
        if (res == 0)   
            continue;  
        buf[res] = '\0'; 

        // printf("res = %d, buf = %s\n", res, buf);//將uart接收到的字元打印出來
        // 由於每次最多隻能拿32位資料,所以要一直獲取直到指令返回資訊全部取回
        s_buf = buf;
        allReturnBuf += s_buf;

        // 判斷AT返回命令是否到頭
        if (isEnd(allReturnBuf)) {
            // for (int i = 0; i < allReturnBuf.size(); i++) {
            //     if ('\n' == allReturnBuf[i]) cout << "\\n";
            //     else if ('\0' == allReturnBuf[i]) cout << "\\0";
            //     else if ('\r' == allReturnBuf[i]) cout << "\\r";
            //     else cout << allReturnBuf[i];
            // }
            cout << "response:\n" << allReturnBuf;
            cout << endl;

            displayRes(selectWifiInfo(allReturnBuf));
            break; 
        }
    }  
//------------關閉uart裝置檔案,恢復原先引數--------  
    close(fd);  
    printf("Close %s\n", UART_DEVICE);  
    tcsetattr(fd, TCSANOW, &oldtio); //恢復原先的設定  

    return 0;  
}  

// 通過判斷字串的最後是否為 ERROR\r\n 或 OK\r\n 來判斷命令輸出是否結束
bool isEnd(string &str) {
    if (str.size() < 4) return false;
    string last4(str, str.size() - 4, 4);
    if ("OK\r\n" == last4) {
        cout << "AT response OK!" << endl;
        return true;
    }

    if (str.size() >= 7) {
        string last7(str, str.size() - 7, 7);
        if ("ERROR\r\n" == last7) {
            cout << "AT response ERROR!" << endl;
            return true;
        }
    }
}

//split a string by a pattern
vector<string> split(string str, string pattern) {
  vector<string> ret;
  if(pattern.empty()) return ret;
  size_t start=0,index=str.find_first_of(pattern,0);
  while(index!=str.npos) {
    if(start!=index)
      ret.push_back(str.substr(start,index-start));
    start=index+1;
    index=str.find_first_of(pattern,start);
  }
  if(!str.substr(start).empty())
    ret.push_back(str.substr(start));
  return ret;
}

// 用於篩選通過 AT+CWLAP 取回的資訊
vector< vector<string> > selectWifiInfo(string &str) {
    /* AT+CWLAP 命令獲取格式
        +CWLAP:<ecn>,<ssid>,<rssi>,<mac>,<channel>,<freq offset>,
            <freq cali>,<pairwise_cipher>,<group_cipher>,<bgn>,<wps>

       AT+CWLAP 獲取樣例
         +CWLAP:(4,"MastarB",-74,"30:fc:68:bf:c5:cd",1,9,0,4,4,7,1)

       AT+CWLAP 引數說明
        <ecn>:加密方式
        <ssid>:字串引數,AP 的 SSID
        <rssi>:訊號強度
        <mac>:字串引數,AP 的 MAC 地址
        <channel>:通道號
        <freq offset>:AP 頻偏,單位:kHz。此數值除以 2.4,可得到 ppm 值
        <freq cali>:頻偏校準值
    */
    vector<string> lines = split(str, "\n"); //lines of response

    // 通過長度將有用資訊篩選
    vector<string>::iterator iter = lines.begin();
    while(iter != lines.end()) {
        if ((*iter).size() <= 15) {
            lines.erase(iter);
        } else {
            iter++;
        }
    }

    vector< vector<string> > result;
    // 裁剪有用資訊
    for (int i = 0; i < lines.size(); i++) {
        string info = string(lines[i].begin(), lines[i].end() - 1);
        result.push_back(split(info, ","));
    }

    return result;
}

// 將RSSI轉換成距離
float rssiTodis(int RSSI) {
    float iu, distance;
    iu = (float)(RSSI - A) / (float)N;  
    distance = pow(10, iu);
    return distance;
}

// 用於命令列展示結果
void displayRes(vector< vector<string> > result) {
    if (result.size() == 0) cout << "NO WIFI INFO!" << endl;

    cout << std::left << setw(40) << setfill(' ') << "| NAME";
    cout << setw(10) << setfill(' ') << "| CHANNEL";
    cout << setw(10) << setfill(' ') << "| RSSI";
    cout << setw(10) << setfill(' ') << "| DIS(m)";
    cout << setw(30) << setfill(' ') << "| MAC" << endl;


    int temp;
    for (int i = 0; i < result.size(); i++) {
        cout <<" " << setw(40) << setfill(' ') << result[i][1];
        cout << setw(10) << setfill(' ') << result[i][4];
        cout << setw(10) << setfill(' ') << result[i][2];
        temp = (result[i][2][1] - '0') * 10 + (result[i][2][2] - '0');
        cout << setw(10) << setiosflags(ios::fixed) << setprecision(2) 
            << setfill(' ') << rssiTodis(temp);
        cout << setw(30) << setfill(' ') << result[i][3];
        cout << endl;
    }
    cout << endl;
}

相關推薦

嵌入式開發利用ESP8266獲取附近WIFI資訊

一、準備 一塊塊裝好AT韌體的8266晶片 一臺裝了linux的嵌入式開發板 將晶片與開發板進行連線 二、AT+CWLAP——掃描當前可用的 AP 通過傳送AT+CWLAP,可捕獲周圍的AP資訊,下面是檢視官方AT文件後得到的資訊:

嵌入式開發Linux上位機通過esptool燒寫ESP8266

前言 本教程針對非官方開發板、無狀態轉換按鈕、須通過gpio口控制esp8266進入下載狀態並實現esp8266韌體更新的嵌入式linux裝置。雖然esp8266有線上更新的功能,但由於開發需求不得不研究相關知識,而網上資料甚少,特此補充。 準備 上位機

嵌入式開發自定義AT指令實現sniffer網路嗅探功能

基礎 該功能是在NON-OS SDK下實現的。 Non-OS SDK 是不不基於作業系統的 SDK,提供 IOT_Demo 和 AT 的編譯。Non-OS SDK 主要使⽤用定時器和回撥函式的方式實現各個功能事件的巢狀,達到特定條件下觸發特定功能函式的目

嵌入式開發時鐘初始化 ( 時鐘相關概念 | 嵌入式時鐘體系 | Lock Time | 分頻引數設定 | CPU 非同步模式設定 | APLL MPLL 時鐘頻率設定 )

本部落格的參考文章及相關資料下載 : 一. 時鐘相關概念解析 1. 相關概念術語 (1) 時鐘脈衝訊號 時鐘脈衝訊號 : 1.概念 : 按照 一定的電壓幅度 和 一定的時間間隔 , 連續發出的 脈衝

嵌入式開發樹莓派+官方攝像頭模組+VLC串流實時輸出網路視訊流

sudo apt-get update sudo apt-get install vlc sudo raspivid -o - -t 0 -w 640 -h 360 -fps 25|cvlc -vvv stream:///dev/stdin --sout \'#standard{access=http,

微信公眾平臺開發利用百度接口,制作一鍵導航功能

顯示 12px font -c cati blog ltr vertica 拾取 微信開發中,非常多商家用戶都要求點詳細地址。能在百度或者soso地圖上面顯示自己的地址。 而這種功能。利用百度api接口地圖標點功能就能夠非常easy實現。 1.功能說明例如以下:

項目利用node開發一個博客網站

get請求 用戶 eat func 讀取 req 教程 並發 布局 項目:利用node開發一個博客網站 首先你要先安裝node(這個在網上都是有教程的) [點擊前往中文網站]:(http://nodejs.cn/) [點擊前往英文網站]:(https://nodejs.or

以太坊開發利用Oraclize開發一個投注合約(一):原理介紹

智慧合約的作用很多,但是很多資料還是要基於網際網路,那麼如何在合約中獲取網際網路中的資料?Oraclize就是為了這個目的而誕生的。 本篇介紹如何利用Oraclize開發一個投注智慧合約,開始coding之前,這一節講述一下理論。 工作原理: 智慧合約通過對Oraclize釋出一個合約之間的呼叫請求來獲

以太坊開發利用Oraclize開發一個投註合約(一):原理介紹

tween 協議 簡單方法 type callback 使用 抓取 獲取 num 智能合約的作用很多,但是很多數據還是要基於互聯網,那麽如何在合約中獲取互聯網中的數據?Oraclize就是為了這個目的而誕生的。 本篇介紹如何利用Oraclize開發一個投註智能合約,開始co

嵌入式LinuxARM開發板通過NFS掛載Linux主機實現檔案共享

一般在Linux主機上arm-linux-gcc編譯程式,然後將該生成的可執行檔案傳送給ARM開發板,ARM開發板再執行該檔案。 (主機Linux系統)--->(ARM開發板Linux系統)  傳送檔案的過程可以採用NFS,FTP等多種方法 採用NFS實現遠端掛載,ARM

嵌入式基礎嵌入式軟體開發:筆試總結

從CSDN各個部落格上摘選的一些容易做錯的嵌入式軟體的筆試題,做一下記錄,讓自己記住。程式語言的基礎考察1、以下程式碼執行結果為:#include <iostream> using namespace std; int func(int x) {     int

嵌入式開發板學習分享2016年最新迅為4412開發板手冊

看到群裡提供了最新的嵌入式開發板4412手冊看著還挺詳細的確實用心,立即收藏,由於檔案過大,上傳到網盤供需要的朋友下載: 版本19 前言23 嵌入式開發板必須注意的問題25 名詞解釋26 一 iTOP-4412 開發板介紹27 1.1 嵌入式開發板平臺簡要介紹27 1.

ARM-LInux開發利用scp 遠端上傳下載檔案/資料夾

scp [-1246BCpqrv] [-c cipher] [-F ssh_config] [-i identity_file] [-l limit] [-o ssh_option] [-P port] [-S program] [[[email protected]]host1:]file1 

ARM-Linux開發CUDA開發視訊開發關於Linux下利用GPU對視訊進行硬體加速轉碼的方案

最近一直在研究Linux下利用GPU進行硬體加速轉碼的方案,折騰了很久,至今沒有找到比較理想的硬加速轉碼方案。似乎網上討論這一方案的文章也特別少,這個過程中也進行了各種嘗試,遇到很多具體問題,以下便對之前所作的一些工作做一些總結和分享,省的時間長了自己也忘記了,也希望後來

Unity開發Unity獲取裝置螢幕解析度

using UnityEngine; using System.Collections; public class ExampleClass : MonoBehaviour { void S

嵌入式基礎嵌入式軟體開發——嵌入式軟體工程師經典筆試題

從CSDN各個部落格上摘選的一些容易做錯的嵌入式軟體的筆試題,做一下記錄,讓自己記住。 1、用預處理指令#define 宣告一個常數,用以表明1年中有多少秒(忽略閏年問題) 解答:這一題主要容易錯的地方就是:意識到這個表示式將使一個16位機的整型數溢位,因此要用到

DSP開發序列 RapidIO: 高效能嵌入式互連技術

作者: 德州儀器技術應用工程師 馮華亮/ Brighton Feng/ [email protected] 摘要 序列RapidIO針對高效能嵌入式系統晶片間和板間互連而設計,它將是未來十幾年中嵌入式系統互連的最佳選擇。 本文比較RapidIO和傳統互連技術的優點;介紹RapidIO協議架構,包格式

Android基礎利用Intent在Activity之間傳遞數據

一次 there center ack and block for success display 前言: 上一篇文章給大家聊了Intent的使用方法。怎樣用Intent啟動Activity和隱式Intent。這一篇文章給大家聊聊怎樣利用Intent在Activit

遊戲開發淺談遊戲開發中常見的設計原則

依賴關系 unity 說過 srp des log gof https 類繼承   俗話說得好:“設計模式,常讀常新~”。的確,每讀一遍設計模式都會有些新的體會和收獲。馬三不才,才讀了兩遍設計模式(還有一遍是在學校學的),屬於菜鳥級別的。這次準備把閱

Web開發Mean web開發 01-Express實現MVC模式開發

http scripts send javascrip 模板引擎 指令 開發環境 depend filter 簡介 Mean是JavaScript的全棧開發框架。更多介紹 用Express實現MVC模式開發是Mean Web全棧開發中的一部分。 Express 是一個基於