【嵌入式开发】利用ESP8266获取附近WIFI信息

一、准备

  1. 一块块装好AT固件的8266芯片
  2. 一台装了linux的嵌入式开发板
  3. 将芯片与开发板进行连接

二、AT+CWLAP——扫描当前可用的 AP

通过发送AT+CWLAP,可捕获周围的AP信息,下面是查看官方AT文档后得到的信息:
【嵌入式开发】利用ESP8266获取附近WIFI信息_第1张图片
【嵌入式开发】利用ESP8266获取附近WIFI信息_第2张图片
【嵌入式开发】利用ESP8266获取附近WIFI信息_第3张图片

在超级终端中尝试,其中新刷入的固件要启动WIFI模式,启动指令:AT+CWMODE_DEF=3
【嵌入式开发】利用ESP8266获取附近WIFI信息_第4张图片

三、在主程序中实现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:,,,,,,
            ,,,,

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

       AT+CWLAP 参数说明
        :加密方式
        :字符串参数,AP 的 SSID
        :信号强度
        :字符串参数,AP 的 MAC 地址
        :信道号
        :AP 频偏,单位:kHz。此数值除以 2.4,可得到 ppm 值
        :频偏校准值
    */
    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转换成距离

参考http://blog.csdn.net/cherish_x/article/details/79362532,利用esp8266wifi信号强度测距,d = 10^((abs(rssi) - A) / (10 * n))。

// 将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   
#include 
#include   
#include   
#include   
#include  
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#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:,,,,,,
            ,,,,

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

       AT+CWLAP 参数说明
        :加密方式
        :字符串参数,AP 的 SSID
        :信号强度
        :字符串参数,AP 的 MAC 地址
        :信道号
        :AP 频偏,单位:kHz。此数值除以 2.4,可得到 ppm 值
        :频偏校准值
    */
    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)