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


https://www.cnblogs.com/yangfengwu/p/11112015.html

 

先規定一下協議

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  卻沒有問題  , 大家都沒有遇到過這種情況嗎???求解答

我認為是編碼問題

但是我嘗試了

 

 好幾個編碼都會有問題,而且我用了手頭的所有調試助手都是有問題..所以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咱先不上菜...咱先簡簡單單的學

我給大家了一個計算工具

 

 

 

 

https://www.cnblogs.com/yangfengwu/p/11192594.html

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM