update hearbeat
This commit is contained in:
169
main.py
169
main.py
@@ -22,6 +22,8 @@ import hashlib
|
||||
import requests
|
||||
import socket
|
||||
|
||||
# import config
|
||||
|
||||
# ==================== 全局配置 ====================
|
||||
|
||||
# OTA 升级地址与本地路径
|
||||
@@ -35,7 +37,7 @@ PASSWORD = None
|
||||
# 服务器连接参数
|
||||
SERVER_IP = "www.shelingxingqiu.com"
|
||||
SERVER_PORT = 50005
|
||||
HEARTBEAT_INTERVAL = 2 # 心跳间隔(秒)
|
||||
HEARTBEAT_INTERVAL = 5 # 心跳间隔(秒)
|
||||
|
||||
# 激光校准配置
|
||||
CONFIG_FILE = "/root/laser_config.json"
|
||||
@@ -60,7 +62,7 @@ pinmap.set_pin_function("P21", "I2C1_SDA")
|
||||
# pinmap.set_pin_function("A15", "I2C5_SCL")
|
||||
# pinmap.set_pin_function("A27", "I2C5_SDA")#ota升级要修改的
|
||||
# ADC 触发阈值(用于检测扳机/激光触发)
|
||||
ADC_TRIGGER_THRESHOLD = 2450
|
||||
ADC_TRIGGER_THRESHOLD = 3000
|
||||
ADC_LASER_THRESHOLD = 3000
|
||||
|
||||
# 显示参数:激光十字线样式
|
||||
@@ -97,8 +99,10 @@ REAL_RADIUS_CM = 15 # 靶心实际半径(厘米)
|
||||
|
||||
# TCP 连接状态
|
||||
tcp_connected = False
|
||||
send_queue = []
|
||||
queue_lock = False
|
||||
high_send_queue = [] # 高优先级发送队列:射箭事件等
|
||||
normal_send_queue = [] # 普通发送队列:电量/校准结果/状态等
|
||||
queue_lock = False # 简易互斥锁,保护队列
|
||||
uart4g_lock = False # 简易互斥锁,保护 4G 串口 AT 发送流程(防并发)
|
||||
update_thread_started = False # 防止 OTA 更新线程重复启动
|
||||
|
||||
|
||||
@@ -250,16 +254,32 @@ def read_device_id():
|
||||
return "DEFAULT_DEVICE_ID"
|
||||
|
||||
|
||||
def safe_enqueue(data_dict, msg_type=2):
|
||||
"""线程安全地将消息加入 TCP 发送队列"""
|
||||
global queue_lock, send_queue
|
||||
def safe_enqueue(data_dict, msg_type=2, high=False):
|
||||
"""线程安全地将消息加入 TCP 发送队列(支持优先级)"""
|
||||
global queue_lock, high_send_queue, normal_send_queue
|
||||
while queue_lock:
|
||||
time.sleep_ms(1)
|
||||
queue_lock = True
|
||||
send_queue.append((msg_type, data_dict))
|
||||
item = (msg_type, data_dict)
|
||||
if high:
|
||||
high_send_queue.append(item)
|
||||
else:
|
||||
normal_send_queue.append(item)
|
||||
queue_lock = False
|
||||
|
||||
|
||||
def _uart4g_lock_acquire():
|
||||
global uart4g_lock
|
||||
while uart4g_lock:
|
||||
time.sleep_ms(1)
|
||||
uart4g_lock = True
|
||||
|
||||
|
||||
def _uart4g_lock_release():
|
||||
global uart4g_lock
|
||||
uart4g_lock = False
|
||||
|
||||
|
||||
def at(cmd, wait="OK", timeout=2000):
|
||||
"""向 4G 模块发送 AT 指令并等待响应"""
|
||||
if cmd:
|
||||
@@ -301,27 +321,30 @@ def tcp_send_raw(data: bytes, max_retries=2) -> bool:
|
||||
global tcp_connected
|
||||
if not tcp_connected:
|
||||
return False
|
||||
|
||||
for attempt in range(max_retries):
|
||||
cmd = f'AT+MIPSEND=0,{len(data)}'
|
||||
if ">" not in at(cmd, ">", 1500):
|
||||
time.sleep_ms(100)
|
||||
continue
|
||||
|
||||
time.sleep_ms(10)
|
||||
full = data + b"\x1A" # AT 指令结束符
|
||||
try:
|
||||
sent = uart4g.write(full)
|
||||
if sent != len(full):
|
||||
_uart4g_lock_acquire()
|
||||
try:
|
||||
for attempt in range(max_retries):
|
||||
cmd = f'AT+MIPSEND=0,{len(data)}'
|
||||
if ">" not in at(cmd, ">", 1500):
|
||||
time.sleep_ms(100)
|
||||
continue
|
||||
except:
|
||||
continue
|
||||
|
||||
if "OK" in at("", "OK", 1000):
|
||||
return True
|
||||
time.sleep_ms(100)
|
||||
time.sleep_ms(10)
|
||||
full = data + b"\x1A" # AT 指令结束符
|
||||
try:
|
||||
sent = uart4g.write(full)
|
||||
if sent != len(full):
|
||||
continue
|
||||
except:
|
||||
continue
|
||||
|
||||
return False
|
||||
if "OK" in at("", "OK", 1000):
|
||||
return True
|
||||
time.sleep_ms(100)
|
||||
|
||||
return False
|
||||
finally:
|
||||
_uart4g_lock_release()
|
||||
|
||||
|
||||
def generate_token(device_id):
|
||||
@@ -565,18 +588,6 @@ def compute_laser_position(circle_center, laser_point, radius, method):
|
||||
dy = ly - cy
|
||||
return dx / (circle_r / 100.0), -dy / (circle_r / 100.0)
|
||||
|
||||
def compute_laser_position_v2(circle_center, laser_point):
|
||||
print(f"circle_center : {circle_center}")
|
||||
cx, cy = circle_center
|
||||
lx, ly = 320,230
|
||||
# lx, ly = laser_point
|
||||
dx = lx - cx
|
||||
dy = ly - cy
|
||||
r = 22.16 * 5
|
||||
target_x = dx/r*100
|
||||
target_y = dy/r*100
|
||||
print(f"lx:{lx} ly: {ly} cx: {cx} cy: {cy} dx: {dx} dy: {dy} result_x: {target_x} result_y: {-target_y}")
|
||||
return (target_x, -target_y)
|
||||
|
||||
# ==================== TCP 通信线程 ====================
|
||||
|
||||
@@ -586,8 +597,12 @@ def connect_server():
|
||||
if tcp_connected:
|
||||
return True
|
||||
print("连接到服务器...")
|
||||
at("AT+MIPCLOSE=0", "OK", 1000)
|
||||
res = at(f'AT+MIPOPEN=0,"TCP","{SERVER_IP}",{SERVER_PORT}', "+MIPOPEN", 8000)
|
||||
_uart4g_lock_acquire()
|
||||
try:
|
||||
at("AT+MIPCLOSE=0", "OK", 1000)
|
||||
res = at(f'AT+MIPOPEN=0,"TCP","{SERVER_IP}",{SERVER_PORT}', "+MIPOPEN", 8000)
|
||||
finally:
|
||||
_uart4g_lock_release()
|
||||
if "+MIPOPEN: 0,0" in res:
|
||||
tcp_connected = True
|
||||
return True
|
||||
@@ -596,7 +611,8 @@ def connect_server():
|
||||
|
||||
def tcp_main():
|
||||
"""TCP 主通信循环:登录、心跳、处理指令、发送数据"""
|
||||
global tcp_connected, send_queue, queue_lock, laser_calibration_active, laser_calibration_result, laser_calibration_lock,update_thread_started
|
||||
global tcp_connected, high_send_queue, normal_send_queue, queue_lock, laser_calibration_active, laser_calibration_result, laser_calibration_lock, update_thread_started
|
||||
send_hartbeat_fail_count = 0
|
||||
while not app.need_exit():
|
||||
if not connect_server():
|
||||
time.sleep_ms(5000)
|
||||
@@ -641,7 +657,7 @@ def tcp_main():
|
||||
# 处理心跳 ACK
|
||||
elif logged_in and msg_type == 4:
|
||||
last_heartbeat_ack_time = time.ticks_ms()
|
||||
# print("✅ 收到心跳确认")
|
||||
print("✅ 收到心跳确认")
|
||||
|
||||
# 处理业务指令
|
||||
elif logged_in and isinstance(body, dict):
|
||||
@@ -709,17 +725,22 @@ def tcp_main():
|
||||
break
|
||||
|
||||
# 发送队列中的业务数据
|
||||
if logged_in and not queue_lock and send_queue:
|
||||
if logged_in and (high_send_queue or normal_send_queue) and (not queue_lock):
|
||||
# 只在锁内取出一个待发包,发送放到锁外,避免长时间占用队列锁
|
||||
while queue_lock:
|
||||
time.sleep_ms(1)
|
||||
queue_lock = True
|
||||
if send_queue:
|
||||
msg_type, data_dict = send_queue.pop(0)
|
||||
pkt = make_packet(msg_type, data_dict)
|
||||
if not tcp_send_raw(pkt):
|
||||
tcp_connected = False
|
||||
queue_lock = False
|
||||
break
|
||||
if high_send_queue:
|
||||
msg_type, data_dict = high_send_queue.pop(0)
|
||||
else:
|
||||
msg_type, data_dict = normal_send_queue.pop(0)
|
||||
queue_lock = False
|
||||
|
||||
pkt = make_packet(msg_type, data_dict)
|
||||
if not tcp_send_raw(pkt):
|
||||
tcp_connected = False
|
||||
break
|
||||
|
||||
# 发送激光校准结果
|
||||
if logged_in and not laser_calibration_lock and laser_calibration_result is not None:
|
||||
laser_calibration_lock = True
|
||||
@@ -733,13 +754,21 @@ def tcp_main():
|
||||
if logged_in and current_time - last_heartbeat_send_time > HEARTBEAT_INTERVAL * 1000:
|
||||
if not tcp_send_raw(make_packet(4, {"t": int(time.time())})):
|
||||
print("💔 心跳发送失败")
|
||||
break
|
||||
last_heartbeat_send_time = current_time
|
||||
# print("💓 心跳已发送")
|
||||
send_hartbeat_fail_count += 1
|
||||
if send_hartbeat_fail_count >= 3:
|
||||
send_hartbeat_fail_count = 0
|
||||
print("连续3次发送心跳失败,重连")
|
||||
break
|
||||
else:
|
||||
continue
|
||||
else:
|
||||
send_hartbeat_fail_count = 0
|
||||
last_heartbeat_send_time = current_time
|
||||
print("💓 心跳已发送")
|
||||
|
||||
# 心跳超时重连
|
||||
if logged_in and current_time - last_heartbeat_ack_time > 6000:
|
||||
print("⏰ 6秒无心跳ACK,重连")
|
||||
if logged_in and current_time - last_heartbeat_ack_time > 1000*60*10: # 十分钟
|
||||
print("⏰ 十分钟无心跳ACK,重连")
|
||||
break
|
||||
|
||||
time.sleep_ms(50)
|
||||
@@ -773,6 +802,7 @@ def laser_calibration_worker():
|
||||
|
||||
def cmd_str():
|
||||
global DEVICE_ID, PASSWORD
|
||||
# print("env: ", config.get_env())
|
||||
DEVICE_ID = read_device_id()
|
||||
PASSWORD = DEVICE_ID + "."
|
||||
|
||||
@@ -802,10 +832,7 @@ def cmd_str():
|
||||
while not app.need_exit():
|
||||
current_time = time.ticks_ms()
|
||||
# print("压力传感器数值: ", adc_obj.read())
|
||||
adc_val = adc_obj.read()
|
||||
if adc_val > 2450:
|
||||
print(f"adc_val: {adc_val}")
|
||||
if adc_val > ADC_TRIGGER_THRESHOLD:
|
||||
if adc_obj.read() > ADC_TRIGGER_THRESHOLD:
|
||||
diff_ms = current_time-last_adc_trigger
|
||||
if diff_ms<3000:
|
||||
continue
|
||||
@@ -824,22 +851,7 @@ def cmd_str():
|
||||
disp.show(result_img)
|
||||
|
||||
# 计算偏移与距离
|
||||
# dx, dy = compute_laser_position(center, (x, y), radius, method)
|
||||
if center is None:
|
||||
print(f"程序异常或者离谱脱靶")
|
||||
# 构造上报数据
|
||||
inner_data = {
|
||||
"x": 200.0,
|
||||
"y": 200.0,
|
||||
"r": 90.0,
|
||||
"d": 0.0, # 距离(厘米)
|
||||
"m": 'error',
|
||||
'adc': adc_val,
|
||||
}
|
||||
report_data = {"cmd": 1, "data": inner_data}
|
||||
tcp_send_raw(make_packet(2, report_data))
|
||||
continue
|
||||
dx, dy = compute_laser_position_v2(center, (x, y))
|
||||
dx, dy = compute_laser_position(center, (x, y), radius, method)
|
||||
distance_m = estimate_distance(best_radius1)
|
||||
|
||||
# 读取电量
|
||||
@@ -860,12 +872,11 @@ def cmd_str():
|
||||
"y": float(dy) if dy is not None else 200.0,
|
||||
"r": 90.0,
|
||||
"d": round((distance_m or 0.0) * 100), # 距离(厘米)
|
||||
"m": method,
|
||||
'adc': adc_val,
|
||||
"m": method
|
||||
}
|
||||
report_data = {"cmd": 1, "data": inner_data}
|
||||
# safe_enqueue(report_data)
|
||||
tcp_send_raw(make_packet(2, report_data))
|
||||
# 射箭事件高优先级入队,由 tcp_main 统一发送
|
||||
safe_enqueue(report_data, msg_type=2, high=True)
|
||||
print("📤 射箭事件已加入发送队列")
|
||||
|
||||
time.sleep_ms(100)
|
||||
|
||||
Reference in New Issue
Block a user