diff --git a/main.py b/main.py index aa3026f..a812796 100644 --- a/main.py +++ b/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)