18-ESP8266 SDK開發基礎入門篇--TCP 服務器 RTOS版,串口透傳,TCP客戶端控制LED

http://www.javashuo.com/article/p-rrxsaeiu-x.htmlhtml

 

先規定一下協議api

aa 55 02 01 F1 4C 控制LED點亮  F1 4C爲CRC高位和低位
aa 55 02 00 30 8C 控制LED熄滅  30 8C爲CRC高位和低位數組

aa 55 03  佔空比(四字節 高位在前,低位在後) CRC校驗高位,CRC校驗低位緩存

 

預留一個問題  我用客戶端發送 aa 55 11 00 00 01 F4     WIFI接收到是  F4 01 00 00 00 01 F4 服務器

aa 55 10 XX XX XX  卻沒有問題  , 你們都沒有遇到過這種狀況嗎???求解答網絡

我認爲是編碼問題app

可是我嘗試了tcp

 

 好幾個編碼都會有問題,並且我用了手頭的全部調試助手都是有問題..因此PWM先不作了,先作控制LED點亮和熄滅函數

 

 

改一下協議工具

00 01 70 C0 控制LED點亮  70 C0  爲CRC高位和低位
00 00 B0 01 控制LED熄滅   B0 01  爲CRC高位和低位

 

 

首先修改一個小小小bug  ,其實這節咱用不到,只不過測試的時候發現的問題...

 

 

 

如今實現一個功能  透傳(TCP客戶端發送的數據,TCP服務器接收之後直接轉發到串口;串口接收的數據TCP服務器直接發給TCP客戶端)

首先說一下哈,咱的發送也用一個任務來執行

不過呢這個發送任務必定要在何時建立呢???

客戶端鏈接之後爲這個客戶端建立一個發送任務

 

 主要是避免,發送任務裏面的變量別一進來實際上是不存在的.......因此有了客戶端鏈接再建立這個任務

 

而後呢 ,還要解決一個問題,,假設客戶端掉線了,咱是否是應該刪除掉那個發送任務哈

因此

其實建立任務的時候最後一個變量,能夠填這個變量

 

 而後刪除這個任務就是

 

 

 

 

其實和上一版程序沒啥大區別,也就是上面的區別

還有一個是,我把之前局部的變量設置成全局的了

 

 

 對了其實

這兩個必定是全局,由於我發送的時候用獲得

 

 其實其它的就沒有什麼了...(前提你把我前面的文章都學了)

/*
 * ESPRSSIF MIT License
 *
 * Copyright (c) 2015 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
 *
 * Permission is hereby granted for use on ESPRESSIF SYSTEMS ESP8266 only, in which case,
 * it is free of charge, to any person obtaining a copy of this software and associated
 * documentation files (the "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the Software is furnished
 * to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in all copies or
 * substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 */

#include "esp_common.h"
#include "gpio.h"
#include "uart.h"
#include "esp_timer.h"
#include "hw_timer.h"
#include "pwm.h"
#include  "data_dispose.h"
#include  "espconn.h"
#include  "esp_wifi.h"
#include "lwip/api.h"


//typedef void (*UartCallback)(void);


extern u8  Usart1ReadBuff[Usart1ReadLen];//接收數據的數組
extern u32 Usart1ReadCnt;//串口1接收到的數據個數
extern u32 Usart1ReadCntCopy;//串口1接收到的數據個數拷貝
extern u8  Usart1ReadFlage;//串口1接收到一條完整數據


#define  SSID "Learn8266" //無線名稱
#define     PWD "11223344"     //密碼
struct softap_config soft_ap_Config;//AP模式配置


xTaskHandle xHandleTcpSendDate;//發送數據任務的句柄,我用來刪除發送任務用

err_t err;//接收操做全部API函數的時候返回的錯誤信息
struct netconn *conn, *newconn;//conn 保存自身TCP服務器的信息    newconn-保存鏈接客戶端的信息
int i = 0;
struct pbuf *q;//用此變量來操做鏈表,能夠看一下    https://www.cnblogs.com/yangfengwu/p/5778872.html
u32 data_len =0;//獲取接收的數據個數
unsigned char TcpRead[1024]={0};//接收數據緩存的數組,最大接收1024字節
struct netbuf *recvbuf;//建立接收數據的結構體,這是lwip提供的緩存數據用的


/******************************************************************************
 * FunctionName : user_rf_cal_sector_set
 * Description  : SDK just reversed 4 sectors, used for rf init data and paramters.
 *                We add this function to force users to set rf cal sector, since
 *                we don't know which sector is free in user's application.
 *                sector map for last several sectors : ABCCC
 *                A : rf cal
 *                B : rf init data
 *                C : sdk parameters
 * Parameters   : none
 * Returns      : rf cal sector
*******************************************************************************/
uint32 user_rf_cal_sector_set(void)
{
    flash_size_map size_map = system_get_flash_size_map();
    uint32 rf_cal_sec = 0;

    switch (size_map) {
        case FLASH_SIZE_4M_MAP_256_256:
            rf_cal_sec = 128 - 5;
            break;

        case FLASH_SIZE_8M_MAP_512_512:
            rf_cal_sec = 256 - 5;
            break;

        case FLASH_SIZE_16M_MAP_512_512:
        case FLASH_SIZE_16M_MAP_1024_1024:
            rf_cal_sec = 512 - 5;
            break;

        case FLASH_SIZE_32M_MAP_512_512:
        case FLASH_SIZE_32M_MAP_1024_1024:
            rf_cal_sec = 1024 - 5;
            break;

        default:
            rf_cal_sec = 0;
            break;
    }

    return rf_cal_sec;
}

//串口調用此函數就說明接收到了一條完整的數據,就能夠去處理了
void UartReadCallback()//定義一個函數
{

}

//發送數據任務
void TcpSendDateThread(void *date)
{
    conn->send_timeout=5;//設置發送超時時間,必定要加,不然任務就阻塞住了
    while(1)
    {
        if(Usart1ReadFlage)//串口接收到一條完整的數據
        {
            Usart1ReadFlage = 0;
            err = netconn_write(newconn ,Usart1ReadBuff,Usart1ReadCntCopy ,NETCONN_COPY);//發送數據
            if(err != ERR_OK)
            {
                //發送失敗
            }
        }
        vTaskDelay(10/portTICK_RATE_MS);//若是沒有數據要發送會執行這個,必定要加這個任務延時哈
    }
    vTaskDelete(NULL);
}



void TcpServerThread(void *date)
{
    static ip_addr_t ipaddr;//存儲客戶端的地址
    static u16_t port;//存儲客戶端的端口號

    conn = netconn_new(NETCONN_TCP);//建立一個TCP

    //注意哈,首先要明白你不管建立 TCP服務器或者客戶端,或者UDP,你建立的時候必須設置下TCP服務器或者客戶端,或者UDP的IP地址和端口號.
    //網絡之間通訊嘛,這是必須的,只有你有IP和端口號了,別人才能和你通訊
    netconn_bind(conn,IP_ADDR_ANY,8888);  //設置conn(TCP服務器) 的IP地址是本身網卡上的IP  設置TCP服務器通訊的端口號是8888   (不管建立 TCP服務器或者客戶端,或者UDP,都是必須的)

    netconn_listen(conn);  //使用監聽函數,說明是建立TCP服務器,只有做爲服務器纔是監聽客戶端鏈接嘛

    //設置任務阻塞時間爲10ms  (注意哈,這個和vTaskDelay(10/portTICK_RATE_MS)相似,可是必定要用這個
    //下面的netconn_accept(conn,&newconn);函數是徹底阻塞的,,若是你不設置conn->recv_timeout  程序就中止在那裏了,除非有客戶端鏈接
    conn->recv_timeout=5;//任務延時5ms
    while(1)
    {
        err = netconn_accept(conn,&newconn);//等待客戶端鏈接,有客戶機鏈接,或者超時了就會往下執行
        if (err == ERR_OK)//只有客戶機鏈接了,而且沒有其它錯誤纔會進入
        {
            netconn_getaddr(newconn,&ipaddr,&port,0); //獲得客戶端的IP地址和端口號     最後一個參數  1獲取本地IP地址,0獲取遠程IP地址
            //打印客戶端的IP地址
            printf("ClientIP:%d.%d.%d.%d Connected\n",(uint8_t)(ipaddr.addr),(uint8_t)(ipaddr.addr >> 8),(uint8_t)(ipaddr.addr >> 16),(uint8_t)(ipaddr.addr >> 24));
            printf("Port:%d\n",port);//打印客戶端的端口號


            xTaskCreate(TcpSendDateThread, "TcpSendDateThread", 1024, NULL, 9, &xHandleTcpSendDate);//建立發送數據任務

            while(1)//一直在這個裏面接收處理數據
            {
                err = netconn_recv(newconn,&recvbuf);//若是一直接受不到數據或者不是其它錯誤信息,不會往下執行
                if(err== ERR_OK)//接收到客戶端發過來的數據
                {
                    taskENTER_CRITICAL();//關閉中斷,禁止其它任務打斷,防止讀數據出現錯誤

                    data_len = 0;
                    for( q = recvbuf->p; q != NULL; q = q->next )  //遍歷完整個pbuf鏈表
                    {
                        //判斷要拷貝到緩存數組中的數據是否大於緩存數組的剩餘空間,若是大於
                        //的話就只拷貝緩存數組中剩餘長度的數據,不然的話就拷貝全部的數據
                        if( q->len > ( 1024-data_len ) )
                              memcpy(TcpRead+data_len,q->payload,(1024-data_len));//拷貝數據
                        else
                                memcpy( TcpRead+data_len, q->payload, q->len );
                        data_len += q->len;
                        if(data_len > 1024)//超出TCP客戶端接收數組,跳出
                        break;
                    }

                    taskEXIT_CRITICAL();//打開中斷

                    for(i=0;i<data_len;i++)
                    {
                        USART_SendData(UART0, TcpRead[i]);//接收的數據發給串口
                    }
                }
                else if(err == ERR_CLSD)  //客戶端斷開鏈接
                {
                    vTaskDelete(xHandleTcpSendDate);//刪除發送數據任務
                    netconn_close(newconn);//關閉鏈接
                    netconn_delete(newconn);//刪除鏈接
                    printf("ClientIP:%d.%d.%d.%d Disconnected\n",(uint8_t)(ipaddr.addr),(uint8_t)(ipaddr.addr >> 8),(uint8_t)(ipaddr.addr >> 16),(uint8_t)(ipaddr.addr >> 24));
                    break;//退出
                }
            }
        }
        else
        {
            conn->recv_timeout=10;//任務延時10ms
        }
    }
    vTaskDelete(NULL);
}



void LedThread(void *date)
{
    while(1)
    {
        vTaskDelay(1000/portTICK_RATE_MS);
        GPIO_OUTPUT_SET(2,1-GPIO_INPUT_GET(2));
    }
    vTaskDelete(NULL);
}

/******************************************************************************
 * FunctionName : user_init
 * Description  : entry of user application, init user function here
 * Parameters   : none
 * Returns      : none
*******************************************************************************/
void user_init(void)
{
    GPIO_OUTPUT_SET(5, 1);
    GPIO_OUTPUT_SET(2, 0);//讓兩個燈初始的狀態同樣,GOIO2是反接的,0的時候是亮
//    GPIO_OUTPUT_SET(5, 0);
    uart_init_new();
    printf("SDK version:%s\n", system_get_sdk_version());
//    printf("Ai-Thinker Technology Co. Ltd.\r\n%s %s\r\n", __DATE__, __TIME__);
//    printf("Hello,World!\r\n");
//    xTaskCreate(LedControl, "LedControl", 1024, NULL, 11, NULL);
    wifi_set_opmode(STATIONAP_MODE);//配置WiFi的模式STATION + AP AP--鏈接WIFI自身的無線實現通訊  STATION--wifi鏈接路由器,手機或者電腦也鏈接路由器,實現通訊
    soft_ap_Config.ssid_len = strlen(SSID);//熱點名稱長度,與你實際的名稱長度一致就好
    memcpy(soft_ap_Config.ssid,SSID,soft_ap_Config.ssid_len);//實際熱點名稱設置,能夠根據你的須要來
    memcpy(soft_ap_Config.password,PWD,strlen(PWD));//熱點密碼設置
    soft_ap_Config.authmode = AUTH_WPA2_PSK;//加密模式
    soft_ap_Config.channel = 1;//信道,共支持1~13個信道
    soft_ap_Config.max_connection = 4;//最大鏈接數量,最大支持四個,默認四個

    wifi_softap_set_config_current(&soft_ap_Config);//設置 Wi-Fi SoftAP 接口配置,不保存到 Flash
    //    wifi_softap_set_config(&soft_ap_Config);//設置 Wi-Fi SoftAP 接口配置,保存到 Flash

    //下面的函數你們點進去一看註釋就明白了
//    espconn_init();//"espconn.h"   195行
//    TcpServer.type = ESPCONN_TCP;     //建立TCP
//    TcpServer.state = ESPCONN_NONE;   //一開始的狀態
//    TcpServer.proto.tcp = &esptcp;    //設置TCP的IP和回調函數存儲用
//    TcpServer.proto.tcp->local_port = 8888;//監聽的端口號
//    espconn_regist_connectcb(&TcpServer, TcpServerListen);//註冊
//    espconn_accept(&TcpServer);//啓動監聽
    UartCallbackRegister(UartReadCallback);//把 UartReadCallback 函數地址傳過去,在串口裏面調用


    xTaskCreate(TcpServerThread, "TcpServerThread", 1024, NULL, 8, NULL);


    xTaskCreate(LedThread, "LedThread", 1024, NULL, 11, NULL);
}

 

測試

       

 

 

 

 

 如今寫控制燈亮滅的程序

00 01 70 C0 控制LED點亮  70 C0  爲CRC高位和低位
00 00 B0 01 控制LED熄滅   B0 01  爲CRC高位和低位

 

最簡單的

 

 

//                    00 01 70 C0 控制LED點亮  70 C0  爲CRC高位和低位
//                    00 00 B0 01 控制LED熄滅   B0 01  爲CRC高位和低位
                    if(TcpRead[0] == 0)
                    {
                        if(TcpRead[1] == 0x01 && TcpRead[2] == 0x70 && TcpRead[3] == 0xC0 )//控制LED點亮
                        {
                            GPIO_OUTPUT_SET(5,1);
                        }
                        else if(TcpRead[1] == 0x00 && TcpRead[2] == 0xB0 && TcpRead[3] == 0x01 )//控制LED熄滅
                        {
                            GPIO_OUTPUT_SET(5,0);
                        }
                    }

 

測試

 

 

 

 

 

 

 

 

 好下節咱作C# TCP客戶端,控制LED

 

說一下哈  那個CRC咱先不上菜...咱先簡簡單單的學

我給你們了一個計算工具

 

 

 

 

http://www.javashuo.com/article/p-mjnxnecd-r.html

相關文章
相關標籤/搜索