基于串口控制的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路径即可。

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