基於8266WIFI模組實現智慧手機與51微控制器的通訊入門

IamDaodaoLi發表於2018-03-28

1  晶片及電路的介紹

1.1  8266WIFI模組

      在文章的最開始我們首先要介紹今天的主角,8266WIFI模組。這是一個非常好用的模組,其他的廢話不在多說,我們直接上乾貨.

                                     

      這是一張我從百度上盜的一張8266WIFI模組的原理圖,雖然8個引腳看著很複雜,但實際上我們只會用到最多五個引腳(1.VCC 2.GND 3.UTXD 4.URXD 5.CH_PD)。為什麼說最多用到五個引腳呢,因為我在網上查資料的時候很多人只是用到了四個引腳,並沒有使用CH_PD這個引腳,但是具體是什麼原因呢,我們在這個入門篇就不進行深入的討論了。我們們只要記住,如果只接了四個引腳不行的話那麼我們就考慮一下接五個引腳。

      下面我們來說一下五個引腳是如何連線的。首先電源和接地是必須要連的,VCC引腳接3V左右的電源,GND接地,UTXD接微控制器的RXD,URXD接微控制器的TXD,CH_PD引腳也需要接3V的電源(有的人不需要接也可以工作,但是我的那個模組不行)。這裡要說明一下,我們51微控制器的工作電壓為5V,而8266WIFI模組的工作電壓為3V,如果8266WIFI模組也接到5V的電源的話很有可能會燒壞,這裡我們需要注意一下。還有在實際的操作時有一個參考電壓的問題,在下文我們在詳細講解。下邊一張又醜又簡單的接線圖送給大家。

                                                     

      說完了接線之後我們還需要了解一下關於8266WIFI模組的AT指令,如何讓8266WIFI模組工作,這是微控制器驅動8266WIFI模組過程中一個必須要了解的問題(敲黑板,劃重點了)。8266WIFI模組的AT指令有很多,但是我們用到的其實也就只有那麼幾個,下邊我們來一一介紹。

      1、AT+CIOBAUD=4800——這是改變波特率的指令這是非常重要的一個指令,我們買來的8266WIFI模組大多數的波特率都是115200(我買的那個就是),而微控制器達不到這麼高的波特率,所以我們要做的第一步就是設定波特率。當然了,波特率的設定必須是要在電腦上完成的,我用的軟體是安可信串列埠除錯助手這個軟體,用這個軟體的時候我們要記得把波特率調到115200才可以進行通訊,如果各位買到的8266WIFI模組在115200這個波特率下無法與電腦實現通訊,那麼就需要聯絡一下賣家了。

    2、AT+CWMODE=2——這是模式設定指令,8266WIFI模組有三種工作模式,AT+CWMODE=1為Station模式,AT+CWMODE=2為AP模式,AT+CWMODE=3為Station+AP模式。那麼什麼是Station模式呢,說白了就是這個WIFI模組可以連線別的WIFI訊號,這個模式在我們這一次的設計中應用的不多,所以我們就先不做詳細的講解。AP模式是我們要講解的重點,AP模式也就是我們可以發射WIFI訊號,別的手機呀電腦呀可以連線我們的8266WIFI模組。那Station+AP模式大家就瞭然於胸了吧,也就是我們既可以連線別的WIFI訊號又可以傳送訊號讓別人連,但是由於韌體的原因,我們並不能實現8266WIFI模組連線了別的有網的WIFI訊號後,有手機在連線8266WIFI模組後手機就可以上網了,這個功能是不能實現的,這段話看著挺繞的,實際上就是8266WIFI模組不能做WIFI網路的訊號中繼器(具體叫什麼功能我也不太清楚,大概就是這個意思吧,哈哈,這不是重點)。實際上在今後的開發中,由於雲伺服器的存在,我們需要用到Station+AP模式的次數還是很多的。在這個入門設計中我們先不說,大家可以關注我們部落格,我還會發別的設計講解的。

      3、AT+CWSAP="名稱",“密碼”,通道,加密方式——這一指令就是設定WIFI訊號名稱和密碼還有加密方式的一個指令,通道可以設定成3,加密方式建議設定成4,因為某米手機的某些原因導致其他加密方式連不上WIFI訊號,這是我在實驗過程中遇到的問題,不知道別人是否也遇到過這樣的問題。我再來說一下加密方式的數字都代表了什麼意思,0是沒有密碼的意思,1是WEP加密方式,2是WPA_PSK加密方式,3是WPA2_PSK加密方式,4是WPA_WPA2_PSK加密方式。

      4、AT+CIPMUX=1——這個指令為設定多路,如果1變為零的話就是設定單路,但是我們在這裡必須要設定為多路才能進行以後的工作。

      5、AT+CIPSERVER=1,8080——這個指令是設定為伺服器模式,數字1代表著開始,數字8080代表著埠號,這個數字可以隨便設定,但是為了防止和電腦系統中其他程式的埠衝突我們還是建議設定成大一些的數字。

      我們在微控制器驅動8266WIFI模組的程式中基本上就只用到了這幾個指令,其他指令我們不在多說。有一點我們要特別注意,第一條指令和第三條指令需要在電腦上完成。還有一點很重要,就是WIFI模組斷電後第4條指令和第5條指令需要重新設定,所以我們在微控制器程式編寫的時候要進行編寫。

1.2 51微控制器

      我用的51微控制器是STM公司出的微控制器,這款微控制器有一個好處就是可以直接從RXD和TXD引腳直接進行通訊,最小系統的構建相對來說比較簡單,只涉及到了晶振電路、復位電路以及EA/VPP端的高電平。下面我們直接上圖,哈哈,原理圖非常簡單,我就又盜了一張,大家將就著看。

      這張原理圖基本上已經是比較全的了,不過我在EA/VPP端接高電平的時候還加了一個10K歐姆的電阻進行限流。在這張原理圖中,我們需要注意的就是晶振電路一定要有,不過這都是常識了,沒有晶振微控制器就不工作啦。其實微控制器我們在學校的學習中已經是非常的熟悉了。在本設計中,對於微控制器的知識也就只涉及到了中斷、波特率的設定、IO口的輸出還有RXD埠和TXD埠的資料讀寫。就這些知識點而言可以說是非常簡單了,任何一本教科書都有講解。

2  軟體編寫

2.1 微控制器軟體編寫

      廢話不多說,直接上程式。這是一個非常簡單的測試程式,可以直接跑通。在這個程式裡有一點要注意,就是微控制器波特率的設定問題。

#include<reg52.h>
#include<string.h>
sbit L1 = P1^0;
sbit kaiguan = P3^4;
sbit L2 = P1^1;
sbit L3 = P1^2;
sbit L4 = P1^3;
sbit L5 = P1^4;
sbit L6 = P1^5;
sbit L7 = P1^6;
sbit L8 = P1^7;
unsigned char Usart_Receive[20]={0};
unsigned char Usart_Cnt=0;
bit Usart_AT_flage;
unsigned char code RST[]="AT+RST\r\n"; 
unsigned char code CWMODE2[]="AT+CWMODE=2\r\n";
unsigned char code CIPMUX[]="AT+CIPMUX=1\r\n";
unsigned char code CIFSR[]="AT+CIFSR\r\n";
unsigned char code CIPSERVER[]="AT+CIPSERVER=1,8080\r\n";
/**********************************************************/
void delay_ms(unsigned long t)
{
	unsigned int x,y;
	for(x=t;x>0;x--)
	{
		for(y=110;y>0;y--)
		{
		}
	}
}
/**********************************************************/
void delays(void)
{
	unsigned char a,b,c;
	for(c=95;c>0;c--)
	{
		for (b=26;b>0;b--)
		{
			for (a=185;a>0;a--)
			{
			}
		}
	}
}
/***************中斷設定***********************************/
void InitUART(void)
{
	TMOD=0x20;
	SCON=0x50;
/***************波特率設定*********************************/
	TH1=0xFA;
	TL1=TH1;
	PCON=0x00;
	EA=1;
	ES=1;
	TR1=1;
}
/*************傳送資料***************************************/
void Send_Uart(unsigned char value)
{
	ES=0;
	TI=0;
	SBUF=value;
	while(TI==0);
	TI=0;
	ES=1;
}
/*************接受資料***************************************/
void UARTInterrupt(void)interrupt 4
{
	if(RI)
	{
		RI=0;
		Usart_Receive[Usart_Cnt]=SBUF;
		Usart_Cnt++;
		if(Usart_Receive[Usart_Cnt-2]=='\r'&&Usart_Receive[Usart_Cnt-1]=='\n'&&Usart_Cnt>=2)
		{
			Usart_Cnt=0;
			Usart_AT_flage=1;
		}
		else if(Usart_Cnt>20)
		{
			Usart_Cnt=0;
		}
	}
}
/*****************傳送設定**********************************/
void ESP8266_Set(unsigned char *puf)
{
    while(*puf!='\0')
    {
        Send_Uart(*puf);
        puf++;
    }
}
/**************設定多連*************************************/
void ManyConnect_AP()
{
	L4=0;
	ESP8266_Set(RST);//返回一大溜,不用判斷返回
    delays();
    delays();
	while(1)
	{
		ESP8266_Set(CWMODE2);//返回ok
        delays();
        if(Usart_AT_flage ==1)
		{
			if(strstr(Usart_Receive, "OK") )
			{
				Usart_AT_flage = 0;
                L1 = 0;
                break;
			}
		}
	}
	while (1)
	{
		ESP8266_Set(CIPMUX);//返回ok
        delays();
        if(Usart_AT_flage ==1)
		{
			if(strstr(Usart_Receive, "OK") )
			{
				Usart_AT_flage = 0;
                L2 = 0;
                break;
			}
		}
	}
	while (1)
	{
		ESP8266_Set(CIPSERVER);//返回ok,多了也返回ok
        delays();
        if(Usart_AT_flage ==1)
		{
			if(strstr(Usart_Receive, "OK") )
			{
				Usart_AT_flage = 0;
                L3 = 0;
                break;
			}
		}
	}
}
/***************延時500ms***************************/
void delays500ms(void)
{
	unsigned char a,b,c;
    for(c=123;c>0;c--)
	{
        for(b=212;b>0;b--)
		{
            for(a=25;a>0;a--)
			{
			}
		}
	}
}
/**************主函式**********************************/
void main()
{
	int i;
	P2=0x00;
	kaiguan=0;
	InitUART();
	while(1)
	{
		ManyConnect_AP();
		while(1)
		{
			 //由於訊息的開頭是+IP  故做此判斷,00000000000000000號
            if((Usart_Receive[0]=='+')&&(Usart_Receive[1]=='I')&&(Usart_Receive[2]=='P'))
            {
                if((Usart_Receive[3]=='D')&&(Usart_Receive[6]==','))
                {
                    if(Usart_Receive[9]=='1')
                    {
                        delays500ms();
                        P2=0xFF;
                        delays();
                        for(i = 0 ; i<20; i++)
                        {
                            Usart_Receive[i]=' ';
                        }
					}
					if(Usart_Receive[9]=='0')
					{
						P2=0x00;
						delays();
						for(i=0;i<20;i++)
						{
							Usart_Receive[i]=' ';
						}
					}
				}
			}
		}
	}
}
	unsigned int x,y;
	for(x=t;x>0;x--)
	{
		for(y=110;y>0;y--)
		{
		}
	}
}
/**********************************************************/
void delays(void)
{
	unsigned char a,b,c;
	for(c=95;c>0;c--)
	{
		for (b=26;b>0;b--)
		{
			for (a=185;a>0;a--)
			{
			}
		}
	}
}
/***************中斷設定***********************************/
void InitUART(void)
{
	TMOD=0x20;
	SCON=0x50;
/***************波特率設定*********************************/
	TH1=0xFA;
	TL1=TH1;
	PCON=0x00;
	EA=1;
	ES=1;
	TR1=1;
}
/*************傳送資料***************************************/
void Send_Uart(unsigned char value)
{
	ES=0;
	TI=0;
	SBUF=value;
	while(TI==0);
	TI=0;
	ES=1;
}
/*************接受資料***************************************/
void UARTInterrupt(void)interrupt 4
{
	if(RI)
	{
		RI=0;
		Usart_Receive[Usart_Cnt]=SBUF;
		Usart_Cnt++;
		if(Usart_Receive[Usart_Cnt-2]=='\r'&&Usart_Receive[Usart_Cnt-1]=='\n'&&Usart_Cnt>=2)
		{
			Usart_Cnt=0;
			Usart_AT_flage=1;
		}
		else if(Usart_Cnt>20)
		{
			Usart_Cnt=0;
		}
	}
}
/*****************傳送設定**********************************/
void ESP8266_Set(unsigned char *puf)
{
    while(*puf!='\0')
    {
        Send_Uart(*puf);
        puf++;
    }
}
/**************設定多連*************************************/
void ManyConnect_AP()
{
	L4=0;
	ESP8266_Set(RST);//返回一大溜,不用判斷返回
    delays();
    delays();
	while(1)
	{
		ESP8266_Set(CWMODE2);//返回ok
        delays();
        if(Usart_AT_flage ==1)
		{
			if(strstr(Usart_Receive, "OK") )
			{
				Usart_AT_flage = 0;
                L1 = 0;
                break;
			}
		}
	}
	while (1)
	{
		ESP8266_Set(CIPMUX);//返回ok
        delays();
        if(Usart_AT_flage ==1)
		{
			if(strstr(Usart_Receive, "OK") )
			{
				Usart_AT_flage = 0;
                L2 = 0;
                break;
			}
		}
	}
	while (1)
	{
		ESP8266_Set(CIPSERVER);//返回ok,多了也返回ok
        delays();
        if(Usart_AT_flage ==1)
		{
			if(strstr(Usart_Receive, "OK") )
			{
				Usart_AT_flage = 0;
                L3 = 0;
                break;
			}
		}
	}
}
/***************延時500ms***************************/
void delays500ms(void)
{
	unsigned char a,b,c;
    for(c=123;c>0;c--)
	{
        for(b=212;b>0;b--)
		{
            for(a=25;a>0;a--)
			{
			}
		}
	}
}
/**************主函式**********************************/
void main()
{
	int i;
	P2=0x00;
	kaiguan=0;
	InitUART();
	while(1)
	{
		ManyConnect_AP();
		while(1)
		{
			 //由於訊息的開頭是+IP  故做此判斷,00000000000000000號
            if((Usart_Receive[0]=='+')&&(Usart_Receive[1]=='I')&&(Usart_Receive[2]=='P'))
            {
                if((Usart_Receive[3]=='D')&&(Usart_Receive[6]==','))
                {
                    if(Usart_Receive[9]=='1')
                    {
                        delays500ms();
                        P2=0xFF;
                        delays();
                        for(i = 0 ; i<20; i++)
                        {
                            Usart_Receive[i]=' ';
                        }
					}
					if(Usart_Receive[9]=='0')
					{
						P2=0x00;
						delays();
						for(i=0;i<20;i++)
						{
							Usart_Receive[i]=' ';
						}
					}
				}
			}
		}
	}
}

2.2 手機端軟體編寫

      廢話不多說,先上程式。

      首先,我先發一個Content_main的程式,這個段程式碼很簡單,只有三個按鈕以及一個Edittext,但是EditText這個功能並沒有用到。在以後開發中會用到,在這裡先埋下一個伏筆。

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <Button
        android:id="@+id/button_1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="連線"/>
    <Button
        android:id="@+id/button_2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="開燈"/>
    <Button
        android:id="@+id/button_3"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="關燈"/>
    <EditText
        android:id="@+id/text_1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="顯示開關狀態"/>
</LinearLayout>

      然後在上ActivityCollector和BaseActivity程式碼,這段程式碼的作用是對活動進行管理。Android開發我也是剛剛接觸,正在摸索階段,這段程式碼是我在《第一行程式碼》這本書上學習到的,哈哈,學了就多用一用才能熟悉掌握。其他的我也不在多說了,大家就看一看程式碼,其實很好理解。

import android.app.Activity;
import java.util.ArrayList;
import java.util.List;

public class ActivityCollector {
    public static List<Activity> activities=new ArrayList<>();
    public static void addActivity(Activity activity){
        activities.add(activity);
    }
    public static void removeActivity(Activity activity){
        activities.remove(activity);
    }
    public static void finishAll(){
        for (Activity activity:activities){
            if(!activity.isFinishing()){
                activity.finish();
            }
        }
        activities.clear();
    }
}
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;


public class BaseActivity extends AppCompatActivity {
    protected void onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState);
        ActivityCollector.addActivity(this);
    }
    protected void onDestroy(){
        super.onDestroy();
        ActivityCollector.removeActivity(this);
    }
}

      下邊我們在來一段MainActivity的程式碼,這段程式碼中就涉及到了Socket方面的知識了,其實我也是一個程式設計小白,好多東西也都是現學現賣。我在程式碼的下邊會稍微講解一下關於Socket方面的知識,很膚淺的一些東西,大家就參考的去看一下,求輕噴,哈哈。

import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;

import java.io.BufferedReader;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.net.Socket;

public class MainActivity extends BaseActivity {
    private Button button1;
    private Button button2;
    private Button button3;
    public Socket socket;
    private EditText text1;
    private static final String HOST="192.168.4.1";
    private PrintWriter printWriter;
    private BufferedReader br;

    protected void onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        button1=(Button)findViewById(R.id.button_1);
        button2=(Button)findViewById(R.id.button_2);
        button3=(Button)findViewById(R.id.button_3);
        text1=(EditText)findViewById(R.id.text_1);
        button1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            socket=new Socket(HOST,8080);
                        }catch (Exception e){
                            e.printStackTrace();
                        }
                    }
                }).start();
            }
        });
        button2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            PrintStream ps=new PrintStream(socket.getOutputStream());
                            ps.println("1");
                        }catch (Exception e){
                            e.printStackTrace();
                        }
                    }
                }).start();
            }
        });
        button3.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            PrintStream ps=new PrintStream(socket.getOutputStream());
                            ps.println("0");
                        }catch (Exception e){
                            e.printStackTrace();
                        }
                    }
                }).start();
            }
        });
    }
}

      這段程式碼其實在我測試完了之後發現了很多問題,但是也可以跑通,暫時就先不做優化了,等開發下一個功能的時候在去做深入的優化。我先來說一下我發現的問題吧,大家參考的時候可以自己優化一下。首先,第一個問題就是在開燈和關燈的時候我感覺應該不開啟執行緒其實也能執行,不過我沒試,也不是很懂,希望各位大神可以在帖子下邊留言,哈哈,我也還是在學習的階段,有不正確的地方還請多多指教。其次還有一個問題就是開關按鍵可以整合成一個按鍵。暫時我就想到了這兩個問題,其他問題慢慢再去發掘。

      其實我寫的程式碼非常簡單易懂,哈哈,畢竟我也是個小白嘛。下面我們就來很膚淺的說一下關於Socket套接字方面的知識,首先,我是基於TCP協議來使用8266WIFI模組的,而TCP會有服務端還有客戶端,我是本著讓8266WIFI模組作為服務端,手機作為客戶端的思想來進行開發的。所以在我們手機端就用到了TCP客戶端的程式設計程式碼。其次,我們來說一下Socket套接字的工作流程,其實我學習程式設計的學習方法是掌握一些類或方法或者硬體的工作流程來進行學習的,而Socket套接字的工作流程就是先建立Socket方法的例項,其中引數帶入IP地址還有埠,這是第一步,完成這一步我們的客戶端和伺服器就算是連線上了。然後第二步就是資料的傳送和接收,我們現在的設計中暫時沒有涉及到接受資料的知識,所以不在講解,我們只來說一下傳送資料的方法。傳送資料需要用到PrintStream方法以及getOutputStream方法。這兩個方法是傳送資料的關鍵,這一步做完之後直接呼叫Println方法來放入要存入的資料或指令。其實我們結合著微控制器的程式碼可以發現,我們微控制器只是判斷接受到的資料到底是什麼如果是1就開燈,如果是0就關燈。其實思想很簡單。

3  開發過程中所遇到的問題

3.1 8266WIFI模組測試問題

      當我買來8266WIFI模組的第一步當然是測試一下模組的功能都有什麼了,那時候基本上是啥也不懂,就在網上看一些資料後就開始操作,這時候問題來了。我在買8266WIFI模組之前手裡有一個TTL串列埠的連線線,我一開始以為用這個可以除錯8266WIFI模組的,但是後來發現並不能,可能有的人可以用TTL串列埠連線線除錯,但是我這個是真的遇到問題了,查了一下資料發現,實際上是TTL串列埠連線線的電壓不穩定,導致8266WIFI模組不能正常工作。

 

      解決方法:在某寶上買了一個專門除錯8266WIFI模組的模組,然後問題解決了。

3.2 8266WIFI模組和微控制器電源的問題

      在開發的過程中我遇到了一個問題就是,微控制器和8266WIFI模組無法正常通訊,而且波特率也調的一致了,在電腦上用串列埠助手測試的也都通過了,但是微控制器和8266WIFI模組一接線就不能用,後來查了一些資料發現,有一個參考電壓的問題,也就是說,微控制器和8266WIFI模組必須要共接地,這樣才能實現通訊。我遇到這個問題的原因是因為8266WIFI模組的工作電壓是3V,而微控制器的工作電壓是5V,所以我找了兩個電源進行供電,這時候問題就出來了。哈哈,這是一個比較低階的錯誤。

4  總結

      在開發的過程中也是遇到了一些問題,但是也都解決了,這次的設計只是一個非常入門的設計,用到的網路也是區域網,操作指令也很簡單,傳輸的資料量也很小,而且沒有涉及到手機接收微控制器發來的資訊的功能。總而言之就是很簡單,下一步的設計我打算做一些涉及到公共網路而且有接收有傳送的設計,而且Android軟體方面也會做一些優化。希望大家可以關注。哈哈,我只是普通學生一枚,不是大神,請各位多多指教,輕噴~輕噴~~哈哈,我會繼續學習的。

 

相關文章