--[[*****************************Copyright(c)**********************************
**                Guangzhou DACAIGAUNGDIAN Technology Co. Ltd
**
**                             http://www.gz-dc.com
**
**------File Info-----------------------------------------------------------
** File Name:               esp32_c3.lua
** Latest modified Date:    2021/10/22
** Latest Version:          
** Description:             4G模块LUA函数库。对AT命令进行封装。
**
**--------------------------------------------------------------------------
** Created By:              刘仁武
** Created date:            2020/08/21
** Version:                 V0.1
** Descriptions:            
**
***************************************************************************--]]

local esp_debug_mode = 0

local esp_at_timer_id = 20              --发送AT的超时定时器

--存储接受数据的 string 类型
local esp_resp_string = ''          --当前接受的数据
local esp_resp_string_last = ''     --上一次接受的数据

--存储接受数据的 table 类型，在 HTTPHEAD 、 HTTPREAD 时使用 
local http_resp_data = nil      --当前接受的数据
local http_resp_data_count = 0  --应接受的数据总长度
local http_resp_data_idx = 0    --当前接受的数据长度

--回调函数
local esp_send_callback = nil       --发送回调函数
local esp_resp_callback = nil       --所有4G返回的数据的回调函数
local esp_log_callback = nil        --调试信息回调函数

--[[***************************************************************************
** Function name:  my_http_print
** Descriptions:   打印虚拟屏调试信息，设置 esp_en_debug = 1
                   注意：实体屏运行时， esp_en_debug 不能等于 1
                   打印实体屏调试信息，设置 esp_en_debug = 2
                   注意：虚拟屏运行时，esp_en_debug 不能等于 2
***************************************************************************--]]
local esp_en_debug = 0     --0-不显示调试信息;1-虚拟屏; 2-实体屏，串口1输出

--[[***************************************************************************
** Function name:  esp_log
** Descriptions :  调试信息回调函数
** @msg         :  调试信息
***************************************************************************--]]
function esp_log(msg)
    if esp_log_callback~= nil
    then
        esp_log_callback(msg)
    end
    if esp_en_debug == 1
    then
        print(msg)
    end
    if esp_en_debug == 2
    then
        uart_send_string(msg)
        uart_send_string('\r\n')
    end
end

function esp32_send_string(data_string)
esp_log('esp32_send_string='..esp_debug_mode)
    if esp_debug_mode == 0 then
        uart_send_string3(data_string)
    elseif esp_debug_mode == 1 then
        uart_send_string(data_string)
    end
end

function esp32_send_table(data_table)
    if esp_debug_mode == 0 then
        uart_send_data3(data_table)
    elseif esp_debug_mode == 1 then
        uart_send_data(data_string)
    end
end

--[[***************************************************************************
** Function name:  esp_set_callback
** Descriptions :  设置回调函数
** @send_cb     :  向4G发送数据的回调函数
** @resp_cb     :  所有4G返回的数据的回调函数
** @log_cb      :  调试信息回调函数
***************************************************************************--]]
function esp_set_callback(send_cb,resp_cb,log_cb)
    esp_send_callback = send_cb
    esp_resp_callback = resp_cb
    esp_log_callback = log_cb
end

-----------------对4G模块的命令发送队列 BEGIN--------------------------
--对4G模块的命令发送队列
EspList = {}

--[[***************************************************************************
** Function name:  EspList.new
** Descriptions :  初始化命令队列
***************************************************************************--]]
function EspList.new()
    return {first = 0, last = -1}
end

--[[***************************************************************************
** Function name:  EspList.pushBack
** Descriptions :  填充队列
** @EspList        :  被填充的队列
** @value       :  填入的数据
***************************************************************************--]]
function EspList.pushBack(EspList, value)
    local last = EspList.last + 1
    EspList.last = last
    EspList[last] = value
end

--[[***************************************************************************
** Function name:  EspList.popFront
** Descriptions :  提取队列中的第一个信息
** @EspList        :  被提取的队列
** @return      :  被提取的数据
***************************************************************************--]]
function EspList.popFront(EspList)
    
    local first = EspList.first
    
    if first > EspList.last then
        return nil
    end
    
    local value = EspList[first]
    EspList[first] = nil
    EspList.first = first + 1
    
    return value
end
----------------对4G模块的命令发送队列 END--------------------------------

local esp_cmd_list = EspList.new()         --命令队列
local esp_cmd_currunt = nil             --当前发送的命令

--[[***************************************************************************
** Function name:  esp_cmd_add
** Descriptions :  命令添加到队列，当前没有已发送的命令时，发送添加的第一条命令
** @sendstr     :  发送的命令
** @ackstr      :  4G模块对命令的返回的数据
** @timeout     :  返回数据的超时时间
** @retry       :  命令超时的重发次数
** @callback    :  数据返回时的回调函数
***************************************************************************--]]
function esp_cmd_add(sendstr,ackstr,timeout,retry,callback)
    local cmd = {}

    if timeout==nil
    then timeout = 0 end

    if retry==nil
    then retry = 0 end

    cmd.send = sendstr
    cmd.ack = ackstr
    cmd.timeout = timeout  
    cmd.retry = retry
    cmd.callback = callback

    EspList.pushBack(esp_cmd_list,cmd)

    --当前没有指令在执行，立即执行一次
    if esp_cmd_currunt==nil then
        esp_cmd_list_run_next()
    end

end

--[[***************************************************************************
** Function name:  esp_cmd_list_timeout
** Descriptions :  命令超时的处理函数
***************************************************************************--]]
function esp_cmd_list_timeout()
    esp_log('esp_cmd_list_timeout()')
    --当前没有命令时退出
    if esp_cmd_currunt==nil 
    then 
        return 
    end
    
    --重发次数大于0时，重发命令
    if esp_cmd_currunt.retry>0 then
        --重发次数递减
        esp_cmd_currunt.retry = esp_cmd_currunt.retry-1
        esp_cmd_list_run()                                          --重发
    else
        --重发次数小于0时
        if esp_cmd_currunt.callback~=nil
        then
            esp_cmd_currunt.callback('timeout',esp_cmd_currunt.send)    --向命令回调函数返回数据
        end
        esp_cmd_list_run_next()                                     --不需要重发，直接发下一条
    end
end

--[[***************************************************************************
** Function name:  esp_cmd_list_run
** Descriptions :  发送命令
***************************************************************************--]]
function esp_cmd_list_run()
    --当前没有命令时退出
    if esp_cmd_currunt==nil
    then 
        stop_timer(esp_at_timer_id)
        return 
    end
    
    --如果命令不为空，则发送命令
    if string.len(esp_cmd_currunt.send)>0
    then
        esp_log('OUT: '..esp_cmd_currunt.send)
        
        esp_send_callback(esp_cmd_currunt.send)
        esp_send_callback('\r\n')
    end
    
    --启动超时计时器
    stop_timer(esp_at_timer_id)
    start_timer(esp_at_timer_id,esp_cmd_currunt.timeout,0,1) --单次

end

--[[***************************************************************************
** Function name:  esp_cmd_list_run
** Descriptions :  发送队列中的下一条命令
***************************************************************************--]]
function esp_cmd_list_run_next()
    esp_log('esp_cmd_list_run_next()')
    esp_cmd_currunt = EspList.popFront(esp_cmd_list)
    esp_cmd_list_run()
end

--[[***************************************************************************
** Function name:  esp_cmd_list_check_ack
** Descriptions :  4G返回数据处理
** @resp        ： 4G返回的数据
***************************************************************************--]]
function esp_cmd_list_check_ack(resp)  
    if esp_cmd_currunt==nil
    then
        esp_resp_callback(nil,resp)                                     --所有数据回调函数
        esp_cmd_list_run_next()                                         --没有命令需要应答，那就发下一条
        return
    else
        esp_resp_callback(esp_cmd_currunt.send,resp)                        --所有数据回调函数
    end
    
    --esp_log('user_log resp -> '..resp)
    --error [string "--[[*****************************Copyright(c)..."]:266: attempt to call a nil value (upvalue 'esp_resp_callback')
    
    --如果又设置对于命令的回调函数，则进入
    if esp_cmd_currunt.callback ~= nil
    then
        esp_log('in cmd cb')
        esp_cmd_currunt.callback(esp_cmd_currunt.send,resp)
        if string.find(resp,'+COPS')~=nil                           
        then                                                        -- COPS 响应，获取SIM卡运营商
            esp_cmd_currunt.callback('sim_oper',resp)                   
        elseif  string.find(esp_cmd_currunt.send,'+HTTPACTION')~=nil
            and string.find(resp,'+HTTPACTION')~=nil                 
        then                                                        -- HTTPACTION 响应
            local nums = {}      
            for v in string.gmatch(resp,"%d+") 
            do nums[#nums+1] = v end      
            esp_cmd_currunt.callback('status',nums[2])                  -- 返回 HTTPACTION 的响应值
        end
    end
  
    --收到应答后立即执行下一条
    if string.find(resp,esp_cmd_currunt.ack)~=nil then
        esp_log('find ack')
        esp_cmd_list_run_next()
    end
end

--[[***************************************************************************
** Function name:  cmd_list_clear
** Descriptions :  清除命令队列
***************************************************************************--]]
function cmd_list_clear()
    esp_cmd_list = nil
    esp_cmd_list = {}
    esp_cmd_list = EspList.new()
    
end

--[[***************************************************************************
** Function name:  on_air_timer
** Descriptions :  4G模块定时处理接口，在 maiin.lua 的 on_timer 中处理
***************************************************************************--]]
function on_espat_timer(timer_id)
    if timer_id==esp_at_timer_id then
		http_resp_data = nil
        esp_cmd_list_timeout()                      -- 超时计时器
    end
end

--[[***************************************************************************
** Function name:  table2str
** Descriptions :  把表转换成字符串
***************************************************************************--]]
function table2str(tbl)    
    if tbl==nil 
    then return '' end
    
    local str = ''
    local count = #(tbl)
    for i=0,count do    
      ch = string.char(tbl[i])          
      str = str .. ch
    end
    return str
end

--[[***************************************************************************
** Function name:  esp32_int_mode
** Descriptions :  设置模块的 mode，客户端
                    <_mode>:
                    0: Null mode, WiFi RF will be disabled
                    1: Station mode（设备模式）
                    2: SoftAP mode（网卡模式）
                    3: SoftAP+Station mode
** Callback     :  模块返回的数据显示在回调函数 on_esp32_init_cb() 
***************************************************************************--]]
function esp32_int_mode(_mode)
    
    --初始化WIFI模块
    esp_cmd_add('AT','OK',1000,0x7FFFFFFF,on_atcb)
    esp_cmd_add('ATE0','OK',1000,3)
    esp_cmd_add('AT+CWMODE='.._mode,'OK',1000,0x7FFFFFFF,on_esp32_init_cb)
    
end

--[[***************************************************************************
** Function name:  esp32_search_wifi
** Descriptions :  列出可用ap
** Callback     :  模块返回的数据显示在回调函数 on_search_wifi_cb() 
***************************************************************************--]]
function esp32_search_wifi()
    
    esp_cmd_add('AT+CWLAP','OK',5000,3,on_search_wifi_cb)
    
end

--[[***************************************************************************
** Function name:  esp32_search_wifi
** Descriptions :  列出可用ap
** Callback     :  模块返回的数据显示在回调函数 on_search_wifi_cb() 
***************************************************************************--]]
function esp32_connect_ap(ssid,pwd)
    --AT+CWJAP="lkjug","12345678"
    esp_cmd_add('AT+CWJAP="'..ssid..'","'..pwd..'"','OK',20000,3,on_connect_ap_cb)
    
end

--[[***************************************************************************
** Function name:  esp32_sta_ip
** Descriptions :  查询 ESP Station 的 IP 地址
** Callback     :  模块返回的数据显示在回调函数 on_sta_ip_cb() 
***************************************************************************--]]
function esp32_sta_ip()
    esp_cmd_add('AT+CIPSTA?','OK',5000,3,on_sta_ip_cb)
    
end

--[[***************************************************************************
** Function name:  esp32_set_baudrate
** Descriptions :  设置4G模块波特率
** @baudrate    :  波特率   
***************************************************************************--]]
function esp32_set_baudrate(baudrate)
    esp_cmd_add('','OK',1000,3)
end

--[[***************************************************************************
** Function name:  esp32_single_tcp_con
** Descriptions :  TCP单链接
** @baudrate    :  波特率   
***************************************************************************--]]
function esp32_single_tcp_con(itype,IP,port)
    esp_cmd_add('AT+CIPSTART="'..itype..'","'..IP..'",'..port,'OK',20000,3)
end

--[[***************************************************************************
** Function name:  esp32_mp_tcp_connection
** Descriptions :  TCP多链接
** @baudrate    :  波特率   
***************************************************************************--]]
function esp32_mp_tcp_connection(itype,IP,port)
    esp_cmd_add('AT+CIPSTART="'..itype..'","'..IP..'",'..port,'OK',20000,3)
end

--[[***************************************************************************
** Function name:  esp32_tcp_sendmode
** Descriptions :  设置TCP传输模式
** @_mode       :  模式，0：普通模式，1：透传   
***************************************************************************--]]
function esp32_tcp_mode( _mode )
    esp_cmd_add('AT+CIPMODE='.._mode,'OK',5000,3)
end

--[[***************************************************************************
** Function name:  esp32_tcp_sendmode
** Descriptions :  设置TCP传输模式
** @...         :  可变参数。1、透传模式时，不写参数，如esp32_tcp_sendmode()
                    2、单链接模式时，使用一个参数，参数是数据长度,如esp32_tcp_sendmode(1)
                    3、多链接模式时，使用两个参数，第一个参数是链路ID，第二个参数是数据长度,如esp32_tcp_sendmode(1,12)
***************************************************************************--]]
function esp32_tcp_sendmode( ... )
    local num = select('#',...)
    if num == 0
    then
        esp_cmd_add('AT+CIPSEND','>',5000,3,on_tcp_send_cb)
    elseif num == 1
    then
        local variate1 = select(1,...)
        esp_cmd_add('AT+CIPSEND='..variate1,'>',5000,3,on_tcp_send_cb)
    elseif num == 2
    then
        local variate1 = select(1,...)
        local variate2 = select(2,...)
        esp_cmd_add('AT+CIPSEND='..variate1..','..variate2,'>',5000,3,on_tcp_send_cb)
    end

end

--[[***************************************************************************
** Function name:  esp32_tcp_sendmode
** Descriptions :  设置TCP传输模式
** @_mode       :  模式，0：普通模式，1：透传   
***************************************************************************--]]
function esp32_tcp_send( data )
    data_type = type(data)
    if data_type == 'string'
    then
        esp32_send_string(data)
    elseif data_type == 'table'
    then
        esp32_send_table(data)
    end
end

--[[***************************************************************************
** Function name:  esp32_tptsm_end
** Descriptions :  结束TCP透明传输   
***************************************************************************--]]
function esp32_tptsm_end()
    esp_cmd_add('+++','',2000,2)
    esp_cmd_add('AT','OK',1000,0x7FFFFFFF)
    esp_cmd_add('AT+CIPCLOSE','OK',1000,3)
end

--[[***************************************************************************
** Function name  :  at_response
** Descriptions   :  4G应答处理
***************************************************************************--]]
function at_response() 
    if string.len(esp_resp_string) >= 1 
    then
        esp_log('IN: '..esp_resp_string)
        --应答处理
        esp_cmd_list_check_ack(esp_resp_string)
    end

    esp_resp_string_last = esp_resp_string
    esp_resp_string = ''
end

--[[***************************************************************************
** Function name  :  on_esp_recv_data
** Descriptions   :  对4G模块串口数据回调
***************************************************************************--]]
function on_esp_recv_data(packet)
    local count = #(packet)

    for i=0,count do    
        --正在接收数据部分
        if http_resp_data_idx<http_resp_data_count 
        then                                                        --当时模块返回的数据时存入 http_resp_data 中
            http_resp_data[http_resp_data_idx] = packet[i]
            http_resp_data_idx = http_resp_data_idx+1
        else
            ch = string.char(packet[i])     
            esp_log('ch='..ch)
            --AT指令应答      
            if ch ~= '\n' 
            then
                esp_resp_string = esp_resp_string .. ch
                esp_log('esp_resp_string='..esp_resp_string)
                if esp_resp_string == '>'
                then
                    at_response()
                end
            end      
            if ch=='\n' 
            then
                
                at_response()
            end
        end
    end
end
----------------------------- air724at.lua END ---------------------------------
