Skip to content

udp — UDP 通信 ​

模块名:udp

无连接的数据收发。适合设备发现、局域网广播、与传感器做快速轻量交换。UDP 不保证送达也不保证顺序,需要可靠传输请用 tcp_client。

对象方法用冒号写法(u:send(...)),库表里也注册了同名函数,udp.send(u, ...) 等价。

函数总览 ​

库级函数 ​

函数作用
udp.creat(port, ip)创建 UDP 对象,绑定本地端口和网卡

对象方法 ​

方法作用
u:reg注册收包回调
u:start启动收包线程
u:send向指定地址发送数据

常量 ​

常量回调入参说明
udp.RECVDATA(ip, port, data)收到数据包。ip 已转成点分字符串

这是本模块唯一可注册的事件类型。


udp.creat ​

lua
udp.creat(port, ip) --> udp

创建 UDP 对象并绑定本地端口与网卡。

参数

#参数类型必填默认值说明
1portinteger是—本机监听端口。传 0 由系统分配(只发不收时可用)
2ipstring是—本机网卡 IP。传 "0.0.0.0" 表示所有网卡

返回值

类型说明
userdataUDP 对象。必须用变量长期持有,被 GC 回收会关闭 socket

示例

lua
udps = udp.creat(6000, "0.0.0.0")

说明

  • 两个参数都是必填的,省略任何一个都会报错。只发不收也要写全,例如 udp.creat(0, "0.0.0.0")。

实用例程 ​

lua
        0, 0, 0, 0, 0, 0, 0, 0,  -- Reference Timestamp
        0, 0, 0, 0, 0, 0, 0, 0,  -- Origin Timestamp
        0, 0, 0, 0, 0, 0, 0, 0,  -- Receive Timestamp
        0, 0, 0, 0, 0, 0, 0, 0   -- Transmit Timestamp
    )
    udp.send(sock, self.server, self.port, packet)
end

function isYearGreaterOrEqualTo2024()
    local currentYear = tonumber(os.date("%Y", os.time())) -- 提取当前年份
    return currentYear >= 2024
end

-- NTP校时标志位,防止重复校时
isNtpSyncing = false

-- 记录上次NTP校时的小时,防止重复校时
lastNtpSyncHour = -1

-- Create UDP socket and register callback
sock = udp.creat(2000, "192.168.1.100")  -- Create UDP object on port 2000, bind to all interfaces for WiFi

-- Register callback for received data
udp.reg(sock, udp.RECVDATA, function(ip, port, buffer) 
    -- Extract timestamp from response (last 8 bytes)
    local seconds = network_bytes_to_int(buffer, 41)
    local fraction = network_bytes_to_int(buffer, 45)
    
    -- Convert NTP time to Unix time
    local unix_time = seconds - NTP_TIMESTAMP_DELTA
    
    -- Calculate milliseconds part
    local milliseconds = math.floor(fraction / 4294967295.0 * 1000.0 + 0.5)
    local ntp_timestamp = unix_time * 1000.0 + milliseconds
    local ts = math.floor((ntp_timestamp) / 1000.0)
    
    -- 转换为北京时间并设置RTC
    local beijing_timestamp = ts + 8 * 3600
    plc.rtc_set(beijing_timestamp)
    print("NTP time <设备ID或凭据> <设备ID或凭据>: " .. os.date("%Y-%m-%d %H:%M:%S", beijing_timestamp))

把上面的地址、主题、引脚替换成你项目里的即可。

u:reg ​

lua
u:reg(event, callback)

注册收包回调。要在 start 之前注册好。

参数

#参数类型必填默认值说明
1eventinteger是—只支持 udp.RECVDATA
2callbackfunction是—回调函数。类型不是 function 会直接报错

返回值

无返回值。

示例

lua
udps:reg(udp.RECVDATA, function(ip, port, data)
    print(string.format("来自 %s:%d 的 %d 字节: %s", ip, port, #data, data))
end)

说明

  • 回调参数顺序是 (ip, port, data),数据在最后。

u:start ​

lua
u:start()

启动收包线程。本函数立即返回。

参数

无参数。

返回值

无返回值。


u:send ​

lua
u:send(ip, port, data) --> integer

向指定地址发送一个 UDP 包。

参数

#参数类型必填默认值说明
1ipstring是—目标 IP。广播地址如 "192.168.1.255"
2portinteger是—目标端口
3datastring是—数据,可为二进制

返回值

类型说明
integer实际发送的字节数。data 为空串时直接返回 0

示例

lua
udps:send("192.168.1.255", 6000, "DISCOVER")

u:close / u:is_linked / u:set_timeout / u:set_transparent —— udp 不提供这四个方法 ​

close / is_linked / set_timeout / set_transparent 是 tcp_client 对象上的方法(tcp_client、tcp_server、udp 三者名字相近但实现各自独立)。udp 模块没有注册这四个名字,对 udp 对象调用会直接报错:

lua
udps = udp.creat(6000, "0.0.0.0")
udps:start()
udps:close()          --> 报错:attempt to call method 'close' (a nil value)

参数

  • close —— 不存在。UDP 无连接,没有"断开某个对端"可言;要停止收包,把持有 udp 对象的变量置 nil 并等 GC,对象回收时 socket 随之关闭
  • is_linked —— 不存在。UDP 是无连接协议,根本没有"已连接/未连接"状态可查;对端是否活着只能用"发出去、等回包、自己判超时"实现
  • set_timeout —— 不存在。udp 的收包线程由 start 内部驱动,回调式收数据,模块没有对外暴露任何超时参数
  • set_transparent —— 不存在。串口透传是 tcp_client 专属能力,udp 侧没有对应实现

返回值

以 u:xxx() 形式调用这四个名字一律报错,没有返回值。

示例

lua
-- 想知道对端在不在:发一个查询包,自己计时等回包
udps = udp.creat(6000, "0.0.0.0")
got_reply = false
udps:reg(udp.RECVDATA, function(ip, port, data) got_reply = true end)
udps:start()

udps:send("192.168.1.100", 6000, "PING")
timer.start(3000, function()
    print(got_reply and "对端在线" or "3 秒没回包")   -- 单次定时器
end, true)

常见用法 ​

局域网设备发现:收到 DISCOVER 就回应自己的身份

lua
udps = udp.creat(6000, "0.0.0.0")

udps:reg(udp.RECVDATA, function(ip, port, data)
    if data == "DISCOVER" then
        udps:send(ip, port, "LPPLC " .. plc.plc_name() .. " " .. plc.eth_ip())
    end
end)

udps:start()

周期广播状态

lua
timer.start(5000, function()
    udps:send("192.168.1.255", 6000,
        json.encode({ name = plc.plc_name(), temp = plc.df(100) }))
end)

注意事项 ​

  • udp.creat 的两个参数都必填,没有默认值。
  • 回调参数顺序是 (ip, port, data)。
  • UDP 单包不宜过大,局域网建议控制在 1400 字节以内,避免 IP 分片。
  • 不保证送达、不保证顺序、不去重。需要可靠性得自己加序号和重传,或直接换 TCP。
  • 和 TCP 服务器一样,本模块没有任何认证。如果回调里会写 PLC 寄存器,请自行校验来源。

例程 ​

每个例程都是可直接下载运行的完整脚本:creat 建对象、reg 注册收包回调、start 启动收包线程,脚本注册完即结束。 API 只用本页已文档化的函数。统一模板「场景 / 完整代码 / 要点 / 验证方法 / 注意事项」。

例程 1:局域网设备发现(收到 DISCOVER 回身份) ​

适用:LP3201 / LP3202 / LP3203 / LP3222 / LP3232 / LP3288 / 软PLC模拟器 | 依赖模块:udp plc | 难度:★☆☆

场景:上位机在网内广播 DISCOVER,设备收到后回应自己的设备名和 IP,方便自动发现。

完整代码(可直接下载运行):

lua
udps = udp.creat(6000, "0.0.0.0")    -- 两参必填:本地端口 + 绑定网卡
udps:reg(udp.RECVDATA, function(ip, port, data)
    if data == "DISCOVER" then
        udps:send(ip, port, "LPPLC " .. plc.plc_name() .. " " .. plc.eth_ip())
    end
end)
udps:start()

要点:

  • udp.creat(port, ip) 两个参数都必填,省略任何一个报错;"0.0.0.0" 表示所有网卡。
  • RECVDATA 回调参数顺序是 (ip, port, data)——数据在最后。

验证方法:

  1. 同网段用工具向 192.168.1.255:6000 广播 DISCOVER。
  2. 设备回 LPPLC <设备名> <IP>。

注意事项:

  • 只发不收也要写全两参,例如 udp.creat(0, "0.0.0.0")。
  • udp 没有 close / is_linked / set_timeout / set_transparent,调用会报错。

其余例程(各自一页) ​

LPPLC 工业物联网云平台 · 软硬件与云平台全栈自研