基於串口控制的linux無線網連接,並回傳當前ip

前言:想法產生

       這個想法是在調試樹莓派的時候發生的,因爲樹莓派作爲嵌入式的這麼一個平臺,尤其在我們安裝上ubuntu-mate這一類帶桌面系統之後,我們仍需要連接一塊屏幕來配置它的網絡,很麻煩,所以就想着用ap熱點什麼的可以直接配網該多好。所有人思維應該是利用ap熱點來控制WiFi的連接,當然這也是正常思維纔對。但是樹莓派這麼一個小東西,它的底層簡直太脆弱了,底層配置錯誤輕則無法聯網,重則連機都開不了。而ap這麼一個重量型工具,需要我們修改更多的底層來配合它完成聯網,很無耐。所以在這之後就在想,我們可不可以在應用層通過串口傳輸賬號密碼、shell腳本控制來完成聯網,我們就可以不用每次換場地聯網都要連接一個邋遢的桌面。

一、功能概述

1、硬件連接:通過兩個轉串模塊將pc和樹莓派連接起來即可,最好在樹莓派端不要連接其他同種類型芯片usb模塊,因爲我們會指定某一端口來作爲接收串口數據端口。如果實在需要,那麼下一篇文章我會講解給Ubuntu增設串口別名的方法。

2、控制流程:Ubuntu端首先啓動shell腳本,用於等待接收串口數據;windows端的串口助手,發送賬號和密碼(空格作爲標識符,所以wifi賬號和密碼都不能出現空格),爲了避免串口剛開始接收數據不穩定,所以我們需要手動發送兩次數據,連接開始。

二、注意事項

       由於大家在看教程的時候只看教程前面的資料,到了代碼這裏直接把代碼一複製就完事不再往下看了,我也這樣,所以我把注意事項寫在這裏。

       1、此代碼只是提供學習使用,並不完善,甚至顯得很蹩腳,雖然完成日常事項已經足夠,但我確實是用了最笨的實現方法,好多腳本讀取文件問題我並沒有深究,所以等會兒你會發現我會有三個文本文檔,其實一個足夠,歡迎大家完善後交流學習。

       2、現在代碼的報錯機制還不完善,比如wifi不在區域內無報錯,沒有密碼錯誤報錯,但是如果一直沒有連接上wifi,一定是這兩種錯誤其中之一。所以爲了能準確無誤連接wifi,首先用手機測試一下有沒有此wifi,密碼對不對。

       3、空格爲賬號和密碼標識符、\r\n爲結尾標識符,咱們賬號密碼不要出現空格和\r\n。

       4、我給的文件裏文本文檔不要刪

       5、開機之前首先把硬件連接好

       6、shell腳本沒有循環,連接一次不管成功與否,程序都已執行完

現在就想到這些,後期再補

三、代碼

提醒大家一下,最後有流程,按照流程來你會成功的

1、c++讀取串口數據(serial1.cpp)

#include <iostream>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <unistd.h>
#include <cstring>
using namespace std;
int Error_time=0;//用於記錄錯誤次數
int i,i1;
int rec_time=0;//用於記錄接收次數
int acc_cri=1;//允許死循環標誌位
int rr_num=0;//用於接收來自串口的數據
FILE* NAME1;//分別爲用戶名和密碼
FILE* PASSWORD1;
FILE* ERROR1;
int main(int argc, char **argv)
{
    int fd,flag,wr_num=0;
    struct termios options, newstate;
    speed_t baud_rate_i,baud_rate_o;
    unsigned char buf[40]="Connecting...\r\n\r\n"; //向串口發送的數組
    unsigned char buf1[100];
	unsigned char buf2[20];
	unsigned char buf3[20]="OK.....\r\n\r\n";
	unsigned char error[40]="Error:case 1\r\n\r\n";
    fd=open("/dev/ttyUSB0", O_RDWR|O_NONBLOCK|O_NOCTTY|O_NDELAY);	//打開串口
    if(fd==-1)
          printf("can not open the COM1!\n");
    else
          printf("open COM1 ok!\n");
    
    /*判斷是否是終端設備
    if(isatty(STDIN_FILENO) == 0)
      printf("不是終端設備\n");
    else
      printf("是終端設備\n");
    */
   if( fcntl(fd, F_SETFL, 0) <0 ) //改爲阻塞模式
      printf("fcntl failed\n");
    else
      printf("fcntl=%d\n", fcntl(fd, F_SETFL, 0));
    
    tcgetattr(fd, &options);

    //設置波特率
    cfsetispeed(&options, B115200);
    cfsetospeed(&options, B115200);
    
    //獲取波特率
    tcgetattr(fd, &newstate);
    baud_rate_i=cfgetispeed(&newstate);
    baud_rate_o=cfgetospeed(&newstate);

    //串口設置
    options.c_cflag |= (CLOCAL | CREAD);
    options.c_cflag &= ~PARENB;//設置無奇偶校驗位,
    options.c_cflag &= ~CSTOPB; //設置停止位1
    options.c_cflag &= ~CSIZE;
    options.c_cflag |= CS8; //設置數據位
    options.c_cc[VTIME]=0;//阻塞模式的設置
    options.c_cc[VMIN]=1;

    //激活新配置
    tcsetattr(fd, TCSANOW, &options);
    //輸出波特率
    printf("輸入波特率爲%d,輸出波特率爲%d\n" , baud_rate_i, baud_rate_o);
    //寫入串口
    
   wr_num=write(fd,buf3,sizeof(buf3));
  while(acc_cri)
  {
    int WENJIAN_Flag=0;
    rr_num=read(fd,buf1,sizeof(buf1));
    
     //printf("%d\r\n",rr_num);
  if(rr_num>1)
    {
      rec_time++;
      NAME1 = fopen("NAME.txt", "w");//打開姓名和密碼文件
      PASSWORD1 = fopen("PASSWORD.txt", "w");
      for(i=0;i<rr_num;i++)//循環讀取並儲存在數組
      {
        //printf("%c\r\n",buf1[i]);
        printf("外:%d\r\n",WENJIAN_Flag);
        if(32 == buf1[i])//接收到空格
        {
          //printf("iiiii\r\n");
          WENJIAN_Flag+=1;
          printf("內:%d\r\n",WENJIAN_Flag);
          continue;
        }
        if(WENJIAN_Flag==0)fprintf(NAME1, "%c", buf1[i]);//未接收到空格的動作
        else if(WENJIAN_Flag==1)//接收到空格的動作
		{
			fprintf(PASSWORD1, "%c", buf1[i]); 		
		}
        else //接收到兩次空格
        {
			rec_time=3;
			Error_time=1;
        }
      }
      fclose(NAME1);//關閉
      fclose(PASSWORD1);
      if(rec_time>=2)
      {
        
        acc_cri=0;//接收2次程序結束
		if(0==Error_time)
		{
			printf("程序正常結束儲存完畢\r\n");
			write(fd,buf,sizeof(buf));
		}
		if(1==Error_time)
		{
			printf("Error 1\r\n"); //接收到兩次空格報錯
			write(fd,error,sizeof(error));
			ERROR1 = fopen("ERROR.txt", "w");//用於儲存錯誤
			fprintf(ERROR1, "%c", buf2[0]);
			fclose(ERROR1);
		}
      }
    }
  }

    return 0;
}

2、shell 腳本連接無線網主體代碼(autoaccwifi.sh)

#!/bin/bash
cd ~/test
./serial1
test="NAME.txt"
test1="ERROR.txt"
time=0;
flag=1;
#若文檔爲空,則在while循環內等待實際上這樣的話,程序已經死了
while [ -e $test -a ! -s $test ]
do
	sleep 1
	echo "wait...$time"
	let time++
	#echo $time
done

if [ -e $test1 -a ! -s $test1 ];then
	flag=0
fi
sed -i '1d' ERROR.txt
echo "已刪除錯誤標誌位"
echo $flag
echo "正在接收..."
#此處由於串口最初接收數據不穩定,延時3s接收
sleep 3
for name in `cat NAME.txt`
do
 echo $name
done
for password in `cat PASSWORD.txt`
do
 echo $password
done
echo "接收完畢"
#獲取帳號和密碼
if (( $flag==0 ));then
	nmcli d wifi connect $name password $password
fi
#刪除帳號和密碼
sed -i '1d' NAME.txt
sed -i '1d' PASSWORD.txt
echo "已刪除賬戶和密碼"
#獲取IP地址並以文本形式保存到本地
if (( $flag==0 ));then
	IP=`ifconfig | grep "inet addr" | grep -v 127.0.0.1 | awk '{print}'`
	echo ${IP}
	sh -c "echo ${IP} > autogainip"
fi
./trx

sed -i '1d' autogainip
echo "已刪除儲存的ip"




3、c++發送當前ip到windows端(trx.cpp)

#include <iostream>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <unistd.h>
#include <cstring>
#include <fstream>
#include <cassert>
using namespace std;
int i;
int spa_time=0;//用於記錄space
int trx_time=0;//用於記錄
int rec_time=0;//用於記錄接收次數
int acc_cri=1;//允許死循環標誌位
int rr_num=0;//用於接收來自串口的數據



int main(int argc, char **argv)
{
    int fd,flag,wr_num=0;
    struct termios options, newstate;
    speed_t baud_rate_i,baud_rate_o;
    unsigned char buf[40]={}; //向串口發送的數組
    unsigned char buf1[100];
    fd=open("/dev/ttyUSB0", O_RDWR|O_NONBLOCK|O_NOCTTY|O_NDELAY);	//打開串口
    if(fd==-1)
          printf("can not open the COM1!\n");
    else
          printf("open COM1 ok!\n");
    
    /*判斷是否是終端設備
    if(isatty(STDIN_FILENO) == 0)
      printf("不是終端設備\n");
    else
      printf("是終端設備\n");
    */
   if( fcntl(fd, F_SETFL, 0) <0 ) //改爲阻塞模式
      printf("fcntl failed\n");
    else
      printf("fcntl=%d\n", fcntl(fd, F_SETFL, 0));
    
    tcgetattr(fd, &options);

    //設置波特率
    cfsetispeed(&options, B115200);
    cfsetospeed(&options, B115200);
    
    //獲取波特率
    tcgetattr(fd, &newstate);
    baud_rate_i=cfgetispeed(&newstate);
    baud_rate_o=cfgetospeed(&newstate);

    //串口設置
    options.c_cflag |= (CLOCAL | CREAD);
    options.c_cflag &= ~PARENB;//設置無奇偶校驗位,N
    options.c_cflag &= ~CSTOPB; //設置停止位1
    options.c_cflag &= ~CSIZE;
    options.c_cflag |= CS8; //設置數據位
    options.c_cc[VTIME]=0;//阻塞模式的設置
    options.c_cc[VMIN]=1;

    //激活新配置
    tcsetattr(fd, TCSANOW, &options);
    //輸出波特率
    printf("輸入波特率爲%d,輸出波特率爲%d\n" , baud_rate_i, baud_rate_o);
  while(acc_cri)
  {
	i=0;
    //readTxt("autogainip");
    ifstream infile; 
	string file="autogainip";
    infile.open(file.data());   //將文件流對象與文件連接起來 
    assert(infile.is_open());   //若失敗,則輸出錯誤消息,並終止程序運行 

    char c;
    infile >> noskipws;
	while(acc_cri)
	{
		while (!infile.eof())
		{
		    infile>>c;
			if(c==32)spa_time++;
		    if(spa_time<2)
			{
				//cout<<c;
				buf[i]=c;
				//cout<<buf[i];
				if(105 == buf[0])acc_cri=0;
				i++;
			}
			
			//for(i=0;i<wr_num;i++)
		}
	}
	wr_num=write(fd,buf,sizeof(buf));
	
	//for(i=0;i<)
	cout<<endl;
    infile.close();             //關閉文件輸入流 
    return 0;
}
}

四、配置流程

1、在home創建一個test文件夾

2、進入test,創建儲存代碼文件。shell腳本命名爲autoaccwifi.sh,c++接收串口代碼命名爲serial1.cpp,c++發送代碼命名爲trx.cpp。其實命名是依據你自己的喜好來的,但實際上是牽一髮動全身,你熟悉這個代碼之後隨意更改。

3、創建如圖所示文本,拓展名不可隨意更改,你熟悉了之後可以在代碼中隨意配置

4、編譯c++代碼,g++ serial1.cpp -o serial1和g++ trx.cpp -o trx

5、測試:硬件連接上,終端進入test,執行./autoaccwifi.sh;windows端串口助手發送兩次賬號和密碼,賬號和密碼以空格爲間距。

6、加入到開機選項。我是這樣想的,既然我們打算是在應用層實現功能,那我們就不要去打擾底層了,所以開機選項我們這樣添加。首先安裝gnome:sudo apt install gnome-session-bin;然後啓動:gnome-session-properties,添加autoaccwifi.sh路徑即可。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章